-
Notifications
You must be signed in to change notification settings - Fork 95
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
Leverage URL metrics to reserve space for embeds to reduce CLS #1373
base: trunk
Are you sure you want to change the base?
Conversation
f38247b
to
947ca41
Compare
3f38eb0
to
b3ca4ad
Compare
return false; | ||
} | ||
|
||
$max_intersection_ratio = $context->url_metrics_group_collection->get_element_max_intersection_ratio( $processor->get_xpath() ); | ||
$embed_wrapper_xpath = $processor->get_xpath() . '/*[1][self::DIV]'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not ideal to be constructing this XPath manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've opened #1407 to explore this, but it's not necessary for this PR to move forward.
I noticed Query Monitor flagging a PHP deprecation error when loading a page with Optimization Detective and Embed Optimizer active. A TikTok embed is present on the page. I'm getting:
Call stack: Update: Addressed in #1411. |
I also saw this for some reason:
Update: See fix below in #1373 (comment) |
02b8fb3
to
b17d8ba
Compare
b17d8ba
to
76369b4
Compare
1f5e5f8
to
77bea30
Compare
$style = $processor->get_attribute( 'style' ); | ||
if ( is_string( $style ) ) { | ||
$style = rtrim( trim( $style ), ';' ) . '; '; | ||
} else { | ||
$style = ''; | ||
} | ||
$style .= sprintf( 'min-height: %dpx;', $minimum_height ); | ||
$processor->set_attribute( 'style', $style ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a helper method on OD_HTML_Tag_Processor
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See prior art in the AMP plugin: https://github.com/ampproject/amp-toolbox-php/blob/78531851c59fa5f306315372719f0064b5542cf4/src/Dom/Element.php#L49-L69
019526c
to
36e5620
Compare
Scratched code to explore using Code scratch const { onLCP, onCLS } = await import( webVitalsLibrarySrc );
onCLS(
( metric ) => {
for ( const entry of metric.entries ) {
if (
entry.entryType === 'layout-shift' &&
! entry.hadRecentInput
) {
console.info( entry );
for ( const source of entry.sources ) {
console.info( source.node );
}
}
}
},
{
// This is necessary in order to collect all layout shifts, even those which don't cause a bad CLS score.
reportAllChanges: true,
}
); |
363b50d
to
0ba2d6e
Compare
This is an iteration on top of #1098 Co-authored-by: swissspidy <[email protected]>
I'm seeing this again:
Update: Fixed in edc52fa. The issue is that every time |
…ecially to end-of-body
d10dd3c
to
edc52fa
Compare
for ( const extensionModuleUrl of extensionModuleUrls ) { | ||
const extension = await import( extensionModuleUrl ); | ||
extensions.push( extension ); | ||
// TODO: There should to be a way to pass additional args into the module. Perhaps extensionModuleUrls should be a mapping of URLs to args. It's important to pass webVitalsLibrarySrc to the extension so that onLCP, onCLS, or onINP can be obtained. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we also have JavaScript hooks you could leverage if the idea is to make this extensible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These aren't available as script modules though, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, but they could be added separately to filter the an args array here for example
setStorageLock( getCurrentTime() ); | ||
for ( const extension of extensions ) { | ||
if ( extension.finalize instanceof Function ) { | ||
extension.finalize( { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So extensions add their own data by directly modifying the passed urlMetric
? Why not have them return the new data instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this PR is currently implemented, Embed Optimizer overrides the boundingClientRect
with the final rect reported the ResizeObserver for embeds. But since then I've realized this isn't ideal because we can't know if the boundingClientRect
was the initial size or the final size. So in that case I think it would be better if the schema is made extensible (cf. #1490 (comment)) so that Embed Optimizer can add a resizedBoundingClientRect
which we then exclusively look for when setting the min-height
. So in that case, the extension's finalize
function could just return an object that gets merged on top of the urlMetric
. But it seems there could be use cases for extensions to still be able modify the entire object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think returning an object and using structuredClone
here to avoid unintended modifications is cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, sounds good. But using structuredClone()
where? We wouldn't need to pass in the urlMetric
anymore to the finalize
function, so would a clone be needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er wait, we do need to pass the urlMetric
because Embed Optimizer needs to amend Element
data in the elements
key.
Co-authored-by: swissspidy <[email protected]>
… add/embed-optimizer-min-height-reservation
} | ||
|
||
/** | ||
* Initialize. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Initialize. | |
* Finalize. |
This is needed because get_json_params() can return null. Also, no need to force the request body to be JSON. See 1da219f
Fixes #1310
TODO: Summarize the changes in this PR. In the mean time, see the videos below.
To do
min-height
style should be set on thefigure.wp-block-embed
but the measurement of the height of the embed should only come from the child.wp-block-embed__wrapper
. In this way, an embed can be allowed to shrink its height over time.min-height
until there are URL metrics gathered for the smallest breakpoint group?resizedBoundingClientRect
instead of overridingboundingClientRect
? We can then only consider elements that have the final rect set. This depends on Allow URL metric schema to be extended #1492.Demo Videos
Before
bad-cls-without-embed-optimizer.webm
After
Once URL metrics have been collected from visitors by Optimization Detective:
good-cls-with-embed-optimizer.webm