-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.html
156 lines (156 loc) · 12.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<!DOCTYPE html>
<html lang="en">
<head>
<title>Peekaboo</title>
<meta charset="utf-8">
<meta name="description" content="Peekaboo - Know when elements are about to scroll into the viewport">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,700' rel='stylesheet' type='text/css'>
<link href='demo.css' rel='stylesheet' type='text/css'>
</head>
<body>
<article>
<h1>Peekaboo</h1>
<p>Fires a one-time event whenever elements of your choice are about to scroll into the viewport. Perfect for triggering animations or for just-in-time asset loading, for example; images, javascript libraries such as twitter and facebook, article comments or 3<sup>rd</sup> party advertisments.</p>
<p>The more curious amongst you may wish to jump straight to one of the demos:</p>
<ol>
<li>A demo that <a href="./demo/pantone/index.html">animates 500 list items</a> as they scroll into view</li>
<li>A demo that covers <a href="./demo/srcset-lazyload/index.html">lazyloading responsive images</a></li>
</ol>
<p>At a glance…
<ul>
<li>Uses the very fast <code>IntersectionObserver</code> API where possible</li>
<li>Fallsback to using a single <code>scroll</code> and <code>resize</code> event in other browsers but hooks into <code>requestAnimationFrame</code> to stop scroll jank</li>
<li>Correctly calculates if an observed element is hidden in the overflow of a parent element</li>
<li>A little over 1k when minified & gzipped</li>
</ul>
<h2>The Peekaboo API</h2>
<p>The API was purposely kept small and focused – It contains only two methods, both of which are described below:</p>
<h3><Object> window.peekaboo</h3>
<p>An Object <code>window.peekaboo</code> is made available. It makes public the following API methods.</p>
<h3 id="peekaboo_observe">peekaboo.observe(<Object>)</h3>
<p>The <code>peekaboo.observe</code> method is used to initiate the observation of DOM elements; for example:</p>
<figure>
<pre><code>peekaboo.observe({
"pattern": "img[data-srcset]",
"callback": function(elemArray) {
elemArray.forEach(function(elem) {
elem.setAttribute("srcset", elem.getAttribute("data-srcset"));
elem.removeAttribute("data-srcset");
});
}
});</code></pre>
<figcaption><span>A barebones example showing how you might <a href="./demo/srcset-lazyload/index.html">lazyload responsive images</a></span></figcaption>
</figure>
<p>The method accepts a configuration Object containing the following attributes:</p>
<table cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<th>Attribute</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr id="pattern">
<td><code>pattern</code></td>
<td>Yes</td>
<td><p>A <code>String</code> value representing a CSS Selector<sup><a id="fn-1" href="#footnote-1">1</a></sup>, used when selecting the set of DOM elements to observe.</p></td>
</tr>
<tr id="callback">
<td><code>callback</code></td>
<td>Yes</td>
<td>
<p>A <code>function</code> invoked by the script whenever elements that match the CSS Selector <a href="#pattern"><code>pattern</code></a> have scrolled into the viewport. The function is passed an <code>Array</code> of DOM elements.</p>
<p>Elements are observed on a <em>one-time-only</em> basis meaning if the element is scrolled out of the observable area and then back in again, the <code>callback</code> function will not fire a second time.</p>
</td>
</tr>
<tr id="fastVisibilityCheck">
<td><code>fastVisibilityCheck</code></td>
<td>No</td>
<td><p>An optional <code>Boolean</code> value that indicates if the faster algorithm for determining an elements visibility should be used<sup><a id="fn-2" href="#footnote-2">2</a></sup>. Defaults to FALSE.</p>
<p>If you know that none of the observable elements targetted by <a href="#pattern"><code>pattern</code></a> will be positioned within an <code>overflow:scroll</code> container then set <code>fastVisibilityCheck</code> to TRUE. This tells the script to use a much faster algorithm
for checking if an element has been scrolled into view (for the curious, we no longer traverse up the DOM tree testing if any parent element is hidden within a scroll container).</p>
</td>
</tr>
<tr id="rootMargin">
<td><code>rootMargin</code></td>
<td>No</td>
<td><p>An optional <code>Integer</code> value representing the pixel margin to use when determining if an element has scrolled into the viewable area. Defaults to 150.</p></td>
</tr>
</tbody>
</table>
<h4>Adding observable elements to the DOM dynamically</h4>
<p>If no configuration object is passed to the <code><a href="#peekaboo_observe">peekaboo.observe</a></code> method, the script cycles through the list of currently assigned patterns and recalculates the observable element set for each. This is a useful way to update the scripts
internal element list if potential observable elements have been added to the DOM dynamically.</p>
<figure><pre><code>// Add an observable element to the DOM
document.body.appendChild(newObservableElement);
// Now tell peekaboo about it
peekaboo.observe();</code></pre>
<figcaption><span>An example showing how to observe dynamically created elements</span></figcaption>
</figure>
<h3>peekaboo.unobserve(<String>)</h3>
<p>Should you no longer need to observe elements, just call the <code>peekaboo.unobserve</code> method, passing in a <code>String</code> value representing a CSS Selector previously used when calling <code><a href="#peekaboo_observe">peekaboo.observe</a></code>.</p>
<figure><pre><code>peekaboo.unobserve("img[data-srcset]");</code></pre>
<figcaption><span>An example showing how to unobserve elements</span></figcaption>
</figure>
<h3 id="peekaboo_supported"><Boolean> peekaboo.supported</h3>
<p>A <code>Boolean</code> variable named <code>peekaboo.supported</code> is made available by the script to enable you to determine if the browser is supported.</p>
<figure><pre><code>if(peekaboo && peekaboo.supported) {
// Yeah, start doing cool things!
peekaboo.observe({
"pattern": "pattern",
"callback": callbackFunction
);
} else {
// Yikes, start the backup plan!
}</code></pre>
<figcaption><span>An example showing how to determine browser support</span></figcaption>
</figure>
<h2>Browser support</h2>
<p>Modern browsers only (IE10+). The script relies on either the <code>IntersectionObserver</code> API or a mix of <code>addEventListener</code>, <code>querySelectorAll</code>, <code>requestAnimationFrame</code> and <code>getBoundingClientRect</code> – which limits it to a more modern browser experience. Hopefully browser uptake for the <code>IntersectionObserver</code> API will be swift and this script will be obsolete in the near future (<a href="https://caniuse.com/#search=IntersectionObserver">caniuse: Intersection Observer</a>).</p>
<h2>Loading the script</h2>
<p>Add the script to the page source code as shown below. It’s a good idea to add it directly before the closing <code></body></code> tag as it requires the DOM to be populated before initialisation.</p>
<figure><pre><code> <script src="/the/path/to/peekaboo.min.js"></script>
</body></code></pre>
<figcaption><span>An example showing where to place the script within the markup</span></figcaption>
</figure>
<h3>“Cuts the mustard” async loading</h3>
<p>If you already use an async file loader (such as <a href="https://github.com/filamentgroup/loadJS">loadJS</a>) then the following cuts the mustard test can be performed before loading the script:</p>
<figure><pre><code>if(("IntersectionObserver" in window)
||
("requestAnimationFrame" in window
&& "querySelectorAll" in document
&& "addEventListener" in document
&& "getBoundingClientRect" in document.createElement('div'))) {
// Yeah, load the script!
}</code></pre>
<figcaption><span>The cuts the mustard test used by the script</span></figcaption>
</figure>
<p>This is basically the same test performed internally by the script when setting the <code><a href="#peekaboo_supported">peekaboo.supported</a></code> variable.</p>
<p>This may seem a bit heavy handed but <code>requestAnimationFrame</code> is often polyfilled and the check for IE9+ needed to be as failsafe as possible.</p>
<h2>Performance matters</h2>
<p>Any script that’s observing DOM elements will undoubtedly demand CPU cycles but the following speed gains have been integrated:</p>
<ul>
<li>Uses the very fast <code>IntersectionObserver</code> API where possible</li>
<li>Attempts to reuse the same <code>IntersectionObserver</code> instance where possible for all calls to <code>peekaboo.observe</code></li>
<li>Batches callbacks i.e. one callback for many elements, not one callback per element</li>
<li>For browsers that don’t support the <code>IntersectionObserver</code>…
<ul>
<li>Fallsback to using a single <code>scroll</code> and <code>resize</code> event in other browsers but hooks into <code>requestAnimationFrame</code> to stop scroll jank</li>
<li>Automatically removes the <code>scroll</code> and <code>resize</code> event handler when no longer needed</li>
<li>Caches element lists to reduce the <code>querySelectorAll</code> overhead to a bare minimum when the <code>scroll</code>/<code>resize</code> fallback is used</li>
<li>Caches window <code>height</code> and <code>width</code> to help avoid layout thrashing</li>
<li>Ships with a <a href="#fastVisibilityCheck"><code>fastVisibilityCheck</code></a> option for speeding things up even more</li>
</ul>
</ul>
<footer>
<ol>
<li id="footnote-1"><p>The CSS Selector has to be able to have the String “<code>:not[data-peekaboo]</code>” concatenated onto it and still be valid for use when calling <code>querySelectorAll</code>. <a href="#fn-1" title="Jump back to footnote 1 in the text.">↩</a></p></li>
<li id="footnote-2"><p>The <a href="#fastVisibilityCheck"><code>fastVisibilityCheck</code></a> option is only taken into account in browsers that do not upport the <code>IntersectionObserver</code> API. <a href="#fn-2" title="Jump back to footnote 2 in the text.">↩</a></p></li>
</ol>
</footer>
</article>
<!-- Badge by http://tholman.com/github-corners/ //-->
<a href="https://github.com/freqdec/peekaboo" class="github-corner"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
</body>
</html>