If you want to log to console, or send to analytics, the longest animation frame and the longest running script within that long animation frame.
The following code works on Chrome versions previous to 122. If you willing to support from Chrome 122 up, you should just use the non-polyfilled version of this script.
const REPORTING_THRESHOLD_MS = 150;
const sourceParts = (source) => {
const atPos = source.indexOf("@");
const lastColon = source.lastIndexOf(":");
const lastColonIsHttpColon = lastColon < 6;
const sourceURL = lastColonIsHttpColon
? source.substring(atPos + 1)
: source.substring(atPos + 1, lastColon);
const sourceFunctionName = source.substring(0, atPos);
const sourceCharPosition = lastColonIsHttpColon
? 0
: Number(source.substring(lastColon + 1));
return {
sourceURL,
sourceFunctionName,
sourceCharPosition,
};
};
const observer = new PerformanceObserver((list) => {
let longestScript = null; // Track the script with the longest duration
let longestEntry = null; // Track the entry containing the longest script
for (const entry of list.getEntries()) {
if (
entry.duration > REPORTING_THRESHOLD_MS &&
entry.firstUIEventTimestamp > 0
) {
const longestDuration = entry.scripts.reduce(
(maxDuration, script) => Math.max(maxDuration, script.duration),
0
);
const currentLongestScript = entry.scripts.find(
(script) => script.duration === longestDuration
);
if (!longestScript || longestDuration > longestScript.duration) {
// Update longestScript and longestEntry
longestScript = currentLongestScript;
longestEntry = entry;
}
}
}
if (longestScript && longestEntry) {
// Example here logs to console, but could also report back to analytics
const newPropertyNames = !!longestScript.invokerType;
const objToLog = newPropertyNames
? // For Chrome 122+
{
loaf_entry: {
start: longestEntry.startTime,
duration: longestEntry.duration,
},
longestScript: ({
invoker,
invokerType,
duration,
sourceURL,
sourceCharPosition,
sourceFunctionName,
pauseDuration,
forcedStyleAndLayoutDuration,
} = longestScript),
}
: // For Chrome up to 121
{
loaf_entry: {
start: longestEntry.startTime,
duration: longestEntry.duration,
},
longestScript: {
invoker: longestScript.name,
invokerType: longestScript.type,
duration: longestScript.duration,
...sourceParts(longestScript.sourceLocation),
},
};
console.log(objToLog, `Remapped old property names: ${!newPropertyNames ? 'Yes' : 'No'}`);
// console.log(longestScript); // need anything else from the longest script object?
}
});
observer.observe({ type: "long-animation-frame", buffered: true });