GoogleChrome / web-vitals

Essential metrics for a healthy site.
https://web.dev/vitals
Apache License 2.0
7.63k stars 418 forks source link

LCP Element missing from attribution data #563

Open darlinge opened 2 weeks ago

darlinge commented 2 weeks ago

Using the web-vitals library with the attribution build (reportAllChanges set to true), I've encountered several scenarios where the LCP Element is reported as a null or blank value, while the LCP URL is a valid image and the Chrome DevTools shows a valid LCP Element in the Performance Panel. (The LCP Element from the Performance Panel is clickable but does nothing) These scenarios are all normal page navigations using Chrome 130.0.6723.93 and the latest version of the web-vitals library.

From a data collection standpoint the web-vitals library reports a valid LCP, and a valid LCP URL for the image, but the LCP Element is reported as null or blank. I'm suspecting something with the page framework (this example uses Angular) that might interfere with the web-vitals library accessing the element. But the Chrome DevTools Performance Panel is able to report the LCP Element correctly.

Additionally I've checked the Web Vitals Chrome Extension, which also reports the LCP Element as null or blank.

One working and consistent example (as of November 6th 2024) is the website https://www.kay.com/ I've attached screenshots showing the LCP Element reporting a blank value, with the LCP URL as an image.

kay_lcp_issue1 kay_lcp_issue2

tunetheweb commented 2 weeks ago

I think this is a duplicate of #561 where the LCP element is removed (e.g. after a soft navigation to another page), and then only reported upon later (where the reference no longer exists hence why it's blank).

Could you test out #562 to confirm this is fixed there? The node will still not be available (as it does not exist), but the selector should be.

mmocny commented 2 weeks ago

Is this a coincidence or did a recent release somehow make this happen more often?

darlinge commented 2 weeks ago

https://web.dev/articles/lcp#what-elements-are-considered

An element with a background image loaded using the [url()](https://developer.mozilla.org/docs/Web/CSS/url()) function, (as opposed to a CSS gradient)

Adding a note here that the example above is a background image using the url() function

kay_lcp_issue3

darlinge commented 2 weeks ago

Tested https://github.com/GoogleChrome/web-vitals/pull/562 using the web-vitals.attribution.iife.js Still seeing a blank value for LCP Element

kay_lcp_issue_web_vitals_fixed

tunetheweb commented 2 weeks ago

Ah I think this is a separate (but related) issue that we can't work around.

This due to Angular's destructive hydration where it re-renders the full DOM (and therefore destroys the element after it was recognised as LCP, but before that LCP entry was sent to web-vitals). It then recreates the page with basically the same, but new, elements. As they are no bigger, the replaced LCP element is not re-emitted as an LCP element with a later timestamp (which is good as you don't get a later LCP, but bad as we don't get a new LCP event with the link to the new DOM node to report).

Therefore once the LCP event is emitted by the browser, the node is already gone, and the web-vitals library can't grab the node details between it getting the event and reporting it (which is what #562 is supposed to make better).

You can see this is the case as if you block the https://www.kay.com/scripts/main.59eab3ae2cc715d9.js request in the Network panel (so the Angular app doesn't load), then web-vitals works fine.

There is not anything the library can do about this, so you're left with a few choices:

@mmocny I guess this answers your question as to whether this is "a coincidence or did a recent release somehow make this happen more often?". It's a coincidence that we had two issues raised for this long-standing problem. And we can only work around this so much in the library!

mmocny commented 2 weeks ago

FWIW the node selector thing was for Event Timing. Makes sense that we want this for element timing as well, but that wasn't actually proposed before (afaik).

darlinge commented 2 weeks ago

Thank you for the detailed explanation @tunetheweb This does make sense regarding the Angular framework, and we can verify this behavior from the Performance Timeline.

kay_lcp_issue_perf

Would it be possible to re-emit the LCP Event in these situations, which could report the existence of the LCP Element but keep the initial LCP timings?

Also is there anything we can do to capture this LCP Element with third party JS code on the page?

tunetheweb commented 1 week ago

Would it be possible to re-emit the LCP Event in these situations, which could report the existence of the LCP Element but keep the initial LCP timings?

The problem is that this is a separate element. It is not related to the previous element. So 1) we do not re-emit elements if they are the same size or less and 2) we cannot link them even if we did reemit them. All in all the risk of breaking LCP just to fix attribution is too great here. We need the fixes in spec/browser code discussed in https://github.com/GoogleChrome/web-vitals/issues/563#issuecomment-2461310664 to solve this.

Also is there anything we can do to capture this LCP Element with third party JS code on the page?

The fix in #561 is likely our best option here. It won't be perfect as it's still possible for the Performance Observer entry to be emitted after the element has been removed, but should cover most cases.

The other option would be for you to hold your own reference to the LCP elements and use that instead.