Skip to content
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

Document Layout Instability API #23007

Merged
merged 19 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions files/en-us/_redirects.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8773,6 +8773,7 @@
/en-US/docs/Web/API/KeyframeEffectReadOnly/iterationComposite /en-US/docs/Web/API/KeyframeEffect/iterationComposite
/en-US/docs/Web/API/KeyframeEffectReadOnly/target /en-US/docs/Web/API/KeyframeEffect/target
/en-US/docs/Web/API/Largest_Contentful_Paint_API /en-US/docs/Web/API/LargestContentfulPaint
/en-US/docs/Web/API/Layout_Instability_API /en-US/docs/Web/API/LayoutShift
/en-US/docs/Web/API/LinearAccelerationSensor/x /en-US/docs/Web/API/Accelerometer/x
/en-US/docs/Web/API/LinearAccelerationSensor/y /en-US/docs/Web/API/Accelerometer/y
/en-US/docs/Web/API/LinearAccelerationSensor/z /en-US/docs/Web/API/Accelerometer/z
Expand Down
45 changes: 0 additions & 45 deletions files/en-us/web/api/layout_instability_api/index.md

This file was deleted.

53 changes: 53 additions & 0 deletions files/en-us/web/api/layoutshift/hadrecentinput/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: LayoutShift.hadRecentInput
slug: Web/API/LayoutShift/hadRecentInput
page-type: web-api-instance-property
browser-compat: api.LayoutShift.hadRecentInput
status:
- experimental
---

{{SeeCompatTable}}{{APIRef("Performance API")}}

The **`hadRecentInput`** read-only property of the {{domxref("LayoutShift")}} interface returns `true` if {{domxref("LayoutShift.lastInputTime", "lastInputTime")}} is less than 500 milliseconds in the past.

Layout shifts are only a problem if the user is not expecting them, so layout shifts that are the result of user interactions (such as a user expanding a UI element) are often not considered in layout shift metrics. The `hadRecentInput` property allows you to exclude these shifts.

## Value

A boolean returning `true` if {{domxref("LayoutShift.lastInputTime", "lastInputTime")}} is less than 500 milliseconds in the past; `false` otherwise.

## Examples

### Ignoring recent user input for layout shift scores

The following example shows how the `hadRecentInput` property is used to only count layout shifts without recent user input.

```js
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Count layout shifts without recent user input only
if (!entry.hadRecentInput) {
Elchi3 marked this conversation as resolved.
Show resolved Hide resolved
console.log("LayoutShift value:", entry.value);
if (entry.sources) {
for (const { node, curRect, prevRect } of entry.sources)
Elchi3 marked this conversation as resolved.
Show resolved Hide resolved
console.log("LayoutShift source:", node, { curRect, prevRect });
}
}
}
});

observer.observe({ type: "layout-shift", buffered: true });
```

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- {{domxref("LayoutShift.lastInputTime")}}
78 changes: 45 additions & 33 deletions files/en-us/web/api/layoutshift/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,43 @@ status:
browser-compat: api.LayoutShift
---

{{APIRef("Layout Instability API")}}{{SeeCompatTable}}
{{APIRef("Performance API")}}{{SeeCompatTable}}

The `LayoutShift` interface of the [Layout Instability API](/en-US/docs/Web/API/Layout_Instability_API) provides insights into the stability of web pages based on movements of the elements on the page.
The `LayoutShift` interface of the [Performance API](/en-US/docs/Web/API/Performance_API) provides insights into the layout stability of web pages based on movements of the elements on the page.

## Description

A layout shift happens when any element that is visible in the viewport changes its position between two frames. These elements are described as being **unstable**, indicating a lack of visual stability.

The Layout Instability API provides a way to measure and report on these layout shifts. All tools for debugging layout shifts, including those in the browser's developer tools, use this API. The API can also be used to observe and debug layout shifts by logging the information to the console, to send the data to a server endpoint, or to web page analytics.

