web-vitals icon indicating copy to clipboard operation
web-vitals copied to clipboard

CLS field value difference Web Vitals JS and Google page speed test

Open shailenderjain opened this issue 2 years ago • 17 comments

Hello,

I am using the web-vitals.attribution.js to capture the field value for CLS, FID and LCP. However, there is a distinct difference between the values captured by the JS library vs "Google page speed test" field values. I am using the following code to capture the field value.

<script>
function sendToGoogleAnalytics ({name, delta, value, id, attribution}) {
  const eventParams = {
    // Built-in params:
  event_category: 'Web Vitals',
    value: delta, // Use `delta` so the value can be summed.
    // Custom params:
    metric_id: id, // Needed to aggregate events.
    metric_value: value, // Optional.
    metric_delta: delta, // Optional.
  }

  switch (name) {
    case 'CLS':
      eventParams.event_label = attribution.largestShiftTarget;
      //console.log("cls: ", attribution.largestShiftTarget);
      //console.log("cls source: ",	attribution.largestShiftSource);
      break;

    case 'FID':
      eventParams.event_label = attribution.eventTarget;
      // console.log("FID: ", attribution.eventTarget);
      break;

    case 'LCP':
      eventParams.event_label = attribution.element;
      //console.log("LCP: ", attribution.element);
      break;
  }

  // Assumes the global `gtag()` function exists, see:
  // https://developers.google.com/analytics/devguides/collection/ga4
  gtag('event', name, eventParams);
}
</script>


<!-- Append the `?module` param to load the module version of `web-vitals` -->
<script type="module">
  import {onCLS, onFID, onLCP} from 'https://unpkg.com/web-vitals@3/dist/web-vitals.attribution.js?module';
  onCLS(sendToGoogleAnalytics);
  onFID(sendToGoogleAnalytics);
  onLCP(sendToGoogleAnalytics);

  //onCLS(console.log);
  //onFID(console.log);
  //onLCP(console.log);
</script>

shailenderjain avatar Sep 28 '22 10:09 shailenderjain

Sorry, it's not really clear what your question is, can you provide more details?

As to why the data captured by web-vitals might be different from the data exposed in PSI, see this document: https://web.dev/crux-and-rum-differences/

philipwalton avatar Sep 28 '22 16:09 philipwalton

CLS_PageSpeed CLS_Analytics

I have attached 2 images for comparison. I have used Web Vitals for real time user monitoring. The CLS value is 0. However, when i run the same page on "Google page speed test" i can see the field value indicate 80% with 0. This is a gap between chrome measurement and Web Vitals library.

shailenderjain avatar Sep 29 '22 07:09 shailenderjain

Do you have any iframes on your pages?

philipwalton avatar Sep 29 '22 18:09 philipwalton

No ...i do not have any iframes on my pages. But i have added Google adsense on the pages.

shailenderjain avatar Sep 30 '22 03:09 shailenderjain

CLS_Analytics1

There is one more observation. I was trying to drill down the CLS. I tried to capture attribution.largestShiftTarget. I could see the maximum value is coming as "(not set)". What is the significance of "(not set)".

shailenderjain avatar Oct 01 '22 18:10 shailenderjain

Thanks, that screenshot is useful and I think I may know what's going on.

What is the significance of "(not set)".

"(not set)" is a value Google Analytics displays when you haven't supplied your own a value for a dimension. (It's also possible you manually set it to "(not set)", though I don't see that anywhere in the code you provided.)

If you're seeing "(not set)" that should mean that for those events, there was no largestShiftTarget value, which would only happen if there were no layout shifts. That means that for all the other events where "Event Label" is set (e.g. html>body>div.bannerdiv), there were layout shifts, despite the fact that the value is showing as 0.

So since I can infer from this report that there were layout shifts but the event value is showing as zero, that makes me suspect that you're not multiplying your CLS value by 1000 to convert it to an integer (as shown in these instructions).

