mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 16:28:58 +00:00
Rework IntersectionObserver
1 - Always fire the callback on the next tick. This is probably the most important change, as frameworks like React don't react well if the callback is fired immediately (they expect to continue processing the page in its current state, not in the mutated state from the callback) 2 - Always fire the callback for observed elements with a parent, whether or not those intersect or are connected. From MDN, the callback is fired "The first time the observer is initially asked to watch a target element." 3 - Add a mutation observer so that if a node is added to the root (or removed) the callback is fired. This, I think, is the best we can currently do for "intersection".
This commit is contained in:
@@ -1,96 +1,163 @@
|
||||
<!DOCTYPE html>
|
||||
<body></body>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=intersectionObserver>
|
||||
// doesn't crash (I guess)
|
||||
new IntersectionObserver(() => {}).observe(document.documentElement);
|
||||
{
|
||||
// never attached
|
||||
let count = 0;
|
||||
const div = document.createElement('div');
|
||||
new IntersectionObserver((entries) => {
|
||||
count += 1;
|
||||
}).observe(div);
|
||||
|
||||
let count_a = 0;
|
||||
const a1 = document.createElement('div');
|
||||
new IntersectionObserver(entries => {count_a += 1;}).observe(a1);
|
||||
testing.expectEqual(1, count_a);
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual(0, count);
|
||||
});
|
||||
}
|
||||
|
||||
// This test is documenting current behavior, not correct behavior.
|
||||
// Currently every time observe is called, the callback is called with all entries.
|
||||
let count_b = 0;
|
||||
let observer_b = new IntersectionObserver(entries => {count_b = entries.length;});
|
||||
const b1 = document.createElement('div');
|
||||
observer_b.observe(b1);
|
||||
testing.expectEqual(1, count_b);
|
||||
const b2 = document.createElement('div');
|
||||
observer_b.observe(b2);
|
||||
testing.expectEqual(2, count_b);
|
||||
{
|
||||
// not connected, but has parent
|
||||
let count = 0;
|
||||
const div1 = document.createElement('div');
|
||||
const div2 = document.createElement('div');
|
||||
new IntersectionObserver((entries) => {
|
||||
console.log(entries[0]);
|
||||
count += 1;
|
||||
}).observe(div1);
|
||||
|
||||
div2.appendChild(div1);
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual(1, count);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=reobserve>
|
||||
let count_bb = 0;
|
||||
let observer_bb = new IntersectionObserver(entries => {count_bb = entries.length;});
|
||||
const bb1 = document.createElement('div');
|
||||
observer_bb.observe(bb1);
|
||||
testing.expectEqual(1, count_bb)
|
||||
observer_bb.observe(bb1);
|
||||
testing.expectEqual(1, count_bb) // Still 1, not 2
|
||||
{
|
||||
let count = 0;
|
||||
let observer = new IntersectionObserver(entries => {
|
||||
count += entries.length;
|
||||
});
|
||||
|
||||
const div1 = document.createElement('div');
|
||||
document.body.appendChild(div1);
|
||||
|
||||
// cannot fire synchronously, must be on the next tick
|
||||
testing.expectEqual(0, count);
|
||||
observer.observe(div1);
|
||||
testing.expectEqual(0, count);
|
||||
observer.observe(div1);
|
||||
observer.observe(div1);
|
||||
testing.expectEqual(0, count);
|
||||
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual(1, count);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=unobserve>
|
||||
let count_c = 0;
|
||||
let observer_c = new IntersectionObserver(entries => { count_c = entries.length;});
|
||||
const c1 = document.createElement('div');
|
||||
observer_c.observe(c1);
|
||||
testing.expectEqual(1, count_c);
|
||||
observer_c.unobserve(c1);
|
||||
const c2 = document.createElement('div');
|
||||
observer_c.observe(c2);
|
||||
testing.expectEqual(1, count_c);
|
||||
</script>
|
||||
{
|
||||
let count = 0;
|
||||
let observer = new IntersectionObserver(entries => {
|
||||
count += entries.length;
|
||||
});
|
||||
|
||||
<script id=takeRecords>
|
||||
let observer_e = new IntersectionObserver(entries => {});
|
||||
let e1 = document.createElement('div');
|
||||
observer_e.observe(e1);
|
||||
const e2 = document.createElement('div');
|
||||
observer_e.observe(e2);
|
||||
testing.expectEqual(2, observer_e.takeRecords().length);
|
||||
const div1 = document.createElement('div');
|
||||
document.body.appendChild(div1);
|
||||
|
||||
testing.expectEqual(0, count);
|
||||
observer.observe(div1);
|
||||
testing.expectEqual(0, count);
|
||||
observer.observe(div1);
|
||||
observer.observe(div1);
|
||||
testing.expectEqual(0, count);
|
||||
|
||||
observer.unobserve(div1);
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual(0, count);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=disconnect>
|
||||
let observer_d = new IntersectionObserver(entries => {});
|
||||
let d1 = document.createElement('div');
|
||||
observer_d.observe(d1);
|
||||
observer_d.disconnect();
|
||||
testing.expectEqual(0, observer_d.takeRecords().length);
|
||||
{
|
||||
let count = 0;
|
||||
let observer = new IntersectionObserver(entries => {
|
||||
count += entries.length;
|
||||
});
|
||||
|
||||
const div1 = document.createElement('div');
|
||||
document.body.appendChild(div1);
|
||||
|
||||
// cannot fire synchronously, must be on the next tick
|
||||
testing.expectEqual(0, count);
|
||||
observer.observe(div1);
|
||||
testing.expectEqual(0, count);
|
||||
observer.observe(div1);
|
||||
observer.observe(div1);
|
||||
testing.expectEqual(0, count);
|
||||
observer.disconnect();
|
||||
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual(0, count);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=entry>
|
||||
let entry;
|
||||
let div1 = document.createElement('div');
|
||||
document.body.appendChild(div1);
|
||||
new IntersectionObserver(entries => { entry = entries[0]; }).observe(div1);
|
||||
{
|
||||
let entry = null;
|
||||
let observer = new IntersectionObserver(entries => {
|
||||
entry = entries[0];
|
||||
});
|
||||
|
||||
testing.expectEqual(0, entry.boundingClientRect.x);
|
||||
testing.expectEqual(1, entry.intersectionRatio);
|
||||
testing.expectEqual(0, entry.intersectionRect.x);
|
||||
testing.expectEqual(0, entry.intersectionRect.y);
|
||||
testing.expectEqual(1, entry.intersectionRect.width);
|
||||
testing.expectEqual(1, entry.intersectionRect.height);
|
||||
testing.expectEqual(true, entry.isIntersecting);
|
||||
testing.expectEqual(0, entry.rootBounds.x);
|
||||
testing.expectEqual(0, entry.rootBounds.y);
|
||||
testing.expectEqual(1, entry.rootBounds.width);
|
||||
testing.expectEqual(1, entry.rootBounds.height);
|
||||
testing.expectEqual('[object HTMLDivElement]', entry.target.toString());
|
||||
let div1 = document.createElement('div');
|
||||
document.body.appendChild(div1);
|
||||
new IntersectionObserver(entries => { entry = entries[0]; }).observe(div1);
|
||||
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual(0, entry.boundingClientRect.x);
|
||||
testing.expectEqual(1, entry.intersectionRatio);
|
||||
testing.expectEqual(0, entry.intersectionRect.x);
|
||||
testing.expectEqual(0, entry.intersectionRect.y);
|
||||
testing.expectEqual(1, entry.intersectionRect.width);
|
||||
testing.expectEqual(1, entry.intersectionRect.height);
|
||||
testing.expectEqual(true, entry.isIntersecting);
|
||||
testing.expectEqual(0, entry.rootBounds.x);
|
||||
testing.expectEqual(0, entry.rootBounds.y);
|
||||
testing.expectEqual(1, entry.rootBounds.width);
|
||||
testing.expectEqual(1, entry.rootBounds.height);
|
||||
testing.expectEqual('[object HTMLDivElement]', entry.target.toString());
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=options>
|
||||
const new_root = document.createElement('span');
|
||||
document.body.appendChild(new_root);
|
||||
<script id=timing>
|
||||
{
|
||||
const capture = [];
|
||||
const observer = new IntersectionObserver(() => {
|
||||
capture.push('callback');
|
||||
});
|
||||
|
||||
let new_entry;
|
||||
const new_observer = new IntersectionObserver(
|
||||
(entries) => { new_entry = entries[0]; },
|
||||
{root: new_root, rootMargin: '0px 0px 0px 0px', threshold: [0]}
|
||||
);
|
||||
const div = document.createElement('div');
|
||||
capture.push('pre-append');
|
||||
document.body.appendChild(div);
|
||||
capture.push('post-append');
|
||||
|
||||
new_observer.observe(document.createElement('div'));
|
||||
testing.expectEqual(1, new_entry.rootBounds.x);
|
||||
capture.push('pre-observe');
|
||||
observer.observe(div);
|
||||
capture.push('post-observe');
|
||||
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual([
|
||||
'pre-append',
|
||||
'post-append',
|
||||
'pre-observe',
|
||||
'post-observe',
|
||||
'callback',
|
||||
], capture)
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user