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

INP breakdown and LoAF integration with web-vitals v4 #177

Merged
merged 42 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8db4f5b
Update web-vitals-extension to work with web vitals v4
tunetheweb Mar 26, 2024
2e885ed
Add back interaction type
tunetheweb Mar 26, 2024
1f5a72f
Simplify first-start
tunetheweb Mar 26, 2024
2d5b033
More simplier
tunetheweb Mar 26, 2024
c42c100
Tidy up
tunetheweb Mar 27, 2024
5ba9825
TTFB redirect breakdown
tunetheweb Apr 5, 2024
16fac55
Update interaction logging to be similar to v4 INP
tunetheweb Apr 5, 2024
4b8ddc9
Script order, invoker, and latest web-vitals v4
tunetheweb Apr 17, 2024
ac7d440
With LCP fixes
tunetheweb Apr 26, 2024
0064458
Fix marks
tunetheweb Apr 28, 2024
c2fb5af
Handle when no metric has target
tunetheweb Apr 30, 2024
46df833
Import thresholds from web-vitals.js
tunetheweb Apr 30, 2024
23dee41
Standardise units
tunetheweb Apr 30, 2024
4302f86
Fix TTFB sub part name
tunetheweb May 1, 2024
e8ce0bb
Latest v4 web-vitals
tunetheweb May 1, 2024
bd94df4
Review feedback
tunetheweb May 1, 2024
4e3b539
Move LoAF observer
tunetheweb May 2, 2024
9d28444
Remove interaction breakdowns to simplify code
tunetheweb May 2, 2024
3d61934
Better filtering of sub parts
tunetheweb May 2, 2024
4bdce40
Improve formatting and stop hardcoding thresholds
tunetheweb May 2, 2024
db7efd3
Further clean ups
tunetheweb May 2, 2024
6f7f8b1
More clean up
tunetheweb May 2, 2024
a4daaeb
More cleanup
tunetheweb May 2, 2024
f35e290
Interaction threshold
tunetheweb May 2, 2024
191786d
Default green status for waiting
tunetheweb May 3, 2024
8e36384
Merge branch 'formatting' into webvitals-v4-support
tunetheweb May 3, 2024
6849e26
Merge issue
tunetheweb May 3, 2024
7070fef
Update to latest v4 changes
tunetheweb May 3, 2024
e886298
Review feedback
tunetheweb May 5, 2024
dd14585
Merge branch 'main' into webvitals-v4-support
tunetheweb May 7, 2024
9ec020a
cleanup
tunetheweb May 7, 2024
4b176ec
Fix merge issues
tunetheweb May 7, 2024
ef03287
clickable script sources
tunetheweb May 8, 2024
d4ae5bf
formatting
tunetheweb May 8, 2024
638b5a6
Merge branch 'main' into webvitals-v4-support
tunetheweb May 8, 2024
4a9cc97
Better target
tunetheweb May 9, 2024
d896140
Latest build
tunetheweb May 9, 2024
356957e
Latest web-vitals build
tunetheweb May 9, 2024
7c2a68f
Add element
tunetheweb May 10, 2024
4790442
t commit -a latest web-vitals v4
tunetheweb May 12, 2024
8355b94
Upgrade web-vitals
tunetheweb May 13, 2024
76a57f9
Minor version bump
tunetheweb May 13, 2024
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
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "web-vitals-extension",
"version": "1.4.2",
"version": "1.5.0",
"description": "Instant Web Vitals metrics",
"main": "src/browser_action/vitals.js",
"repository": "https://github.com/googlechrome/web-vitals-extension",
Expand All @@ -21,6 +21,6 @@
"eslint-config-google": "^0.14.0"
},
"dependencies": {
"web-vitals": "^3.5.2"
"web-vitals": "^4.0.0"
}
}
83 changes: 76 additions & 7 deletions src/browser_action/on-each-interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,44 @@

import {INPThresholds} from './web-vitals.js';


/**
* @param {Function} callback
*/
export function onEachInteraction(callback) {
const valueToRating = (score) => score <= INPThresholds[0] ? 'good' : score <= INPThresholds[1] ? 'needs-improvement' : 'poor';

const observer = new PerformanceObserver((list) => {
const eventObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const interactions = {};

for (const entry of list.getEntries().filter((entry) => entry.interactionId)) {
const getSelector = (node, maxLen) => {
let sel = '';

try {
while (node && node.nodeType !== 9) {
const el = node;
const part = el.id
? '#' + el.id
: getName(el) +
(el.classList &&
el.classList.value &&
el.classList.value.trim() &&
el.classList.value.trim().length
? '.' + el.classList.value.trim().replace(/\s+/g, '.')
: '');
if (sel.length + part.length > (maxLen || 100) - 1) return sel || part;
sel = sel ? part + '>' + sel : part;
if (el.id) break;
node = el.parentNode;
}
} catch (err) {
// Do nothing...
}
return sel;
};

// Filter all events to those with interactionids
for (const entry of entries.filter((entry) => entry.interactionId)) {
interactions[entry.interactionId] = interactions[entry.interactionId] || [];
interactions[entry.interactionId].push(entry);
}
Expand All @@ -33,12 +60,24 @@ export function onEachInteraction(callback) {
for (const interaction of Object.values(interactions)) {
const entry = interaction.reduce((prev, curr) => prev.duration >= curr.duration ? prev : curr);
const value = entry.duration;
const interactionId = entry.interactionId;

// Filter down LoAFs to ones that intersected any event startTime and any processingEnd
const longAnimationFrameEntries = getIntersectingLoAFs(entry.startTime, entry.startTime + entry.value)

let firstEntryWithTarget = interaction[0].target;
firstEntryWithTarget = null;
if (!firstEntryWithTarget) {
firstEntryWithTarget = Object.values(interactions).flat().find(entry => entry.interactionId === interactionId && entry.target)?.target;
}

callback({
attribution: {
eventEntry: entry,
eventTime: entry.startTime,
eventType: entry.name,
interactionTarget: getSelector(firstEntryWithTarget),
interactionTargetElement: firstEntryWithTarget,
interactionTime: entry.startTime,
interactionType: entry.name.startsWith('key') ? 'keyboard' : 'pointer',
longAnimationFrameEntries: longAnimationFrameEntries
},
entries: interaction,
name: 'Interaction',
Expand All @@ -48,9 +87,39 @@ export function onEachInteraction(callback) {
}
});

observer.observe({
eventObserver.observe({
type: 'event',
durationThreshold: 0,
buffered: true,
});

let recentLoAFs = [];

const getIntersectingLoAFs = (start, end) => {
const intersectingLoAFs = [];

for (let i = 0, loaf; (loaf = recentLoAFs[i]); i++) {
// If the LoAF ends before the given start time, ignore it.
if (loaf.startTime + loaf.duration < start) continue;

// If the LoAF starts after the given end time, ignore it and all
// subsequent pending LoAFs (because they're in time order).
if (loaf.startTime > end) break;

// Still here? If so this LoAF intersects with the interaction.
intersectingLoAFs.push(loaf);
}
return intersectingLoAFs;
};

const loafObserver = new PerformanceObserver((list) => {
// We report interactions immediately, so don't need to keep many LoAFs around.
// Let's keep the last 5.
recentLoAFs = recentLoAFs.concat(list.getEntries()).slice(-5);

});
loafObserver.observe({
type: 'long-animation-frame',
buffered: true,
});
}
Loading
Loading