Skip to content

Commit 7088d4e

Browse files
authored
Initial upload
1 parent 54e8460 commit 7088d4e

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

gocek.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
gocek = ({
2+
3+
/* ---
4+
| MAIN
5+
--- */
6+
7+
init() {
8+
9+
//elements container - collect any starting elements
10+
this.els = Array.from(document.querySelectorAll('*:not(img).lazy, img.lazy[data-src]:not(.viewed)')).map(el => this.register_el(el, 1));
11+
12+
//custom events conatiner
13+
this.evts = {};
14+
15+
//on scroll end (on window), handle any elements that come into view. For imgs, load them...
16+
addEventListener('scroll', () => {
17+
clearTimeout(this.scroll_timeout);
18+
this.scroll_timeout = setTimeout(() => {
19+
this.els.forEach(el => {
20+
21+
let
22+
old_state = el.getAttribute('data-gocek-state'),
23+
new_state = this.is_visible(el, el.matches('.completely')) ? 'visible' : 'hidden';
24+
25+
//...state - if old state same as new state, ignore
26+
if (old_state == new_state) return;
27+
el.setAttribute('data-gocek-state', new_state);
28+
29+
//...if visible and unloaded image, load now
30+
if (new_state == 'visible' && el.matches('img:not(.viewed)')) {
31+
el.src = el.dataset.src;
32+
el.classList.add('viewed');
33+
el.classList.remove('loading');
34+
}
35+
36+
//...any callbacks registered on this element?
37+
let cb = el['gocek_on_'+new_state+'_cb'];
38+
if (cb) cb('gocek_'+new_state, el);
39+
if (el['gocek_on_'+new_state+'_once']) delete cb;
40+
41+
});
42+
}, 250);
43+
});
44+
window.dispatchEvent(new CustomEvent('scroll'));
45+
46+
//return API
47+
return this;
48+
49+
},
50+
51+
/* ---
52+
| ON... - register event to fire when element(s) become(s) scrolls into or out of view. Args:
53+
| @on (str) - which event, either 'visible' or 'hidden'
54+
| @el (str; obj) - a selector or element reference representing the element to listen for
55+
| @func (func) - the callback function
56+
| @once (bool) - if true, runs only once for given event type, not each time
57+
--- */
58+
59+
on(on, el, func, once) {
60+
if (typeof el == 'string') el = document.querySelector(el);
61+
else if (!(el instanceof HTMLElement)) return;
62+
el['gocek_on_'+on+'_cb'] = func;
63+
if (once) el['gocek_on_'+on+'_once'] = 1;
64+
},
65+
66+
/* ---
67+
| IS VISIBLE - return whether an element is currently visible - partially or fully. Used internally but available
68+
| on API too. Args:
69+
| @el (str; obj) - (see ::on())
70+
| @completely (bool) - if true, returns true only if completely visible, not partially
71+
--- */
72+
73+
is_visible(el, completely) {
74+
75+
let
76+
scrollTop = window.pageYOffset,
77+
winHeight = window.innerHeight,
78+
elTop = el.offsetTop,
79+
elBottom = el.offsetTop + el.offsetHeight;
80+
81+
if (!completely)
82+
return elBottom > scrollTop && elTop < scrollTop + winHeight;
83+
else
84+
return elTop > scrollTop && elBottom < scrollTop + winHeight;
85+
86+
},
87+
88+
/* ---
89+
| REGISTER ELEMENT(S) - alternate, programmatic way to register element(s) with Gocek (other way is via HTML attributes). Args:
90+
| @els (obj; arr) - reference to element, or array of elements
91+
--- */
92+
93+
register_el(els, internal) {
94+
els = els instanceof Array ? els : [els];
95+
for (let i=0; i<els.length; i++) {
96+
if (els[i].tagName == 'IMG') els[i].classList.add('loading');
97+
if (internal)
98+
return els[i]; //<-- no problem returning from loop; for internal use, only ever one element passed at a time
99+
else
100+
this.els.push(els[i]);
101+
}
102+
},
103+
104+
}).init();

0 commit comments

Comments
 (0)