In short, looking at interacted elements can be misleading as an INP exists of 3 phases. So instead, you might want to start digging in your LoAF insights first.
Input delay throwing you off
One of the INP phases is the input delay
phase. In other words, it could actually be another script that was still running when a user attempted to interact with any element on your webpage. A chatwidget or cookie notice might still be running when a visitor is clicking on the hamburger menu or maybe even your "add to cart" button.
The web-vitals library will report that button as the interactionTarget
. But instead of some JavaScript that you expect to run when a button is pressed, the third party's JS was still running as well. And that JS might actually come with a bigger impact towards the responsiveness and user perception of your website (or in short: the INP score).
In other words, looking at the interactionTarget
will be misleading in this scenario.
The hidden cost of third parties
You could say third parties (and any other JS actually) are a potential invisible performance bottleneck: they need to run on the main thread to even become visible.
And we don't know if they're still blocking the main thread when users are about to interact with the webpage. We especially don't know as no-one will interact at the exact same moment in time. So solely relying on synthetic testing won't give you a full overview of the experience of your real visitors.
Processing duration and presentation delay
Your hamburger menu button will still have some event listeners as well as mentione before. Afterall, something in the screen needs to visually change. In order to do so, your JavaScript might dynamically be adding a is-open
CSS class to whatever element in your DOM. And some CSS will then trigger to actually change the pixels in the screen.
This translates to the final two phases:
- The part where you use JavaScript to change classes will become part of the
processing duration
phase. - The part where the browser is going to change pixels into the resulting visual changes is the
presentation delay
phase.
But enough on the phases. If looking at the the interactionTarget
isn't always your best bet, then what is?
Maximize your LoAF RUM data
If you're new to this subject, be sure to read up on Long Animation Frames (LoAF).
Below is how most users are approaching the LoAF RUM data of their sites in order to get most out of it.
Check LoAF invoker(type)
The LoAF invokertype is an interesting aspect to start with. For example, is it often a callback in whatever form that is causing INP issues? Or is it in fact an event-listener, directly related to the user interaction that happened.
In the technical panel, request the data from our LoAF invokertype dimension to get this data. This is also illustrated in the screenshot below.
Match it with INP breakdown
But in general and whatever the data that the invokertype is showing, I will double check this with the INP breakdown:
- If it's typically user-callback, I expect the
input delay
phase to be relatively high compared to the other phases; - If is't event-listener, I expect the
processing duration
phase to be relatively high compared to the other phases.
Data of a random website is in fact confirming this:
SPAs and hydration
I especially want to know when dealing with Single Page Applications (SPAs). Single Page Applications are using hydration for some or all parts of a website, which tends to be the biggest INP risk on a website.
If I do see a high input delay
value and a high amount of occurences in the user-callback bucket, it's typically confirming my expectations. From here, I check the LoAF function name information and will then move over to DevTools to explore the hydration process.
LoAF function name
But even if hydration is not to blame, it might be interesting to look at the function name that is exposed by LoAF insights. Truth to be told though: a build process might often obfuscate/anonymize function names, making this less valuable. Other measures might be needed to better disclose such information.
LoAF invoker
Next to look at is LoAF invoker, which will show how the script was called. Maybe you already filters on either user-callback or event-listener in the previous step to drill down this information a bit more.
Let's continue with #document.onclick
for the rest of this article.
Interaction Target
When you decided to continue research within your event-listeners bucket, the interactionTarget
as reported by the web-vitals library becomes interesting again. In RUMvision, the corresponding dimension is called metric element.
You can click on a LoAF invoker first before requesting the metric element values in your dashboard. This way, you can easily filter on metric elements for an invoker with a high occurence rate. I used #document.onclick
in this example.
The metric element dimension will show the selector identifying the element that the user first interacted with. If one (or multiple) has a high occurenty-rate, then it's not a coincidence anymore and you might have found a great candidate to debug.
In the screenshot above, the first two values have a good INP value. But we can already see another selector with a way higher INP value that we would certainly want to address. In our case, it's #search
.
And given the name, it's not hard to guess to which feature or part of the UI this is related to: probably the search feature. But at this point, we don't know if it's the button to confirm your search, or an input element.
Moving over to DevTools
Above explains how to drill down and got more specific insights. Following this will help you to get the exact conditions where most (or highest, that's up to you) INP issues were happening. This can be used to reproduce issues when testing yourselves using Chrome DevTools.
By checking the page URL as well, you know which page to navigate to to start reproducing the INP issue.
DevTools JS console
The metric element dimension will show you CSS selectors that you can easily copy from the dashboard. If not removed from the DOM, it should correspond with an element in your current DOM.
Move to the Console pane in your DevTools, and paste the value there in between the single quotes (like below) and execute the following:
$$('#search');
The double dollar-combo is a Chromium shorthand for querySelectorAll. You can try it with $$('body')
Visiting the correct URL to reproduce your INP issue, and pasting the code into DevTools can look like this:
It should give you all elements that will match your selector. Obviously, when the selector has a CSS id, it is expected to return just a single element. But there could also be multiple elements. Such as next and previous buttons in an image gallery slider.
In the results shown in the Console panel, you can collapse them and hover over each individual element to see DevTools highlighting that element in your browser window. In some cases (when elements aren't directly visible above the fold) you might want to click on the element in the Console results to then end up in the "Elements" panel. Once there, hit right-mouse-click on that element to then select "Scroll into view" in the context menu options.
Record a performance trace
I will then record a Performance trace in DevTools. When recording, I interact with the element that I identified in the previous step and then stop the recording again.
By only recording my interaction instead of the whole pageload as well, I reduce the chances of getting overwhelming data in my Performance recording.
The best part is that DevTools introduced whiskers around interactions, highlighting the INP phases as well. So when interacting, you should see these in the Interactions lane in your Performance recording.
In DevTools, you can mix and match debugging strategies with your own methodologies to get more out of your debugging efforts.
If you've got a different strategy when approaching INP issues, or if you're missing anything here, then be sure to reach out!