Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactivity API: Defer hydration until node is scrolled near the viewport #58284

Open
wants to merge 19 commits into
base: trunk
Choose a base branch
from
Open
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
645c945
Break long hydration task in interactivity init
westonruter Jan 25, 2024
6ac9d62
Yield to main between toVdom() and hydrate()
westonruter Jan 25, 2024
cffdb4d
Update changelog
westonruter Jan 25, 2024
4cc10a7
Delay hydration of nodes until they near the viewport
westonruter Jan 25, 2024
3770575
Yield to main after hydration
westonruter Jan 25, 2024
1f845c9
Check if isInputPending prior to hydration
westonruter Jan 25, 2024
d0587db
Merge remote-tracking branch 'origin/trunk' into try/interactivity-la…
westonruter Jan 26, 2024
cfe2b04
Merge branch 'trunk' of https://github.com/WordPress/gutenberg into t…
westonruter Feb 6, 2024
d580cce
Merge branch 'trunk' into try/interactivity-lazy-hydration
westonruter Jul 23, 2024
fb15565
Remove now-discouraged use of isInputPending
westonruter Jul 23, 2024
3ec8de5
Merge branch 'trunk' of https://github.com/WordPress/gutenberg into t…
westonruter Oct 10, 2024
6b95333
Merge branch 'trunk' into try/interactivity-lazy-hydration
westonruter Dec 19, 2024
5532ee4
Merge branch 'trunk' of https://github.com/WordPress/gutenberg into t…
westonruter Dec 21, 2024
08720c4
Fix e2e tests by scrolling elements into viewport to trigger intersec…
sirreal Dec 26, 2024
d27985e
Always scroll to page bottom
sirreal Dec 26, 2024
88d679c
Merge branch 'trunk' of https://github.com/WordPress/gutenberg into t…
westonruter Jan 7, 2025
abd3747
Use counter instead of Set of observed nodes
westonruter Jan 7, 2025
387ab6c
Restore missing initialVdom population
westonruter Jan 7, 2025
d100ab7
Skip observing already-hydrated nodes
westonruter Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions packages/interactivity/src/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,47 @@ function yieldToMain() {

// Initialize the router with the initial DOM.
export const init = async () => {
const pendingNodes = new Set();

const intersectionObserver = new window.IntersectionObserver(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are no nodes below, then this wouldn't need to be constructed in the first place.

async ( entries ) => {
for ( const entry of entries ) {
if ( ! entry.isIntersecting ) {
continue;
}

const node = entry.target;
intersectionObserver.unobserve( node );
pendingNodes.delete( node );
if ( pendingNodes.size === 0 ) {
intersectionObserver.disconnect();
}
Copy link
Member Author

@westonruter westonruter Jan 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be problematic for islands that are added dynamically during client-side navigations. The IntersectionObserver may need to remain persistently, if init() isn't called again after new islands are added.


if ( ! hydratedIslands.has( node ) ) {
while ( window.navigator.scheduling?.isInputPending() ) {
await yieldToMain();
}
const fragment = getRegionRootFragment( node );
const vdom = toVdom( node );
await yieldToMain();
hydrate( vdom, fragment );
await yieldToMain();
}
}
},
{
root: null, // To watch for intersection relative to the device's viewport.
rootMargin: '100% 0% 100% 0%', // Intersect when within 1 viewport approaching from top or bottom.
threshold: 0.0, // As soon as even one pixel is visible.
}
);

const nodes = document.querySelectorAll(
`[data-${ directivePrefix }-interactive]`
);

for ( const node of nodes ) {
if ( ! hydratedIslands.has( node ) ) {
await yieldToMain();
const fragment = getRegionRootFragment( node );
const vdom = toVdom( node );
await yieldToMain();
hydrate( vdom, fragment );
}
pendingNodes.add( node );
intersectionObserver.observe( node );
}
};
Loading