intersection-observer 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/intersection-observer-test.js +143 -1
- package/intersection-observer.js +45 -17
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -61,16 +61,16 @@ There are, however, additional use cases that the default configuration will not
|
|
|
61
61
|
|
|
62
62
|
If you need to handle any of these use-cases, you can configure the polyfill to poll the document by setting the `POLL_INTERVAL` property. This can be set either globally or on a per-instance basis.
|
|
63
63
|
|
|
64
|
-
**Enabling polling for all
|
|
64
|
+
**Enabling polling for all instances:**
|
|
65
65
|
|
|
66
|
-
To enable polling for all
|
|
66
|
+
To enable polling for all instances, set a value for `POLL_INTERVAL` on the `IntersectionObserver` prototype:
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
```js
|
|
70
70
|
IntersectionObserver.prototype.POLL_INTERVAL = 100; // Time in milliseconds.
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
**Enabling polling for individual
|
|
73
|
+
**Enabling polling for individual instances:**
|
|
74
74
|
|
|
75
75
|
To enable polling on only specific instances, set a `POLL_INTERVAL` value on the instance itself:
|
|
76
76
|
|
|
@@ -94,7 +94,7 @@ var io = new IntersectionObserver(callback);
|
|
|
94
94
|
io.USE_MUTATION_OBSERVER = false;
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
This is recommended in cases where the DOM will update frequently but you know those updates will have no
|
|
97
|
+
This is recommended in cases where the DOM will update frequently but you know those updates will have no effect on the position or your target elements.
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
## iframe support
|
|
@@ -171,7 +171,7 @@ Legacy support is also possible in very old browsers by including a shim for ES5
|
|
|
171
171
|
<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
-
With these polyfills, `IntersectionObserver` has been tested
|
|
174
|
+
With these polyfills, `IntersectionObserver` has been tested and known to work in the following browsers:
|
|
175
175
|
|
|
176
176
|
<table>
|
|
177
177
|
<tr>
|
|
@@ -210,4 +210,4 @@ With these polyfills, `IntersectionObserver` has been tested an known to work in
|
|
|
210
210
|
|
|
211
211
|
To run the test suite for the `IntersectionObserver` polyfill, open the [`intersection-observer-test.html`](./intersection-observer-test.html) page in the browser of your choice.
|
|
212
212
|
|
|
213
|
-
If you run the tests in a browser that
|
|
213
|
+
If you run the tests in a browser that supports `IntersectionObserver` natively, the tests will be run against the native implementation. If it doesn't, the tests will be run against the polyfill.
|
|
@@ -69,12 +69,15 @@ describe('IntersectionObserver', function() {
|
|
|
69
69
|
io = new IntersectionObserver(noop);
|
|
70
70
|
expect(io.root).to.be(null);
|
|
71
71
|
|
|
72
|
+
io = new IntersectionObserver(noop, {root: document});
|
|
73
|
+
expect(io.root).to.be(document);
|
|
74
|
+
|
|
72
75
|
io = new IntersectionObserver(noop, {root: rootEl});
|
|
73
76
|
expect(io.root).to.be(rootEl);
|
|
74
77
|
});
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
it('throws when root is not
|
|
80
|
+
it('throws when root is not a Document or Element', function() {
|
|
78
81
|
expect(function() {
|
|
79
82
|
io = new IntersectionObserver(noop, {root: 'foo'});
|
|
80
83
|
}).to.throwException();
|
|
@@ -1398,6 +1401,89 @@ describe('IntersectionObserver', function() {
|
|
|
1398
1401
|
io.observe(iframeTargetEl2);
|
|
1399
1402
|
});
|
|
1400
1403
|
|
|
1404
|
+
it('handles tracking iframe viewport', function(done) {
|
|
1405
|
+
iframe.style.height = '100px';
|
|
1406
|
+
iframe.style.top = '100px';
|
|
1407
|
+
iframeWin.scrollTo(0, 110);
|
|
1408
|
+
// {root:iframeDoc} means to track the iframe viewport.
|
|
1409
|
+
var io = new IntersectionObserver(
|
|
1410
|
+
function (records) {
|
|
1411
|
+
io.unobserve(iframeTargetEl1);
|
|
1412
|
+
|
|
1413
|
+
var intersectionRect = rect({
|
|
1414
|
+
top: 0, // if root=null, then this would be 100.
|
|
1415
|
+
left: 0,
|
|
1416
|
+
height: 90,
|
|
1417
|
+
width: bodyWidth
|
|
1418
|
+
});
|
|
1419
|
+
expect(records.length).to.be(1);
|
|
1420
|
+
expect(rect(records[0].rootBounds)).to.eql(getRootRect(iframeDoc));
|
|
1421
|
+
expect(rect(records[0].intersectionRect)).to.eql(intersectionRect);
|
|
1422
|
+
done();
|
|
1423
|
+
},
|
|
1424
|
+
{ root: iframeDoc }
|
|
1425
|
+
);
|
|
1426
|
+
|
|
1427
|
+
io.observe(iframeTargetEl1);
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
it('handles tracking iframe viewport with rootMargin', function(done) {
|
|
1431
|
+
iframe.style.height = '100px';
|
|
1432
|
+
|
|
1433
|
+
var io = new IntersectionObserver(
|
|
1434
|
+
function (records) {
|
|
1435
|
+
io.unobserve(iframeTargetEl1);
|
|
1436
|
+
var intersectionRect = rect({
|
|
1437
|
+
top: 0, // if root=null, then this would be 100.
|
|
1438
|
+
left: 0,
|
|
1439
|
+
height: 200,
|
|
1440
|
+
width: bodyWidth
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
// rootMargin: 100% --> 3x width + 3x height.
|
|
1444
|
+
var expectedRootBounds = rect({
|
|
1445
|
+
top: -100,
|
|
1446
|
+
left: -bodyWidth,
|
|
1447
|
+
width: bodyWidth * 3,
|
|
1448
|
+
height: 100 * 3
|
|
1449
|
+
});
|
|
1450
|
+
expect(records.length).to.be(1);
|
|
1451
|
+
expect(rect(records[0].rootBounds)).to.eql(expectedRootBounds);
|
|
1452
|
+
expect(rect(records[0].intersectionRect)).to.eql(intersectionRect);
|
|
1453
|
+
done();
|
|
1454
|
+
},
|
|
1455
|
+
{ root: iframeDoc, rootMargin: '100%' }
|
|
1456
|
+
);
|
|
1457
|
+
|
|
1458
|
+
io.observe(iframeTargetEl1);
|
|
1459
|
+
});
|
|
1460
|
+
|
|
1461
|
+
// Current spec indicates that cross-document tracking yields
|
|
1462
|
+
// an essentially empty IntersectionObserverEntry.
|
|
1463
|
+
// See: https://github.com/w3c/IntersectionObserver/issues/87
|
|
1464
|
+
it('does not track cross-document elements', function(done) {
|
|
1465
|
+
var io = new IntersectionObserver(
|
|
1466
|
+
function (records) {
|
|
1467
|
+
io.unobserve(iframeTargetEl1)
|
|
1468
|
+
|
|
1469
|
+
expect(records.length).to.be(1);
|
|
1470
|
+
const zeroesRect = rect({
|
|
1471
|
+
top: 0,
|
|
1472
|
+
left: 0,
|
|
1473
|
+
width: 0,
|
|
1474
|
+
height: 0
|
|
1475
|
+
});
|
|
1476
|
+
expect(rect(records[0].rootBounds)).to.eql(zeroesRect);
|
|
1477
|
+
expect(rect(records[0].intersectionRect)).to.eql(zeroesRect);
|
|
1478
|
+
expect(records.isIntersecting).false;
|
|
1479
|
+
done();
|
|
1480
|
+
},
|
|
1481
|
+
{ root: document }
|
|
1482
|
+
);
|
|
1483
|
+
|
|
1484
|
+
io.observe(iframeTargetEl1);
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1401
1487
|
it('handles style changes', function(done) {
|
|
1402
1488
|
var spy = sinon.spy();
|
|
1403
1489
|
|
|
@@ -3022,6 +3108,62 @@ describe('IntersectionObserver', function() {
|
|
|
3022
3108
|
}
|
|
3023
3109
|
], done);
|
|
3024
3110
|
});
|
|
3111
|
+
|
|
3112
|
+
it('handles tracking iframe viewport', function(done) {
|
|
3113
|
+
iframe.style.height = '100px';
|
|
3114
|
+
iframe.style.top = '100px';
|
|
3115
|
+
iframeWin.scrollTo(0, 110);
|
|
3116
|
+
// {root:iframeDoc} means to track the iframe viewport.
|
|
3117
|
+
var io = createObserver(
|
|
3118
|
+
function (records) {
|
|
3119
|
+
io.unobserve(iframeTargetEl1);
|
|
3120
|
+
var intersectionRect = rect({
|
|
3121
|
+
top: 0, // if root=null, then this would be 100.
|
|
3122
|
+
left: 0,
|
|
3123
|
+
height: 90,
|
|
3124
|
+
width: bodyWidth
|
|
3125
|
+
});
|
|
3126
|
+
expect(records.length).to.be(1);
|
|
3127
|
+
expect(rect(records[0].rootBounds)).to.eql(getRootRect(iframeDoc));
|
|
3128
|
+
expect(rect(records[0].intersectionRect)).to.eql(intersectionRect);
|
|
3129
|
+
done();
|
|
3130
|
+
},
|
|
3131
|
+
{ root: iframeDoc }
|
|
3132
|
+
);
|
|
3133
|
+
|
|
3134
|
+
io.observe(iframeTargetEl1);
|
|
3135
|
+
});
|
|
3136
|
+
|
|
3137
|
+
it('handles tracking iframe viewport with rootMargin', function(done) {
|
|
3138
|
+
iframe.style.height = '100px';
|
|
3139
|
+
|
|
3140
|
+
var io = createObserver(
|
|
3141
|
+
function (records) {
|
|
3142
|
+
io.unobserve(iframeTargetEl1);
|
|
3143
|
+
var intersectionRect = rect({
|
|
3144
|
+
top: 0, // if root=null, then this would be 100.
|
|
3145
|
+
left: 0,
|
|
3146
|
+
height: 200,
|
|
3147
|
+
width: bodyWidth
|
|
3148
|
+
});
|
|
3149
|
+
|
|
3150
|
+
// rootMargin: 100% --> 3x width + 3x height.
|
|
3151
|
+
var expectedRootBounds = rect({
|
|
3152
|
+
top: -100,
|
|
3153
|
+
left: -bodyWidth,
|
|
3154
|
+
width: bodyWidth * 3,
|
|
3155
|
+
height: 100 * 3
|
|
3156
|
+
});
|
|
3157
|
+
expect(records.length).to.be(1);
|
|
3158
|
+
expect(rect(records[0].rootBounds)).to.eql(expectedRootBounds);
|
|
3159
|
+
expect(rect(records[0].intersectionRect)).to.eql(intersectionRect);
|
|
3160
|
+
done();
|
|
3161
|
+
},
|
|
3162
|
+
{ root: iframeDoc, rootMargin: '100%' }
|
|
3163
|
+
);
|
|
3164
|
+
|
|
3165
|
+
io.observe(iframeTargetEl1);
|
|
3166
|
+
});
|
|
3025
3167
|
});
|
|
3026
3168
|
});
|
|
3027
3169
|
});
|
package/intersection-observer.js
CHANGED
|
@@ -131,8 +131,12 @@ function IntersectionObserver(callback, opt_options) {
|
|
|
131
131
|
throw new Error('callback must be a function');
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
if (
|
|
135
|
-
|
|
134
|
+
if (
|
|
135
|
+
options.root &&
|
|
136
|
+
options.root.nodeType != 1 &&
|
|
137
|
+
options.root.nodeType != 9
|
|
138
|
+
) {
|
|
139
|
+
throw new Error('root must be a Document or Element');
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
// Binds and throttles `this._checkForIntersections`.
|
|
@@ -395,7 +399,9 @@ IntersectionObserver.prototype._monitorIntersections = function(doc) {
|
|
|
395
399
|
});
|
|
396
400
|
|
|
397
401
|
// Also monitor the parent.
|
|
398
|
-
|
|
402
|
+
var rootDoc =
|
|
403
|
+
(this.root && (this.root.ownerDocument || this.root)) || document;
|
|
404
|
+
if (doc != rootDoc) {
|
|
399
405
|
var frame = getFrameElement(doc);
|
|
400
406
|
if (frame) {
|
|
401
407
|
this._monitorIntersections(frame.ownerDocument);
|
|
@@ -415,7 +421,8 @@ IntersectionObserver.prototype._unmonitorIntersections = function(doc) {
|
|
|
415
421
|
return;
|
|
416
422
|
}
|
|
417
423
|
|
|
418
|
-
var rootDoc =
|
|
424
|
+
var rootDoc =
|
|
425
|
+
(this.root && (this.root.ownerDocument || this.root)) || document;
|
|
419
426
|
|
|
420
427
|
// Check if any dependent targets are still remaining.
|
|
421
428
|
var hasDependentTargets =
|
|
@@ -493,11 +500,18 @@ IntersectionObserver.prototype._checkForIntersections = function() {
|
|
|
493
500
|
var intersectionRect = rootIsInDom && rootContainsTarget &&
|
|
494
501
|
this._computeTargetAndRootIntersection(target, targetRect, rootRect);
|
|
495
502
|
|
|
503
|
+
var rootBounds = null;
|
|
504
|
+
if (!this._rootContainsTarget(target)) {
|
|
505
|
+
rootBounds = getEmptyRect();
|
|
506
|
+
} else if (!crossOriginUpdater || this.root) {
|
|
507
|
+
rootBounds = rootRect;
|
|
508
|
+
}
|
|
509
|
+
|
|
496
510
|
var newEntry = item.entry = new IntersectionObserverEntry({
|
|
497
511
|
time: now(),
|
|
498
512
|
target: target,
|
|
499
513
|
boundingClientRect: targetRect,
|
|
500
|
-
rootBounds:
|
|
514
|
+
rootBounds: rootBounds,
|
|
501
515
|
intersectionRect: intersectionRect
|
|
502
516
|
});
|
|
503
517
|
|
|
@@ -618,12 +632,13 @@ IntersectionObserver.prototype._computeTargetAndRootIntersection =
|
|
|
618
632
|
*/
|
|
619
633
|
IntersectionObserver.prototype._getRootRect = function() {
|
|
620
634
|
var rootRect;
|
|
621
|
-
if (this.root) {
|
|
635
|
+
if (this.root && !isDoc(this.root)) {
|
|
622
636
|
rootRect = getBoundingClientRect(this.root);
|
|
623
637
|
} else {
|
|
624
638
|
// Use <html>/<body> instead of window since scroll bars affect size.
|
|
625
|
-
var
|
|
626
|
-
var
|
|
639
|
+
var doc = isDoc(this.root) ? this.root : document;
|
|
640
|
+
var html = doc.documentElement;
|
|
641
|
+
var body = doc.body;
|
|
627
642
|
rootRect = {
|
|
628
643
|
top: 0,
|
|
629
644
|
left: 0,
|
|
@@ -714,8 +729,12 @@ IntersectionObserver.prototype._rootIsInDom = function() {
|
|
|
714
729
|
* @private
|
|
715
730
|
*/
|
|
716
731
|
IntersectionObserver.prototype._rootContainsTarget = function(target) {
|
|
717
|
-
|
|
718
|
-
(
|
|
732
|
+
var rootDoc =
|
|
733
|
+
(this.root && (this.root.ownerDocument || this.root)) || document;
|
|
734
|
+
return (
|
|
735
|
+
containsDeep(rootDoc, target) &&
|
|
736
|
+
(!this.root || rootDoc == target.ownerDocument)
|
|
737
|
+
);
|
|
719
738
|
};
|
|
720
739
|
|
|
721
740
|
|
|
@@ -802,8 +821,8 @@ function removeEvent(node, event, fn, opt_useCapture) {
|
|
|
802
821
|
if (typeof node.removeEventListener == 'function') {
|
|
803
822
|
node.removeEventListener(event, fn, opt_useCapture || false);
|
|
804
823
|
}
|
|
805
|
-
else if (typeof node.
|
|
806
|
-
node.
|
|
824
|
+
else if (typeof node.detachEvent == 'function') {
|
|
825
|
+
node.detachEvent('on' + event, fn);
|
|
807
826
|
}
|
|
808
827
|
}
|
|
809
828
|
|
|
@@ -965,19 +984,28 @@ function getParentNode(node) {
|
|
|
965
984
|
return getFrameElement(node);
|
|
966
985
|
}
|
|
967
986
|
|
|
987
|
+
// If the parent has element that is assigned through shadow root slot
|
|
988
|
+
if (parent && parent.assignedSlot) {
|
|
989
|
+
parent = parent.assignedSlot.parentNode
|
|
990
|
+
}
|
|
991
|
+
|
|
968
992
|
if (parent && parent.nodeType == 11 && parent.host) {
|
|
969
993
|
// If the parent is a shadow root, return the host element.
|
|
970
994
|
return parent.host;
|
|
971
995
|
}
|
|
972
996
|
|
|
973
|
-
if (parent && parent.assignedSlot) {
|
|
974
|
-
// If the parent is distributed in a <slot>, return the parent of a slot.
|
|
975
|
-
return parent.assignedSlot.parentNode;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
997
|
return parent;
|
|
979
998
|
}
|
|
980
999
|
|
|
1000
|
+
/**
|
|
1001
|
+
* Returns true if `node` is a Document.
|
|
1002
|
+
* @param {!Node} node
|
|
1003
|
+
* @returns {boolean}
|
|
1004
|
+
*/
|
|
1005
|
+
function isDoc(node) {
|
|
1006
|
+
return node && node.nodeType === 9;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
981
1009
|
|
|
982
1010
|
// Exposes the constructors globally.
|
|
983
1011
|
window.IntersectionObserver = IntersectionObserver;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intersection-observer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "A polyfill for IntersectionObserver",
|
|
5
|
-
"main": "intersection-observer",
|
|
5
|
+
"main": "intersection-observer.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git@github.com:w3c/IntersectionObserver.git"
|