Open zuoaoyuan opened 3 months ago
Steps to reproduce:
document.documentElement.addEventListener('click', () => alert("hi"));
const { onLCP, onINP, onCLS } = await import('https://unpkg.com/web-vitals/attribution?module');
onINP((metric) => {
console.log('web-vitals.js', metric.value, metric);
}, { reportAllChanges: true });
This results in the following:
The reported INP will show something like (specific numbers will vary):
{
"name": "INP",
"value": 376,
"rating": "needs-improvement",
"delta": 104,
"entries": [
{
"name": "pointerdown",
"entryType": "event",
"startTime": 13763.100000023842,
"duration": 376,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"interactionId": 7303,
"processingStart": 13820,
"processingEnd": 13935.100000023842,
"cancelable": true
}
],
"id": "v4-1717772209807-2007043867067",
"navigationType": "back-forward",
"attribution": {
"interactionTarget": "#textarea",
"interactionTargetElement": {},
"interactionType": "pointer",
"interactionTime": 13763.100000023842,
"nextPaintTime": 16142.899999976158,
"processedEventEntries": [
{
"name": "pointerdown",
"entryType": "event",
"startTime": 13763.100000023842,
"duration": 376,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"interactionId": 7303,
"processingStart": 13820,
"processingEnd": 13935.100000023842,
"cancelable": true
},
{
"name": "mousedown",
"entryType": "event",
"startTime": 13763.100000023842,
"duration": 376,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"interactionId": 0,
"processingStart": 13935.100000023842,
"processingEnd": 13935.100000023842,
"cancelable": true
},
{
"name": "pointerup",
"entryType": "event",
"startTime": 13769.199999928474,
"duration": 368,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"interactionId": 7303,
"processingStart": 13936.299999952316,
"processingEnd": 14136.299999952316,
"cancelable": true
},
{
"name": "mouseup",
"entryType": "event",
"startTime": 13769.199999928474,
"duration": 368,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"interactionId": 0,
"processingStart": 14136.299999952316,
"processingEnd": 14136.299999952316,
"cancelable": true
},
{
"name": "click",
"entryType": "event",
"startTime": 13769.199999928474,
"duration": 368,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"interactionId": 7303,
"processingStart": 14136.299999952316,
"processingEnd": 16142,
"cancelable": true
}
],
"longAnimationFrameEntries": [
{
"name": "long-animation-frame",
"entryType": "long-animation-frame",
"startTime": 13745.899999976158,
"duration": 2397,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"renderStart": 16142.100000023842,
"styleAndLayoutStart": 16142.299999952316,
"firstUIEventTimestamp": 13763.100000023842,
"blockingDuration": 2289,
"scripts": [
{
"name": "script",
"entryType": "script",
"startTime": 13754.100000023842,
"duration": 65,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"invoker": "TimerHandler:setTimeout",
"invokerType": "user-callback",
"windowAttribution": "self",
"executionStart": 13754.100000023842,
"forcedStyleAndLayoutDuration": 0,
"pauseDuration": 0,
"sourceURL": "https://inp-demo.glitch.me/main.js",
"sourceFunctionName": "periodicBlock",
"sourceCharPosition": 1410
},
{
"name": "script",
"entryType": "script",
"startTime": 13820,
"duration": 114,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"invoker": "DOMWindow.onpointerdown",
"invokerType": "event-listener",
"windowAttribution": "self",
"executionStart": 13820,
"forcedStyleAndLayoutDuration": 0,
"pauseDuration": 0,
"sourceURL": "https://inp-demo.glitch.me/main.js",
"sourceFunctionName": "",
"sourceCharPosition": 4059
},
{
"name": "script",
"entryType": "script",
"startTime": 13936.299999952316,
"duration": 200,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"invoker": "DOMWindow.onpointerup",
"invokerType": "event-listener",
"windowAttribution": "self",
"executionStart": 13936.299999952316,
"forcedStyleAndLayoutDuration": 0,
"pauseDuration": 0,
"sourceURL": "https://inp-demo.glitch.me/main.js",
"sourceFunctionName": "",
"sourceCharPosition": 4059
},
{
"name": "script",
"entryType": "script",
"startTime": 14136.699999928474,
"duration": 2005,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"invoker": "HTML.onclick",
"invokerType": "event-listener",
"windowAttribution": "self",
"executionStart": 14136.699999928474,
"forcedStyleAndLayoutDuration": 0,
"pauseDuration": 0,
"sourceURL": "",
"sourceFunctionName": "",
"sourceCharPosition": -1
}
]
}
],
"inputDelay": 56.89999997615814,
"processingDuration": 2322,
"presentationDelay": 0.8999999761581421,
"loadState": "complete"
}
}
The value
of 376
here represent the time it took to show the dialog-- which in this case did have observable UX delays. It does not include the time the dialog was opened.
However, the attribution
data represents the entirely of the animation frame, including other events and the total time the js dialog was opened.
In particular, "processingDuration": 2322
is somewhat inconsistent with overall value of the metric, at first glance. (the last LoAF Script entry should have a "pauseDuration":
value which is not 0, but seems not working on Canary for me...
In my opinion the attribution is FINE but there probably should be some obvious clue about WHY the numbers are the way they are. Maybe if the LoAF "pauseDuration"
worked and the sum of all pauseDuration was split out from "processingDuration"
(subtracted from and reported distinctly) this would be good enough?
To make testing this a bit easier, I've forked the INP demo to include a few buttons for triggering different dialogs and upgraded it to the latest attribution build: https://inp-demo-dialog.glitch.me/
Filed https://github.com/w3c/long-animation-frames/issues/17 to track the 0ms pauseDuration
bug
@mmocny / @zuoaoyuan this looks weird:
{
"name": "click",
"entryType": "event",
"startTime": 13769.199999928474,
"duration": 368,
"navigationId": "204030c4-b7ac-4c2c-a31d-5d15ef400627",
"interactionId": 7303,
"processingStart": 14136.299999952316,
"processingEnd": 16142,
"cancelable": true
}
The processingEnd
is > duration + startTime
.
I guess that's technically correct since we're capping the duration
but why not also cap processingEnd
? Is the thought that people might still want to measure the full duration?
I guess web-vitals could similarly cap the processingDuration
but it seems to me it's accurately reporting that based on the event timing details.
why not also cap
processingEnd
?
Because the event is not done processing yet, and we thought that it would be best (i.e. for LoAF attribution with scripts) to leave the value accurate and "honest". Also, duration
is capped because for INP we thought it made more sense-- but developers using Event Timing for other reasons might have different preferences.
I think we should expose MORE data to make this less confusing, such as making it obvious that the duration is based on an alternative fallback time, and to also still expose the presentation time alongside. We've started to do this in tracing but need to make a spec proposal to change public entry interface.
One more note: when you have multiple events in one frame, the "fallback time" will be in the middle of exactly one of those events. For the other events, the fallback time can be before processingStart or after processingEnd.
The only reliable way to know if we're using a fallback time (at least today) I think is to compare with LoAF data and see if the end time of the event disagrees with the end time of the animation frame.
Hmmm... so I guess from an INP point of view the duration has finished processing. So we could cap processingDuration
(there precedence for this in #527 for example).
Would still look weird for LoAF-based bucketing, but guess we can deal with that, when we decide how that looks.
the duration has finished processing
I would say:
I think capping processingDuration for the interaction as used for INP breakdowns for example, I think makes sense to me.
That's why I said "from an INP point of view" 😁
OK will raise a PR similar to #527
With landing of EventTimingFallbackToModalDialogStart, event timing now will treat JS modal dialog showing time as an alternative end point of INP or event duration measurement.
It has following impact on WebVitals.js v4:
So now we can have a situation like: INP: 100ms, input delay: 20ms, processing: 10000ms, presentationDelay: 0ms This doesn't break web-vitals.js which is good, and it's also technically the truth:)
However, that huge event timing processing is also used to get a LoAF attribution, and it can be something that users will find surprising if it comes from field data, and we might want to signal it somehow. (Loaf ::Script will have a pauseDuration for dialogs, which could be one strategy to mitigate this)