Popular performance tools, use this API to calculate a [Cumulative Layout Shift (CLS)](https://web.dev/cls/) score.

{{InheritanceDiagram}}

## Instance properties

This interface extends the following {{domxref("PerformanceEntry")}} properties by qualifying them as follows:

- {{domxref("PerformanceEntry.duration")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Always returns `0` (the concept of duration does not apply to layout shifts).
- {{domxref("PerformanceEntry.entryType")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Always returns `"layout-shift"`.
- {{domxref("PerformanceEntry.name")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Always returns `"layout-shift"`.
- {{domxref("PerformanceEntry.startTime")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Returns a {{domxref("DOMHighResTimeStamp")}} representing the time when the layout shift started.

This interface also supports the following properties:

- {{domxref("LayoutShift.value")}} {{Experimental_Inline}}
- : Returns the `impact fraction` (fraction of the viewport that was shifted) times the `distance fraction` (distance moved as a fraction of viewport).
- : Returns the layout shift score calculated as the impact fraction (fraction of the viewport that was shifted) multiplied by the distance fraction (distance moved as a fraction of viewport).
- {{domxref("LayoutShift.hadRecentInput")}} {{Experimental_Inline}}
- : Returns `true` if there was a user input in the past 500 milliseconds.
- : Returns `true` if {{domxref("LayoutShift.lastInputTime", "lastInputTime")}} is less than 500 milliseconds in the past.
- {{domxref("LayoutShift.lastInputTime")}} {{Experimental_Inline}}
- : Returns the time of the most recent user input.
- : Returns the time of the most recent excluding input (user input that would exclude this entry as a contributor to the CLS score) or `0` if no excluding input has occurred.
- {{domxref("LayoutShift.sources")}} {{Experimental_Inline}}
- : Returns an array of {{domxref('LayoutShiftAttribution')}} objects with information on the elements that were shifted.
- : Returns an array of {{domxref("LayoutShiftAttribution")}} objects with information on the elements that were shifted.

## Instance methods

Expand All @@ -31,39 +52,25 @@ The `LayoutShift` interface of the [Layout Instability API](/en-US/docs/Web/API/

## Examples

The following example shows how to capture layout shifts and log them to the console.
### Logging layout shift values

Note that in this example data is only sent to the server when the user leaves the tab.
The following example shows how to capture layout shifts and log them to the console.

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://webkit.org/b/209216
try {
let cumulativeLayoutShiftScore = 0;

const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Only count layout shifts without recent user input.
if (!entry.hadRecentInput) {
cumulativeLayoutShiftScore += entry.value;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Count layout shifts without recent user input only
if (!entry.hadRecentInput) {
console.log("LayoutShift value:", entry.value);
if (entry.sources) {
for (const { node, curRect, prevRect } of entry.sources)
console.log("LayoutShift source:", node, { curRect, prevRect });
}
}
});

observer.observe({ type: "layout-shift", buffered: true });

document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
// Force any pending records to be dispatched.
observer.takeRecords();
observer.disconnect();
}
});

console.log("CLS:", cumulativeLayoutShiftScore);
}
});
} catch (e) {
// Do nothing if the browser doesn't support this API.
}
observer.observe({ type: "layout-shift", buffered: true });
```

## Specifications
Expand All @@ -73,3 +80,8 @@ try {
## Browser compatibility

{{Compat}}

## See also

- {{domxref("LayoutShiftAttribution")}}
- [Cumulative Layout Shift (CLS)](https://web.dev/cls/)
54 changes: 54 additions & 0 deletions files/en-us/web/api/layoutshift/lastinputtime/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
title: LayoutShift.lastInputTime
slug: Web/API/LayoutShift/lastInputTime
page-type: web-api-instance-property
browser-compat: api.LayoutShift.lastInputTime
status:
- experimental
---

{{SeeCompatTable}}{{APIRef("Performance API")}}

The **`lastInputTime`** read-only property of the {{domxref("LayoutShift")}} interface returns the time of the most recent excluding input or `0` if no excluding input has occurred.

Layout shifts are only bad if the user wasn't expecting them. Many layout shift metrics (like [Cumulative Layout Shift (CLS)](https://web.dev/cls/)) exclude shifts that occurred soon after certain user interactions. These interactions are called _excluding inputs_. Excluding inputs are:

- Any events which signal a user's active interactive with the document: ([`mousedown`](/en-US/docs/Web/API/Element/mousedown_event), [`keydown`](/en-US/docs/Web/API/Element/keydown_event), and [`pointerdown`](/en-US/docs/Web/API/Element/pointerdown_event))
Elchi3 marked this conversation as resolved.
Show resolved Hide resolved
- Any events which directly changes the size of the viewport.
- [`change`](/en-US/docs/Web/API/HTMLElement/change_event) events.

The [`mousemove`](/en-US/docs/Web/API/Element/mousemove_event) and [`pointermove`](/en-US/docs/Web/API/Element/pointermove_event) events are **not** excluding inputs.

## Value

A {{domxref("DOMHighResTimeStamp")}} indicating the most recent excluding input time or `0` if no excluding input has occurred.

## Examples

### Logging last input times

Log excluding input times if excluding input has occurred.

```js
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.lastInputTime) {
console.log(entry.lastInputTime);
}
});
});

observer.observe({ type: "layout-shift", buffered: true });
```

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- {{domxref("LayoutShift.hadRecentInput")}}
44 changes: 44 additions & 0 deletions files/en-us/web/api/layoutshift/sources/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: LayoutShift.sources
slug: Web/API/LayoutShift/sources
page-type: web-api-instance-property
browser-compat: api.LayoutShift.sources
status:
- experimental
---

{{SeeCompatTable}}{{APIRef("Performance API")}}

The **`sources`** read-only property of the {{domxref("LayoutShift")}} interface returns an array of {{domxref("LayoutShiftAttribution")}} objects that indicate the DOM elements that moved during the layout shift.

## Value

An {{jsxref("Array")}} of {{domxref("LayoutShiftAttribution")}} objects. This array will not contain more than five sources. If there are more than five elements impacted by the layout shift, the five most impactful elements are reported.

## Examples

### Logging layout shift sources

```js
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
entry.sources.forEach((source) => {
console.log(source);
});
});
});

observer.observe({ type: "layout-shift", buffered: true });
```

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- {{domxref("LayoutShiftAttribution")}}
Loading