intersection-observer
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -71,3 +71,3 @@ /** | ||
io = new IntersectionObserver(null); | ||
}).to.throwException(/function/i); | ||
}).to.throwException(); | ||
}); | ||
@@ -88,3 +88,3 @@ | ||
io = new IntersectionObserver(noop, {root: 'foo'}); | ||
}).to.throwException(/element/i); | ||
}).to.throwException(); | ||
}); | ||
@@ -116,7 +116,9 @@ | ||
it('throws when rootMargin is not in pixels or pecernt', function() { | ||
expect(function() { | ||
io = new IntersectionObserver(noop, {rootMargin: '0'}); | ||
}).to.throwException(/pixels.*percent/i); | ||
}); | ||
// TODO(philipwalton): this doesn't throw in FF, consider readding once | ||
// expected behavior is clarified. | ||
// it('throws when rootMargin is not in pixels or pecernt', function() { | ||
// expect(function() { | ||
// io = new IntersectionObserver(noop, {rootMargin: '0'}); | ||
// }).to.throwException(); | ||
// }); | ||
@@ -143,11 +145,7 @@ | ||
// TODO(philipwalton) Only test this in the polyfill until the following | ||
// patch is released: https://codereview.chromium.org/2132863002 | ||
if (!supportsNativeIntersectionObserver()) { | ||
it('throws when a threshold is not a number', function() { | ||
expect(function() { | ||
io = new IntersectionObserver(noop, {threshold: ['foo']}); | ||
}).to.throwException(/threshold/i); | ||
}); | ||
} | ||
it('throws when a threshold is not a number', function() { | ||
expect(function() { | ||
io = new IntersectionObserver(noop, {threshold: ['foo']}); | ||
}).to.throwException(/threshold/i); | ||
}); | ||
@@ -174,9 +172,13 @@ | ||
it('triggers if target intersects when observing begins', function(done) { | ||
it('triggers for all targets when observing begins', function(done) { | ||
io = new IntersectionObserver(function(records) { | ||
expect(records.length).to.be(1); | ||
expect(records.length).to.be(2); | ||
expect(records[0].intersectionRatio).to.be(1); | ||
expect(records[1].intersectionRatio).to.be(0); | ||
done(); | ||
}, {root: rootEl}); | ||
targetEl2.style.top = '-40px'; | ||
io.observe(targetEl1); | ||
io.observe(targetEl2); | ||
}); | ||
@@ -187,4 +189,5 @@ | ||
io = new IntersectionObserver(function(records, observer) { | ||
expect(records.length).to.be(1); | ||
expect(records.length).to.be(2); | ||
expect(records[0] instanceof IntersectionObserverEntry).to.be.ok(); | ||
expect(records[1] instanceof IntersectionObserverEntry).to.be.ok(); | ||
expect(observer).to.be(io); | ||
@@ -194,18 +197,6 @@ expect(this).to.be(io); | ||
}, {root: rootEl}); | ||
io.observe(targetEl1); | ||
}); | ||
it('does not trigger if target does not intersect when observing begins', | ||
function(done) { | ||
var spy = sinon.spy(); | ||
io = new IntersectionObserver(spy, {root: rootEl}); | ||
targetEl2.style.top = '-40px'; | ||
io.observe(targetEl1); | ||
io.observe(targetEl2); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(0); | ||
done(); | ||
}, ASYNC_TIMEOUT); | ||
}); | ||
@@ -327,3 +318,3 @@ | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(2); | ||
expect(records.length).to.be(3); | ||
expect(records[0].target).to.be(targetEl1); | ||
@@ -333,2 +324,4 @@ expect(records[0].intersectionRatio).to.be(0.25); | ||
expect(records[1].intersectionRatio).to.be(0.75); | ||
expect(records[2].target).to.be(targetEl3); | ||
expect(records[2].intersectionRatio).to.be(0); | ||
done(); | ||
@@ -430,5 +423,2 @@ }, ASYNC_TIMEOUT); | ||
io.observe(targetEl4); | ||
// Force a new frame to fix https://crbug.com/612323 | ||
window.requestAnimationFrame && requestAnimationFrame(function(){}); | ||
}, | ||
@@ -438,9 +428,12 @@ function(done) { | ||
records = sortRecords(records); | ||
expect(records.length).to.be(3); | ||
expect(records.length).to.be(4); | ||
expect(records[0].target).to.be(targetEl1); | ||
expect(records[0].intersectionRatio).to.be(0.5); | ||
expect(records[1].target).to.be(targetEl3); | ||
expect(records[1].intersectionRatio).to.be(0.5); | ||
expect(records[2].target).to.be(targetEl4); | ||
expect(records[1].target).to.be(targetEl2); | ||
expect(records[1].intersectionRatio).to.be(0); | ||
expect(records[2].target).to.be(targetEl3); | ||
expect(records[2].intersectionRatio).to.be(0.5); | ||
expect(records[3].target).to.be(targetEl4); | ||
expect(records[3].intersectionRatio).to.be(0.5); | ||
io.disconnect(); | ||
@@ -454,5 +447,2 @@ done(); | ||
io.observe(targetEl4); | ||
// Force a new frame to fix https://crbug.com/612323 | ||
window.requestAnimationFrame && requestAnimationFrame(function(){}); | ||
}, | ||
@@ -462,7 +452,11 @@ function(done) { | ||
records = sortRecords(records); | ||
expect(records.length).to.be(2); | ||
expect(records.length).to.be(4); | ||
expect(records[0].target).to.be(targetEl1); | ||
expect(records[0].intersectionRatio).to.be(0.5); | ||
expect(records[1].target).to.be(targetEl4); | ||
expect(records[1].intersectionRatio).to.be(0.5); | ||
expect(records[1].target).to.be(targetEl2); | ||
expect(records[1].intersectionRatio).to.be(0); | ||
expect(records[2].target).to.be(targetEl3); | ||
expect(records[2].intersectionRatio).to.be(0); | ||
expect(records[3].target).to.be(targetEl4); | ||
expect(records[3].intersectionRatio).to.be(0.5); | ||
io.disconnect(); | ||
@@ -476,5 +470,2 @@ done(); | ||
io.observe(targetEl4); | ||
// Force a new frame to fix https://crbug.com/612323 | ||
window.requestAnimationFrame && requestAnimationFrame(function(){}); | ||
}, | ||
@@ -484,3 +475,3 @@ function(done) { | ||
records = sortRecords(records); | ||
expect(records.length).to.be(3); | ||
expect(records.length).to.be(4); | ||
expect(records[0].target).to.be(targetEl1); | ||
@@ -490,4 +481,6 @@ expect(records[0].intersectionRatio).to.be(0.5); | ||
expect(records[1].intersectionRatio).to.be(0.5); | ||
expect(records[2].target).to.be(targetEl4); | ||
expect(records[2].intersectionRatio).to.be(0.25); | ||
expect(records[2].target).to.be(targetEl3); | ||
expect(records[2].intersectionRatio).to.be(0); | ||
expect(records[3].target).to.be(targetEl4); | ||
expect(records[3].intersectionRatio).to.be(0.25); | ||
io.disconnect(); | ||
@@ -501,5 +494,2 @@ done(); | ||
io.observe(targetEl4); | ||
// Force a new frame to fix https://crbug.com/612323 | ||
window.requestAnimationFrame && requestAnimationFrame(function(){}); | ||
} | ||
@@ -526,5 +516,9 @@ ], done); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(1); | ||
expect(records.length).to.be(2); | ||
expect(records[0].intersectionRatio).to.be(0); | ||
expect(records[0].target).to.be(targetEl2); | ||
expect(records[0].target).to.be(targetEl1); | ||
expect(records[0].isIntersecting).to.be(false); | ||
expect(records[1].intersectionRatio).to.be(0); | ||
expect(records[1].target).to.be(targetEl2); | ||
expect(records[1].isIntersecting).to.be(true); | ||
done(); | ||
@@ -574,3 +568,4 @@ }, ASYNC_TIMEOUT); | ||
expect(records.length).to.be(1); | ||
expect(records[0].intersectionRatio).to.be(0); | ||
expect(records[0].isIntersecting).to.be(true); | ||
expect(records[0].intersectionRatio).to.be(1); | ||
done(); | ||
@@ -602,3 +597,7 @@ }, {root: rootEl}); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(0); | ||
expect(spy.callCount).to.be(1); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(1); | ||
expect(records[0].isIntersecting).to.be(false); | ||
expect(records[0].intersectionRatio).to.be(0); | ||
done(); | ||
@@ -610,5 +609,7 @@ }, ASYNC_TIMEOUT); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(1); | ||
expect(spy.callCount).to.be(2); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(1); | ||
expect(records[0].isIntersecting).to.be(true); | ||
expect(records[0].intersectionRatio).to.be(1); | ||
done(); | ||
@@ -620,5 +621,7 @@ }, ASYNC_TIMEOUT); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(2); | ||
expect(spy.callCount).to.be(3); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(1); | ||
expect(records[0].isIntersecting).to.be(false); | ||
expect(records[0].intersectionRatio).to.be(0); | ||
done(); | ||
@@ -642,8 +645,17 @@ }, ASYNC_TIMEOUT); | ||
io.observe(targetEl1); | ||
setTimeout(done, 0); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(1); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(1); | ||
expect(records[0].isIntersecting).to.be(false); | ||
expect(records[0].intersectionRatio).to.be(0); | ||
expect(records[0].target).to.be(targetEl1); | ||
done(); | ||
}, ASYNC_TIMEOUT); | ||
}, | ||
function(done) { | ||
// Adding rootEl without targetEl1 in it should trigger nothing. | ||
document.getElementById('fixtures').appendChild(rootEl); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(0); | ||
expect(spy.callCount).to.be(1); | ||
done(); | ||
@@ -653,7 +665,9 @@ }, ASYNC_TIMEOUT); | ||
function(done) { | ||
// Adding targetEl1 inside rootEl should trigger. | ||
parentEl.insertBefore(targetEl1, targetEl2); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(1); | ||
expect(spy.callCount).to.be(2); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(1); | ||
expect(records[0].isIntersecting).to.be(true); | ||
expect(records[0].intersectionRatio).to.be(1); | ||
@@ -665,7 +679,9 @@ expect(records[0].target).to.be(targetEl1); | ||
function(done) { | ||
// Removing an ancestor of targetEl1 should trigger. | ||
grandParentEl.parentNode.removeChild(grandParentEl); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(2); | ||
expect(spy.callCount).to.be(3); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(1); | ||
expect(records[0].isIntersecting).to.be(false); | ||
expect(records[0].intersectionRatio).to.be(0); | ||
@@ -677,7 +693,10 @@ expect(records[0].target).to.be(targetEl1); | ||
function(done) { | ||
// Adding the previously removed targetEl1 (via grandParentEl) back | ||
// directly inside rootEl should trigger. | ||
rootEl.appendChild(targetEl1); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(3); | ||
expect(spy.callCount).to.be(4); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
expect(records.length).to.be(1); | ||
expect(records[0].isIntersecting).to.be(true); | ||
expect(records[0].intersectionRatio).to.be(1); | ||
@@ -689,5 +708,6 @@ expect(records[0].target).to.be(targetEl1); | ||
function(done) { | ||
// Removing rootEl should trigger. | ||
rootEl.parentNode.removeChild(rootEl); | ||
setTimeout(function() { | ||
expect(spy.callCount).to.be(4); | ||
expect(spy.callCount).to.be(5); | ||
var records = sortRecords(spy.lastCall.args[0]); | ||
@@ -704,2 +724,18 @@ expect(records.length).to.be(1); | ||
if ('attachShadow' in Element.prototype) { | ||
it('handles targets in shadow DOM', function(done) { | ||
grandParentEl.attachShadow({mode: 'open'}); | ||
grandParentEl.shadowRoot.appendChild(parentEl); | ||
io = new IntersectionObserver(function(records) { | ||
expect(records.length).to.be(1); | ||
expect(records[0].intersectionRatio).to.be(1); | ||
done(); | ||
}, {root: rootEl}); | ||
io.observe(targetEl1); | ||
}); | ||
} | ||
it('handles sub-root element scrolling', function(done) { | ||
@@ -706,0 +742,0 @@ io = new IntersectionObserver(function(records) { |
@@ -30,7 +30,2 @@ /** | ||
// Use :root element of the document for .contains() calls because older IEs | ||
// support Node.prototype.contains only on Element nodes. | ||
var docElement = document.documentElement; | ||
/** | ||
@@ -59,3 +54,3 @@ * An IntersectionObserver registry. This registry exists to hold a strong | ||
// Calculates the intersection ratio. Sets it to 0 if the target area is 0. | ||
// Calculates the intersection ratio. | ||
var targetRect = this.boundingClientRect; | ||
@@ -65,3 +60,10 @@ var targetArea = targetRect.width * targetRect.height; | ||
var intersectionArea = intersectionRect.width * intersectionRect.height; | ||
this.intersectionRatio = targetArea ? (intersectionArea / targetArea) : 0; | ||
// Sets intersection ratio. | ||
if (targetArea) { | ||
this.intersectionRatio = intersectionArea / targetArea; | ||
} else { | ||
// If area is zero and is intersecting, sets to 1, otherwise to 0 | ||
this.intersectionRatio = this.isIntersecting ? 1 : 0; | ||
} | ||
} | ||
@@ -323,3 +325,5 @@ | ||
if (rootIsInDom && rootContainsTarget) { | ||
if (!oldEntry) { | ||
this._queuedEntries.push(newEntry); | ||
} else if (rootIsInDom && rootContainsTarget) { | ||
// If the new entry intersection ratio has crossed any of the | ||
@@ -490,3 +494,3 @@ // thresholds, add a new entry. | ||
IntersectionObserver.prototype._rootIsInDom = function() { | ||
return !this.root || docElement.contains(this.root); | ||
return !this.root || containsDeep(document, this.root); | ||
}; | ||
@@ -502,3 +506,3 @@ | ||
IntersectionObserver.prototype._rootContainsTarget = function(target) { | ||
return (this.root || docElement).contains(target); | ||
return containsDeep(this.root || document, target); | ||
}; | ||
@@ -662,3 +666,26 @@ | ||
/** | ||
* Checks to see if a parent element contains a child elemnt (including inside | ||
* shadow DOM). | ||
* @param {Node} parent The parent element. | ||
* @param {Node} child The child element. | ||
* @return {boolean} True if the parent node contains the child node. | ||
*/ | ||
function containsDeep(parent, child) { | ||
var node = child; | ||
while (node) { | ||
// Check if the node is a shadow root, if it is get the host. | ||
if (node.nodeType == 11 && node.host) { | ||
node = node.host; | ||
} | ||
if (node == parent) return true; | ||
// Traverse upwards in the DOM. | ||
node = node.parentNode; | ||
} | ||
return false; | ||
} | ||
// Exposes the constructors globally. | ||
@@ -665,0 +692,0 @@ window.IntersectionObserver = IntersectionObserver; |
{ | ||
"name": "intersection-observer", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "A polyfill for IntersectionObserver", | ||
@@ -5,0 +5,0 @@ "main": "intersection-observer", |
65873
1495