I'm using the JavaScript Performance API and I'm trying to figure out the right combination of metrics to log my page load time on the console, whenever any individual page is requested and loads.
TLDR: I want to use JavaScript's Performance API to get a number close to the number reported by load
on the Network tab of Firefox Developer Tools (or any browser developer tools).
See the number on the right hand side in the image immediately below:
I wouldn't have an issue but for the fact that no combination that I've yet tried comes consistently close to the number reported by load
on the Network tab of Firefox Developer Tools - sometimes the final number I get is up to several hundredths of a second under, sometimes the same amount over.
It may be that I'm already achieving numbers as close as I can get, but I want to be sure that I am and not accidentally referring to inappropriate metrics.
Here are the metrics I'm using (from the PerformanceNavigationTiming
interface of the Performance API
):
domainLookupEnd
connectEnd
- I was using this before, but not currentlyresponseEnd
loadEventStart
And here's what I have at present:
window.addEventListener('load', () => {
let domainLookupEnd = performance.getEntriesByType('navigation')[0].domainLookupEnd;
let connectEnd = performance.getEntriesByType('navigation')[0].connectEnd;
let responseEnd = performance.getEntriesByType('navigation')[0].responseEnd;
let loadEventStart = performance.getEntriesByType('navigation')[0].loadEventStart;
console.log(`
domainLookupEnd: ${domainLookupEnd}
connectEnd: ${connectEnd}
responseEnd: ${responseEnd}
loadEventStart: ${loadEventStart}
Page loaded in: (${responseEnd} - ${domainLookupEnd})
Page built in: (${loadEventStart} - ${responseEnd})
Page loaded and built in: (${loadEventStart} - ${domainLookupEnd})
// ^^^ All this is just temporary helper info to ensure that the three lines below are correct
Page loaded in: ${((responseEnd - domainLookupEnd) / 1000)} seconds.
Page built in: ${((loadEventStart - responseEnd) / 1000)} seconds.
Page loaded and built in: ${((loadEventStart - domainLookupEnd) / 1000)} seconds.
`);
});
Fairly consistently I find that the metrics reported in the javascript console are between one thousandth (0.001
) and (as much as) four hundredths (0.04
) of a second less than the load
time reported by the Network tab.
Is this the best I can hope for, or am I doing something wrong / choosing the wrong metrics?
Let's start with MDN's very excellent illustration from Measuring performance:
🚩 The earliest segment moment you include is domainLookupEnd
, which means only the stuff after the green DNS lookup in the graph.
🚩 All of the MDN and Mozilla docs below consider navigationStart
the beginning of the page load time measurement, and go at least to loadEventStart
, if not to loadEventEnd
.
MDN's Page load time doc says:
Page load time is the time it takes for a page to load, measured from navigation start to the start of the load event.
let time = performance.timing; let pageloadtime = time.loadEventStart - time.navigationStart;
The MDN's Navigation Timing API doc has a slightly different calculation of page load time:
Calculate the total page load time
To compute the total amount of time it took to load the page, you can use the following code:
const perfData = window.performance.timing; const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
This subtracts the time at which navigation began (navigationStart) from the time at which the load event handler returns (loadEventEnd). This gives you the perceived page load time.
Mozilla's Improving Firefox Page Load (2020) blog post:
We define page load as the time between clicking a link, or pressing enter in the URL bar, to the time that a page is displayed in the browser and ready to be used.
Mozilla's Comparing Browser Page Load Time: An Introduction to Methodology (2017) seems to go with loadEventEnd
:
loadEventEnd
represents the moment when the load event for the requested page is completed, i.e all static content of the page is fully loaded.
It is a valid concern that
loadEventEnd
may not be the best indicator for what users experience on screen when loading a page. However, bothloadEventEnd
as well as average session load time were recently found to be good predictors for user bounce rate.
Firefox Developer Tools shows both separately, so you should make sure you are using the set of numbers that matches how your code's requests are handled.
onLoad
segment in your measurementIf Firefox is including the entire onLoad
segment in its measurement, it's including the execution time of all onLoad
or load
event scripts. This currently includes the execution time of your own measurement script!
That might explain why sometimes it differs by so little ("one thousandth") and sometimes by more ("four hundredths"): on pages where your measurement script is the only onLoad
script, it differs by just the time it takes to execute that script, and on pages that have more scripts it takes more time and the difference in measurement is larger.
There is no standard event to trigger execution after all onLoad
scripts are done. So instead you'll have to schedule it later from within onLoad
:
// 2 sec delay to cover page with heavy onLoad scripts, may need
// to lengthen, or maybe can shorten if all your pages are fast.
setTimeout(() => {
//your measurement code or call to measurement function here
}, 2000)