Element Timing: One true metric to rule them all?
One of the great things about Google's Core Web Vitals is that they provide a standard way to measure our visitors’ experience. Core Web Vitals can answer questions like:
- When was the largest element displayed? Largest Contentful Paint (LCP) measures when the largest visual element (image or video) finishes rendering.
- How much did the content move around as it loads? Cumulative Layout Shift (CLS) measures the visual stability of a page.
- How responsive was the page to visitors' actions? First Input Delay (FID) measures how quickly a page responds to a user interaction, such as a click/tap or keypress.
Sensible defaults, such as Core Web Vitals, are a good start, but one pitfall of standard measures is that they can miss what’s actually most important.
The (potential) problems with Largest Contentful Paint
Largest Contentful Paint (LCP) makes the assumption that the largest visible element is the most important content from the visitors’ perspective; however, we don’t have a choice about which element it measures. LCP may not be measuring the most appropriate – or even the same – element for each page view.
The LCP element can vary for first-time vs repeat visitors
In the case of a first-time visitor, the largest element might be a consent banner. On subsequent visits to the same page, the largest element might be an image for a product or a photo that illustrates a news story.
The screenshots from What Hifi (a UK audio-visual magazine) illustrate this problem. When the consent banner is shown, then one of its paragraphs is the LCP element. When the consent banner is not shown, an article title becomes the LCP element. In other words, the LCP timestamp varies depending on which of these two experiences the visitor had!
What Hi Fi with and without the consent banner visible
Even when it’s visible, a cookie banner may not always be the LCP element
In this example from my local newspaper, the story image is displayed and then the consent dialog covers it. But because the text elements in the dialog are smaller than the image, the image is measured for LCP.
Stroud News with and without the consent banner visible
The largest element can vary by viewport size
On some devices What HiFi has a story image hidden behind the ad at the bottom of the page, which is gets measured as LCP. But more commonly we see variations between desktop and mobile viewport sizes, where different elements are measured for LCP.
Joules clothing has this challenge on its product listing pages where the message about the impact of UK postal services going on strike is the LCP element at mobile viewport sizes, but one of the product images becomes LCP element at larger viewports.
Joules on mobile and desktop viewports
LCP uses the rendering timestamp to prioritize same-size elements
Joules’ also highlights another other challenge with LCP – if I asked you to guess which element would be measured for LCP you’d probably guess the first product image, but as all four images are the same size, and the third image was actually rendered first, it’s measured for the LCP timestamp. This might be fine in some situations, but the LCP element might not be the most important image from your – or your visitors' – perspective.
Other caveats...
Those are just some examples of the issues we come across when measuring visitors’ experiences day-to-day. There are other examples, such as:
- image elements that are animated in, and
- 'soft-navigations' in single-page apps (SPAs).
How to understand which elements are chosen for LCP
If you want to understand which elements are being chosen for LCP, you can either use synthetic monitoring or the webvital.js library, which includes support to help determine which element is the LCP element.
With LCP, Chrome is making a best guess on which element is most important, but it may not be consistent between different pages, or different viewports, and it can be affected by other UI elements too.
There's no guarantee that LCP is measuring the moments that matter to your site and your visitors. That’s where Element Timing helps.
What is Element Timing?
Similar to LCP, Element Timing measures when a DOM element is rendered. Unlike LCP, Element Timing allows you (rather than Chrome) to decide which elements you want to measure. And unlike LCP, Element Timing allows you to measure more than one element on a page.
Element Timing is implemented by adding the elementtiming
attribute to the element you want to measure, with its value set to the identifier the timestamp will be given.
For example, if we wanted to measure when an image is displayed, we could use this markup:
<img src=”/images/logo.png” elementtiming="logo-shown" />
Then, when the logo is displayed, an Element Timing entry with the identifier ‘logo-shown’ will be created, containing a timestamp and other data about the element, e.g., intersection with the viewport.
Timing entries can be retrieved using a Performance Observer, and the data can be forwarded to a RUM or analytics product:
<script>
const observer = new PerformanceObserver((list) => {
let entries = list.getEntries().forEach((entry) => {
// process entry here;
});
});
observer.observe({ entryTypes: ["element"] });
</script>
How Element Timing can fill the gaps that Largest Contentful Paint leaves
Revisiting the Joules’ example from earlier, we could use Element Timing to measure when landmarks such as the logo or category title are displayed. We could also measure when the first product image is rendered.
Example landmarks on Joules' Product Listing Page (PLP)
Similarly, a publisher might want to measure when their logo, the story headline, and the accompanying image are shown.
It should also be possible to use Element Timing to measure some pop-ups, such as consent banners; however, for now this probably depends on a site serving its own consent banner rather than relying on a third-party service.
A replacement for LCP in single-page apps?
A common question we get from customers with single-page applications (SPAs) is: "How do I measure LCP after a route change?"
The short answer is: "You can’t."
LCP starts measuring when a navigation starts and stops on user input. SPAs often only have a single navigation as the initial page loads, with responses to user actions updating the contents of the page rather than navigating to a new one.
Element Timing doesn't have these restrictions. It can measure new elements as they're inserted into the DOM and rendered, even after user interaction.
In the past we've typically relied on User Timing's marks and measures to measure how long a route change or other action takes to complete. With Element Timing we can also measure when the visual changes are displayed and get a more accurate picture of the users' experience.
How to collect Element Timing data in SpeedCurve
Element Timings can be collected in both Synthetic and RUM by configuring them in the Custom Metrics section of your settings:
Adding a custom metric in SpeedCurve
Configuring Element Timing in SpeedCurve
After you’ve started collecting Element Timings, you can add them to custom charts on your Favorites dashboards and start tracking the moments that matter for you and your visitors. You can also create performance budgets and get alerts when they exceed their thresholds.
In this example, a retailer is tracking when their logo and the first product image is displayed:
Visualising when a retailer's logo and first product image is displayed to a visitor
Limitations of Element Timing
Currently, Element Timing has a few limitations.
Only available in Chromium-based browsers
Neither WebKit's nor Firefox’s status trackers mention it, but as it's implemented as an attribute in markup, it will degrade gracefully in those browsers:
Supported on a limited subset of HTML elements
For privacy and security reasons Element Timing is only supported on a limited subset of HTML elements:
img
elementsimage
elements within an SVGvideo
element's poster image- elements with a
background-image
- text nodes
But even with just this subset we can still measure many of the loading milestones we care about, such as key images and headings.
Can't track elements within iframes
As Element Timing only measure elements in the current document, we can’t yet use it to track when say an embedded YouTube video renders or when ads are displayed, as these are typically contained within an iframe. (There’s a proposal to allow iframes to expose some of their performance data to their parent that might enable this, but it’s at an early stage.)
Elements in the ShadowDOM are currently excluded from Element Timing
Watch the discussion about this at the most recent Web Performance Working Group TPAC meeting.
Summary
Largest Contentful Paint is a useful default, but the largest element isn't necessarily the most important from a visitor's perspective.
Element Timing allows us to choose the elements that are most important on our own pages, and therefore measure moments that are most essential to our visitors' experience.
You can use SpeedCurve to collect and track Element Timings so you can see how the performance of key elements changes over time or varies between different pages.
If you've been using Element Timings to measure your visitors' experiences, or if you've implemented them in a SPA, we'd love to hear about it.
References
- Mozilla Standards Positions
- WebKit Feature Status
- Element Timing API Specification
- W3C User Timing API Specification
- Browser support for ElementTiming
- Configuring SpeedCurve Custom Data
- Proposal for Cross Frame Performance Timeline