In Universal Analytics (which I can tell you're using from the screenshot), even values must be integers, so if the CLS value that you're reporting for your page is 0.123, then my guess is it's just either dropping it altogether or rounding it to 0.

If you had upgraded to GA4, then you could sending float values for events, but you'd have to be using GA4 to do that.

philipwalton avatar Oct 01 '22 19:10 philipwalton

Thanks.. for your response. This is very helpful, However, i still need to understand the following.

  1. As per the Web Vitals report there were 7697 unique events and 7411 did not experience any CLS. This mean the users with no CLS will be approx. 96%
  2. However, from google page speed test the number of users with 0 or no CLS is approx. 80%.
  3. This means as per the web vitals library the % of users with CLS will be approx. 4%. Whereas as google page speed it should be approx. 20%
  4. I have been trying to understand this gap. I understand chrome uses some sampling method. But the difference is huge.
  5. Additionally, as per my observation the gap between Web Vitals library and Google page speed test started occurring from June 21.

shailenderjain avatar Oct 02 '22 06:10 shailenderjain

I presume you are filtering your Google Analytics to Desktop or Mobile to match PageSpeed Insights? And more specifically Chrome Desktop or Mobile users (so ignoring other Chromium users that also expose CLS such as Edge, or in-app WebViews)? And that you are also looking over the 28-days that CrUX collects over?

tunetheweb avatar Oct 02 '22 06:10 tunetheweb

Thanks...I did the calculation according to the filter mentioned by you.

Capture1

  1. 5669 unique events with no cls
  2. 139 unique events with CLS (Filter = Chrome, Desktop).
  3. This means approx. 2.4% of the users should experience CLS. However, the same is reported as 20%

shailenderjain avatar Oct 02 '22 07:10 shailenderjain

Quick clarification: the PageSpeed Insights report you posted shows that 80% of visits have good CLS, not that they have no CLS. Good CLS is defined as having a score of <= 0.1.

My guess is that many of cases reported via your attribution data will show that they have some layout shifting happening, but it's very small and well below the 0.1 "good" threshold. However, you'll have to update your code to fix the reporting issue in order to verify that.

philipwalton avatar Oct 02 '22 18:10 philipwalton

I have got another page where i have multiplied CLS with 1000. Capture_PDF_ID

Filter = Chrome & Desktop Total Events = 68186 Events (not set) = 66855 The events which have some attribution data shows an average value less then <0.01 Hence, the approx. % of users with 0 CLS should be around 98%. However, page speed test show 82%. Page_Speed_PDF_ID

I have been trying to do a research on the CLS using Web Vitals from a long time. My thinking is again something changed around Jun 21 which had created a discrepancy between the Web Vitals data and Chrome reported data.

My another observation is that this could be due to Google Ad sense. I have enabled this on my site from last 3 years. But, again something changed in last year Jun 21. I have only enabled Vignette ads. But, surprisingly this is creating CLS.

Adsense

Another point to note is that the issue with CLS only occurs on Desktop with Vignette Ads. This does not occur on Mobile. My CLS on mobile is 100%.

But the issue is why Web Vitals does not capture CLS?

shailenderjain avatar Oct 07 '22 08:10 shailenderjain

When did you set the multiplication by 1000?

PSI will show the values over the last 28-days so if you only set it recently in GA, it's not surprising they do not match.

It stills seems very unlikely that none of your page views have registered a CLS greater than 0. Which to me suggests this still isn't being tracked correctly. Even a perfect page can generate CLS - for example I use Chrome's translate option on that page and get a small CLS of 0.01037 when translating to English. Some changes like this, or due to extensions, may be out of the site owners control but usually represent a very small minority but none-the-less it's very unlikely this sort of thing is not represented in your analytics at all.

In fact when I do that using the Google Analytics extension on that page I can see the CLS is not being multiplied by 1000 and is still therefore registering as zero:

image

And looking at the code on that page I also don't see it multipled by 1000, except for one commented out console.log line:



function sendToGoogleAnalytics({name, delta, value, id, attribution}) {
--
  | const eventParams = {
  | // Built-in params:
  | event_category: 'Web Vitals PDF ID',
  | value: delta, // Use `delta` so the value can be summed.
  | // Custom params:
  | metric_id: id, // Needed to aggregate events.
  | metric_value: value, // Optional.
  | metric_delta: delta, // Optional.
  | }
  |  
  | switch (name) {
  | case 'CLS':
  | eventParams.event_label = attribution.largestShiftTarget;
  | //console.log("cls: ", attribution.largestShiftTarget, ":", parseFloat(delta)*1000);
  | //console.log("cls source: ",	attribution.largestShiftSource);
  | break;

tunetheweb avatar Oct 07 '22 08:10 tunetheweb

Thanks... a lot... I have updated my code. May be there was issue with my CDN. I will try to update outcome after 5 days.

shailenderjain avatar Oct 07 '22 10:10 shailenderjain

Capture_5_days

This is the result of last 5 days. This is after updating code with multiplication of 1000.

  1. Approx. 9298 unique events
  2. 175 unique events with attribution values

It looks like only 2% had some layout shift. This is inline with previous result. Wherever there is no attribution node the value for CLS is very low. This means at least approx. 98% should have CLS less then 0.1. However, google page speed test is still at 82%.

shailenderjain avatar Oct 12 '22 07:10 shailenderjain

You are still not sending integer values so think they still won't be read by GA properly and imagine all but exact integers are being ignored and treated as zero.

We note in the README that you should use Math.round (but AFTER multipling by 1000).

    // Google Analytics metrics must be integers, so the value is rounded.
    // For CLS the value is first multiplied by 1000 for greater precision
    // (note: increase the multiplier for greater precision if needed).
    eventValue: Math.round(name === 'CLS' ? delta * 1000 : delta),

tunetheweb avatar Oct 12 '22 08:10 tunetheweb

OPPO F15

On Wed, Oct 12, 2022, 14:56 Barry Pollard @.***> wrote:

You are still not sending decimal values so think they still won't be read by GA properly and imagine all but exact integers are being ignored and treated as zero.

We note in the README https://github.com/GoogleChrome/web-vitals#using-analyticsjs that you should use Math.round (but AFTER multipling by 1000).

// Google Analytics metrics must be integers, so the value is rounded.
// For CLS the value is first multiplied by 1000 for greater precision
// (note: increase the multiplier for greater precision if needed).
eventValue: Math.round(name === 'CLS' ? delta * 1000 : delta),

— Reply to this email directly, view it on GitHub https://github.com/GoogleChrome/web-vitals/issues/263#issuecomment-1275824271, or unsubscribe https://github.com/notifications/unsubscribe-auth/APRHNQP6PRGM26QBWJPMSQTWCZ4LZANCNFSM6AAAAAAQXUDAJA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

Anando12 avatar Oct 12 '22 08:10 Anando12

ok.. I have changed the code to Math.round(delta*1000)

shailenderjain avatar Oct 12 '22 09:10 shailenderjain

Capture_CLS1

Here are the result of the last 6 days. 10533 out of 10749 have an almost 0 CLS. This means at least 97.9 have less then 0.1

Trying to drill down on the second row item which is showing value of 704, I could see there were few users with some large values.

Capture_CLS2

Overall, if the right CLS value is derived after division with 1000, then i cannot see any user with CLS more then 0.1. Almost 100% users have CLS less then 0.1. Whereas google page speed test is at 84%

shailenderjain avatar Oct 18 '22 08:10 shailenderjain

I don't have an answer I'm afraid, but I do have a few comments:

The unique events don't really matter for this report. If two people experience the same shift (or one person experiences the same shift twice on loading the page), then that still counts as two people, even if it's one unique event. Unique views make more sense for page views. But still your unique events and total events are nearly similar (in fact total looks even better in terms of CLS) but thought worth noting.

You cache your page for nearly 4 days (333200 seconds). I wonder if you are still getting a significant number of page views for the code that wasn't sending back the right values (x 1000)? I wonder if it will change over time and get closer to CrUX?

You are sending back two CLS events - one is correctly multiplied up by 1000, one is not. I presume in above screenshots you are only looking at the correct one and not both?

You are sending a CLS event on each page visibility change. There should only be one event so you may be incorrectly counting multiple values. Is this why you are trying to use unique events?

You are not including events when the bfcache is used, but those will be measured in CrUX. This section details how to send events on those restores as well. But those should be relatively low CLS so doubt that's the cause.

It is interesting that you only experience the CLS issue in CrUX on desktop and not mobile. That suggests it may not be something wrong with the page, but more likely extensions, or other browser features (I notice when I use Chrome's built in translation tool causes a CLS, similarly when I zoom in). If so, these may be out of your control to a large part.

I also note that when I zoom in, the even fails to get sent with the error Payload size is too large (17241). Max allowed is 8192. as it tries to include basically the whole page as the event label. That may be skewing your numbers a bit if some of the events can't be logged.

The good news is that at 84%, you are well above the threshold, and the point of using the p75 value is in somewhat due to the fact that it is not always possible to get a perfect score. A CLS of 0 is always better of course, but there's a lots of varied users out there! However, it still seems odd to me that there is such a high percentage (16%) difference between your analytics and CrUX, so take above comments on board to see if you can narrow that down. Plus the page doesn't really show any shifts to me other than the translation issue.

tunetheweb avatar Oct 18 '22 09:10 tunetheweb

Thanks... a lot for taking time out and providing response. This is very helpful

Capture_Users

From analytics i could see 10,916 session during the same period for the same page. This is almost equal to unique events. I had done the cache clearance from CDN. I think cache may not be issue.

2 CLS events are sent with different event_category. Hence, there should not be any overlap.

There is something related to translation. I understand many users may land on the page with browser configured as en-us and page language as id. I am not sure if this will always lead to translation which can cause CLS. Is the CLS related to translation not captured.

Another important point which i have observed. I have very similar page https://miniimagesvideos.com/id_compress_jpeg This page has CLS of 95% on desktop. The important point to note is this page has same layout, users from same country, almost same type of desktop.

shailenderjain avatar Oct 18 '22 10:10 shailenderjain

Is the CLS related to translation not captured.

No, I can see this IS captured. I can see GA events firing when I translate the page. I was just pointing out that things like this are often the cause of CLS rather than necessarily anything wrong with the page.

tunetheweb avatar Oct 18 '22 10:10 tunetheweb