Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A proposal for an Event Timing specification.

License

NotificationsYou must be signed in to change notification settings

tdresser/event-timing

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 

Repository files navigation

Monitoring event latency today requires an event listener. This precludes measuring event latency early in page load, and adds unnecessary performance overhead.

This document provides a proposal for giving developers insight into the latency of a subset of events triggered by user interaction.

Minimal Proposal

This proposal explains the minimal API required to solve the following use cases:

  1. Observe the queueing delay of input events before event handlers are registered.
  2. Measure combined event handler duration, including browser event handling logic.

A polyfill approximately implementing this API can be foundhere.

Only knowing about slow events doesn't provide enough context to determine if a site is getting better or worse. If a site change results in more engaged users, and the fraction of slow events remains constant, we expect an increase in the number of slow events. We also need to enable computing the fraction of events which are slow to avoid conflating changes in event frequency with changes in event latency.

To accomplish these goals, we introduce:

interface PerformanceEventTiming :PerformanceEntry{// The type of event dispatched. E.g. "touchmove".// Doesn't require an event listener of this type to be registered.readonlyattributeDOMStringname;// "event".readonlyattributeDOMStringentryType;// The event timestamp.readonlyattributeDOMHighResTimeStampstartTime;// The time the first event handler started to execute.// |startTime| if no event handlers executed.readonlyattributeDOMHighResTimeStampprocessingStart;// The time the last event handler finished executing.// |startTime| if no event handlers executed.readonlyattributeDOMHighResTimeStampprocessingEnd;// The duration between |startTime| and the next execution of step 7.12 in the HTML event loop processing model.readonlyattributeDOMHighResTimeStampduration;// Whether or not the event was cancelable.readonlyattributebooleancancelable;};// Contains the number of events which have been dispatched, per event type.interfaceEventCounts{readonlyattributeunsignedlongclick;  ...readonlyattributeunsignedlongtouchmove;  ...};partialinterfacePerformance{// Contains the number of events which have been dispatched, per event type. Populated asynchronously.readonlyattributeEventCountseventCounts;};

Make the following modifications to the "to dispatch an event algorithm".

LetpendingEventEntries be an initially empty list ofPerformanceEventTiming objects, stored per document.

Before step one, run these steps:

  1. Ifevent is none of: "MouseEvent", "PointerEvent", "TouchEvent", "KeyboardEvent", "WheelEvent", "InputEvent", "CompositionEvent" andevent.type isn't "scroll" then return.
  2. Let newEntry be a newPerformanceEventTiming object.
  3. Set newEntry's name attribute toevent.type.
  4. Set newEntry's entryType attribute to "event".
  5. Set newEntry's startTime attribute toevent.timeStamp.
  6. Ifevent.type is "pointermove", set newEntry's startTime toevent.getCoalescedEvents()[0].startTime.
  7. Set newEntry's processingStart attribute to the value returned byperformance.now().
  8. Set newEntry's duration attribute to 0.
  9. Set newEntry's cancelable attribute toevent.cancelable.

After step 13

  • SetnewEntry.processingEnd to the value returned byperformance.now().
  • AppendnewEntry topendingEventEntries.

After step 7.12 of theevent loop processing model

  • For eachnewEntry inpendingEventEntries:
    • Set newEntry's duration attribute to the value returned by
      • Math.round((performance.now() - newEntry.startTime)/8) * 8
      • This value is rounded to the nearest 8ms to avoid providing a high resolution timer.
    • Incrementperformance.eventsCounts[newEntry.name].
    • IfnewEntry.duration > 50 && newEntry.processingStart != newEntry.processingEnd, queuenewEntry on the current document.
    • IfnewEntry.duration > 50 && newEntry.processingStart == newEntry.processingEnd, the user agent MAY queuenewEntry on the current document.

In the case where event handlers took no time, a user agent may opt not to queue the entry. This provides browsers the flexibility to ignore input which never blocks on the main thread.

Security and Privacy

To avoid adding another high resolution timer to the platform,duration is rounded to the nearest multiple of 8. Event handler duration inherits it's precision fromperformance.now(), and could previously be measured by overriding addEventListener, as demonstrated in the polyfill.

Usage

constperformanceObserver=newPerformanceObserver((entries)=>{for(constentryofentries.getEntries()){console.log(entry);}});performanceObserver.observe({entryTypes:['event']});

First Input Timing

The very first user interaction has a disproportionate impact on user experience, and is often disproportionately slow. In Chrome, the 99'th percentile of theentry.processingStart -entry.startTime for the following events is over 1 second:

  • Key down
  • Mouse down
  • Pointer down which is followed by a pointer up
  • Click

This is ~4x the 99'th percentile of these events overall. In the median, we see ~10ms for the first event, and ~2.5ms for subsequent events.

This list intentionally excludes scrolls, which are often not blocked on javascript execution.

In order to address capture user pain caused by slow initial interactions, we propose a small addition to the event timing API specific to this use-case.

LetpendingPointerDown benull.LethasDispatchedEvent be set tofalse on navigationStart.

When iterating through the entries inpendingEventEntries, after dispatchingnewEntry:

  • IfhasDispatchedEvent istrue, return.
  • LetnewFirstInputDelayEntry be a copy ofnewEntry.
  • SetnewFirstInputDelayEntry.entryType tofirstInput.
  • IfnewFirstInputDelayEntry.type is "pointerdown"`:
    • SetpendingPointerDown to newFirstInputDelayEntry
    • return
  • SethasDispatchedEvent totrue.
  • IfnewFirstInputDelayEntry.type is "pointerup":
    • QueuependingPointerDown
  • IfnewFirstInputDelayEntry.type is one of "click", "keydown" or "mousedown":
    • QueuenewFirstInputDelayEntry

FirstInputDelay can be polyfilled today: seehere for an example. However, this requires registering analytics JS before any events are processed, which is often not possible. First Input Delay can also be polyfilled on top of the event timing API, but it isn't very ergonomic, and due to the asynchrony ofperformance.eventCounts can sometimes incorrectly report an event as the first event when there was a prior event less than 50ms.

About

A proposal for an Event Timing specification.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp