-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
core(layout-shift-elements): surface CLS contribution per shifted element #10968
Conversation
All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the ℹ️ Googlers: Go here for more info. |
const clsPerNodeMap = new Map(); | ||
/** @type {Set<number>} */ | ||
const clsNodeIds = new Set(); | ||
static getLayoutShiftElements(mainThreadEvents) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gonna add comments throughout this function as it has grown pretty complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. A high-level comment here explaining the approach would be useful. Explaining that score
is the additional CLS since the last event, and that we want to distribute that incremental score between all the nodes that have shifted, proportional to the impact region of each shift.
@@ -483,6 +483,7 @@ declare global { | |||
nodeLabel?: string; | |||
devtoolsNodePath: string; | |||
snippet?: string; | |||
score?: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open to suggestions on renaming this. Don't know if there's a better naming option here so left it as score for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah this seems fine to me. score
will be specific to the type of element, so metricName
becomes a discriminant. future metrics may overload score
but it'll be orthogonal so it seems fine to just use score
and not cls_score
. also seems fine to keep the types simple and not do a real type discriminant (like interface CLSTraceElement extends TraceElement { score: number}
.
@@ -176,15 +176,15 @@ module.exports = [ | |||
}, | |||
}, | |||
// TODO: uncomment when Chrome m84 lands |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will delete this TODO once I verify the smoke test can pass in the CI runs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#10976 will take care of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you rm this change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any idea why the now deployment doesn't have any values for CLS contribution?
@@ -16,6 +16,8 @@ const pageFunctions = require('../../lib/page-functions.js'); | |||
const TraceProcessor = require('../../lib/tracehouse/trace-processor.js'); | |||
const RectHelpers = require('../../lib/rect-helpers.js'); | |||
|
|||
/** @typedef {{nodeId: Number, score?: Number}} TraceElementData */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totalAreaOfImpact += areaOfImpact; | ||
}); | ||
|
||
[...pixelsMovedPerNode.entries()].forEach(entry => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (const [key, pixelsMoved] of pixelsMovedPerNode.entries()) { ...
for-loops are better than .forEach
, and destructuring key-value entry into variables is nice for readability
.sort((a, b) => b[1] - a[1]) | ||
.slice(0, 5).map(entry => Number(entry[0])); | ||
const topFive = [...clsPerNode.entries()] | ||
.sort((a, b) => b[1] - a[1]).slice(0, 5) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.sort((a, b) => b[1] - a[1]).slice(0, 5) | |
.sort((a, b) => b[1] - a[1]) | |
.slice(0, 5) |
nit
.map(entry => { | ||
return { | ||
nodeId: Number(entry[0]), | ||
score: Number(entry[1]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like at least one of these are numbers already
@@ -483,6 +483,7 @@ declare global { | |||
nodeLabel?: string; | |||
devtoolsNodePath: string; | |||
snippet?: string; | |||
score?: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah this seems fine to me. score
will be specific to the type of element, so metricName
becomes a discriminant. future metrics may overload score
but it'll be orthogonal so it seems fine to just use score
and not cls_score
. also seems fine to keep the types simple and not do a real type discriminant (like interface CLSTraceElement extends TraceElement { score: number}
.
const clsPerNodeMap = new Map(); | ||
/** @type {Set<number>} */ | ||
const clsNodeIds = new Set(); | ||
static getLayoutShiftElements(mainThreadEvents) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. A high-level comment here explaining the approach would be useful. Explaining that score
is the additional CLS since the last event, and that we want to distribute that incremental score between all the nodes that have shifted, proportional to the impact region of each shift.
|
||
for (const [nodeId, pixelsMoved] of pixelsMovedPerNode.entries()) { | ||
let clsContribution = clsPerNode.get(nodeId) || 0; | ||
clsContribution += (pixelsMoved / totalAreaOfImpact) * Number(event.score); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is event.score already a number?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bump on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one small thing left from me (the usage of Number(event.score)
A Googler has manually verified that the CLAs look good. (Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.) ℹ️ Googlers: Go here for more info. |
The
layout-shift-elements
audit surfaces up to the top 5 elements that shifted during page load and contributed the most to the CLS metric. With this change, it'll also show the amount each of these elements contribute to the CLS score.Addresses #10751