intersection-observer 0.4.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +21 -0
- package/intersection-observer-test.js +24 -0
- package/intersection-observer.js +38 -14
- package/package.json +1 -1
package/README.md
CHANGED
@@ -4,6 +4,7 @@ This library polyfills the native [`IntersectionObserver`](http://w3c.github.io/
|
|
4
4
|
|
5
5
|
- [Installation](#installation)
|
6
6
|
- [Configuring the polyfill](#configuring-the-polyfill)
|
7
|
+
- [Limitations](#limitations)
|
7
8
|
- [Browser support](#browser-support)
|
8
9
|
- [Running the tests](#running-the-tests)
|
9
10
|
|
@@ -81,6 +82,26 @@ io.observe(someTargetElement);
|
|
81
82
|
|
82
83
|
**Note:** the `POLL_INTERVAL` property must be set prior to calling the `.observe` method, or the default configuration will be used.
|
83
84
|
|
85
|
+
**Ignoring DOM changes**
|
86
|
+
|
87
|
+
You can also choose to not check for intersections when the DOM changes by setting an observer's `USE_MUTATION_OBSERVER` property to `false` (either globally on the prototype or per-instance)
|
88
|
+
|
89
|
+
```js
|
90
|
+
IntersectionObserver.prototype.USE_MUTATION_OBSERVER = false; // Globally
|
91
|
+
|
92
|
+
// for an instance
|
93
|
+
var io = new IntersectionObserver(callback);
|
94
|
+
io.USE_MUTATION_OBSERVER = false;
|
95
|
+
```
|
96
|
+
|
97
|
+
This is recommended in cases where the DOM will update frequently but you know those updates will have no affect on the position or your target elements.
|
98
|
+
|
99
|
+
## Limitations
|
100
|
+
|
101
|
+
This polyfill does not attempt to report intersections across same-origin `iframe` boundaries. While supporting same-origin iframes is technically possible, it would drastically reduce performance. Since most `iframe` usage on the web is cross-origin, this polyfill chooses to optimize for performantly handling the most common use cases. (Note: neither this polyfill nor native implementations support reporting intersections across cross-origin `iframe` boundaries.)
|
102
|
+
|
103
|
+
This polyfill also does not support the [proposed v2 additions](https://github.com/szager-chromium/IntersectionObserver/blob/v2/explainer.md), as these features are not currently possible to do with JavaScript and existing web APIs.
|
104
|
+
|
84
105
|
## Browser support
|
85
106
|
|
86
107
|
The polyfill has been tested and known to work in the latest version of all browsers.
|
@@ -724,6 +724,30 @@ describe('IntersectionObserver', function() {
|
|
724
724
|
|
725
725
|
io.observe(targetEl1);
|
726
726
|
});
|
727
|
+
|
728
|
+
it('handles roots in shadow DOM', function(done) {
|
729
|
+
var shadowRoot = grandParentEl.attachShadow({mode: 'open'});
|
730
|
+
|
731
|
+
shadowRoot.innerHTML =
|
732
|
+
'<style>' +
|
733
|
+
'#slot-parent {' +
|
734
|
+
' position: relative;' +
|
735
|
+
' width: 400px;' +
|
736
|
+
' height: 200px;' +
|
737
|
+
'}' +
|
738
|
+
'</style>' +
|
739
|
+
'<div id="slot-parent"><slot></slot></div>';
|
740
|
+
|
741
|
+
var slotParent = shadowRoot.getElementById('slot-parent');
|
742
|
+
|
743
|
+
io = new IntersectionObserver(function(records) {
|
744
|
+
expect(records.length).to.be(1);
|
745
|
+
expect(records[0].intersectionRatio).to.be(1);
|
746
|
+
done();
|
747
|
+
}, {root: slotParent});
|
748
|
+
|
749
|
+
io.observe(targetEl1);
|
750
|
+
});
|
727
751
|
}
|
728
752
|
|
729
753
|
|
package/intersection-observer.js
CHANGED
@@ -4,14 +4,17 @@
|
|
4
4
|
* Licensed under the W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE.
|
5
5
|
*
|
6
6
|
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
|
7
|
-
*
|
7
|
+
*
|
8
8
|
*/
|
9
|
-
|
10
|
-
(function(window, document) {
|
9
|
+
(function() {
|
11
10
|
'use strict';
|
12
11
|
|
12
|
+
// Exit early if we're not running in a browser.
|
13
|
+
if (typeof window !== 'object') {
|
14
|
+
return;
|
15
|
+
}
|
13
16
|
|
14
|
-
//
|
17
|
+
// Exit early if all IntersectionObserver and IntersectionObserverEntry
|
15
18
|
// features are natively supported.
|
16
19
|
if ('IntersectionObserver' in window &&
|
17
20
|
'IntersectionObserverEntry' in window &&
|
@@ -31,9 +34,15 @@ if ('IntersectionObserver' in window &&
|
|
31
34
|
}
|
32
35
|
|
33
36
|
|
37
|
+
/**
|
38
|
+
* A local reference to the document.
|
39
|
+
*/
|
40
|
+
var document = window.document;
|
41
|
+
|
42
|
+
|
34
43
|
/**
|
35
44
|
* An IntersectionObserver registry. This registry exists to hold a strong
|
36
|
-
* reference to IntersectionObserver instances currently
|
45
|
+
* reference to IntersectionObserver instances currently observing a target
|
37
46
|
* element. Without this registry, instances without another reference may be
|
38
47
|
* garbage collected.
|
39
48
|
*/
|
@@ -62,7 +71,9 @@ function IntersectionObserverEntry(entry) {
|
|
62
71
|
|
63
72
|
// Sets intersection ratio.
|
64
73
|
if (targetArea) {
|
65
|
-
|
74
|
+
// Round the intersection ratio to avoid floating point math issues:
|
75
|
+
// https://github.com/w3c/IntersectionObserver/issues/324
|
76
|
+
this.intersectionRatio = Number((intersectionArea / targetArea).toFixed(4));
|
66
77
|
} else {
|
67
78
|
// If area is zero and is intersecting, sets to 1, otherwise to 0
|
68
79
|
this.intersectionRatio = this.isIntersecting ? 1 : 0;
|
@@ -124,6 +135,12 @@ IntersectionObserver.prototype.THROTTLE_TIMEOUT = 100;
|
|
124
135
|
*/
|
125
136
|
IntersectionObserver.prototype.POLL_INTERVAL = null;
|
126
137
|
|
138
|
+
/**
|
139
|
+
* Use a mutation observer on the root element
|
140
|
+
* to detect intersection changes.
|
141
|
+
*/
|
142
|
+
IntersectionObserver.prototype.USE_MUTATION_OBSERVER = true;
|
143
|
+
|
127
144
|
|
128
145
|
/**
|
129
146
|
* Starts observing a target element for intersection changes based on
|
@@ -131,10 +148,11 @@ IntersectionObserver.prototype.POLL_INTERVAL = null;
|
|
131
148
|
* @param {Element} target The DOM element to observe.
|
132
149
|
*/
|
133
150
|
IntersectionObserver.prototype.observe = function(target) {
|
134
|
-
|
135
|
-
if (this._observationTargets.some(function(item) {
|
151
|
+
var isTargetAlreadyObserved = this._observationTargets.some(function(item) {
|
136
152
|
return item.element == target;
|
137
|
-
})
|
153
|
+
});
|
154
|
+
|
155
|
+
if (isTargetAlreadyObserved) {
|
138
156
|
return;
|
139
157
|
}
|
140
158
|
|
@@ -243,7 +261,7 @@ IntersectionObserver.prototype._parseRootMargin = function(opt_rootMargin) {
|
|
243
261
|
|
244
262
|
/**
|
245
263
|
* Starts polling for intersection changes if the polling is not already
|
246
|
-
* happening, and if the page's
|
264
|
+
* happening, and if the page's visibility state is visible.
|
247
265
|
* @private
|
248
266
|
*/
|
249
267
|
IntersectionObserver.prototype._monitorIntersections = function() {
|
@@ -260,7 +278,7 @@ IntersectionObserver.prototype._monitorIntersections = function() {
|
|
260
278
|
addEvent(window, 'resize', this._checkForIntersections, true);
|
261
279
|
addEvent(document, 'scroll', this._checkForIntersections, true);
|
262
280
|
|
263
|
-
if ('MutationObserver' in window) {
|
281
|
+
if (this.USE_MUTATION_OBSERVER && 'MutationObserver' in window) {
|
264
282
|
this._domObserver = new MutationObserver(this._checkForIntersections);
|
265
283
|
this._domObserver.observe(document, {
|
266
284
|
attributes: true,
|
@@ -545,7 +563,7 @@ function now() {
|
|
545
563
|
|
546
564
|
|
547
565
|
/**
|
548
|
-
* Throttles a function and delays its
|
566
|
+
* Throttles a function and delays its execution, so it's only called at most
|
549
567
|
* once within a given time period.
|
550
568
|
* @param {Function} fn The function to throttle.
|
551
569
|
* @param {number} timeout The amount of time that must pass before the
|
@@ -676,7 +694,7 @@ function getEmptyRect() {
|
|
676
694
|
}
|
677
695
|
|
678
696
|
/**
|
679
|
-
* Checks to see if a parent element contains a child
|
697
|
+
* Checks to see if a parent element contains a child element (including inside
|
680
698
|
* shadow DOM).
|
681
699
|
* @param {Node} parent The parent element.
|
682
700
|
* @param {Node} child The child element.
|
@@ -706,6 +724,12 @@ function getParentNode(node) {
|
|
706
724
|
// If the parent is a shadow root, return the host element.
|
707
725
|
return parent.host;
|
708
726
|
}
|
727
|
+
|
728
|
+
if (parent && parent.assignedSlot) {
|
729
|
+
// If the parent is distributed in a <slot>, return the parent of a slot.
|
730
|
+
return parent.assignedSlot.parentNode;
|
731
|
+
}
|
732
|
+
|
709
733
|
return parent;
|
710
734
|
}
|
711
735
|
|
@@ -714,4 +738,4 @@ function getParentNode(node) {
|
|
714
738
|
window.IntersectionObserver = IntersectionObserver;
|
715
739
|
window.IntersectionObserverEntry = IntersectionObserverEntry;
|
716
740
|
|
717
|
-
}(
|
741
|
+
}());
|