Skip to content

Instantly share code, notes, and snippets.

@ShirtlessKirk
Last active July 4, 2024 20:24
Show Gist options
  • Save ShirtlessKirk/eb41720a797411defae6 to your computer and use it in GitHub Desktop.
Save ShirtlessKirk/eb41720a797411defae6 to your computer and use it in GitHub Desktop.
Performance timing polyfill
/**
* performance-timing.js: Polyfill for performance.timing object
* For greatest accuracy, this needs to be run as soon as possible in the page, preferably inline.
* The values returned are necessarily not absolutely accurate, but are close enough for general purposes.
* @author ShirtlessKirk. Copyright (c) 2014.
* @license WTFPL (http://www.wtfpl.net/txt/copying)
*/
(function (window) {
'use strict';
var
document = window.document,
loadParams,
setTimeout = window.setTimeout,
D = Date,
dateNow = D.now ? function () { return D.now(); } : function () { return (new D()).getTime(); },
M = Math,
min = M.min,
start = dateNow(); // this is the earliest time we can get without built-in timing
function domLoad() {
var
time = dateNow(),
timing = window.performance.timing;
/* DOMContentLoadedEventEnd value is set via domReady function.
* However, this function may run before domReady does (making the value 0), so we check for the falsey value
*/
timing.domContentLoadedEventEnd = timing.domContentLoadedEventStart = min(timing.domContentLoadedEventEnd, time) || time;
/* If this function runs before domReady then DOMComplete will equal DOMContentLoadedEventStart
* Otherwise there will be a few ms difference
*/
timing.domComplete = timing.loadingEventEnd = timing.loadingEventStart = M.max(timing.domContentLoadedEventEnd, time);
try {
window.removeEventListener.apply(window, loadParams);
} catch (e) {
try {
window.detachEvent('onload', domLoad);
} catch (ignore) {}
}
}
function domReady() {
var
readyState = document.readyState,
time = dateNow(),
timing = window.performance.timing;
if (readyState === 'uninitialized' || readyState === 'loading' || readyState === 'interactive') {
if (readyState === 'loading') {
timing.domLoading = timing.domLoading || time;
} else if (readyState === 'interactive') {
timing.domInteractive = timing.domInteractive || time;
}
setTimeout(domReady, 9);
return;
}
timing.domInteractive = timing.domInteractive || timing.domComplete || time;
timing.domLoading = timing.domLoading || min(timing.navigationStart, time);
timing.domContentLoadedEventEnd = timing.domContentLoadedEventStart = min(timing.domInteractive, time);
if (window.history.length) {
timing.unloadEventEnd = timing.unloadEventStart = timing.navigationStart;
}
}
window.performance = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance;
if (window.performance === undefined) {
window.performance = {};
window.performance.timing = {
domComplete: 0,
domContentLoadedEventEnd: 0,
domContentLoadedEventStart: 0,
domInteractive: 0,
domLoading: 0,
legacyNavigationStart: start,
loadEventEnd: 0,
loadEventStart: 0,
navigationStart: start,
unloadEventEnd: 0,
unloadEventStart: 0
};
loadParams = ['load', domLoad, false];
if (document.readyState !== 'complete') {
try {
window.addEventListener.apply(window, loadParams);
} catch (e) {
try {
window.attachEvent('onload', domLoad);
} catch (ignore) {}
}
}
setTimeout(domReady, 0);
}
}(this));
@ShirtlessKirk
Copy link
Author

Closure compiled version (1.41KB uncompressed, 570 bytes gzipped):

(function(a){function f(){var d=g(),c=a.performance.timing;c.domContentLoadedEventEnd=c.domContentLoadedEventStart=h(c.domContentLoadedEventEnd,d)||d;c.domComplete=c.loadingEventEnd=c.loadingEventStart=l.max(c.domContentLoadedEventEnd,d);try{a.removeEventListener.apply(a,k)}catch(b){try{a.detachEvent("onload",f)}catch(e){}}}function m(){var d=n.readyState,c=g(),b=a.performance.timing;"uninitialized"===d||"loading"===d||"interactive"===d?("loading"===d?b.domLoading=b.domLoading||c:"interactive"===
d&&(b.domInteractive=b.domInteractive||c),p(m,9)):(b.domInteractive=b.domInteractive||b.domComplete||c,b.domLoading=b.domLoading||h(b.navigationStart,c),b.domContentLoadedEventEnd=b.domContentLoadedEventStart=h(b.domInteractive,c),a.history.length&&(b.unloadEventEnd=b.unloadEventStart=b.navigationStart))}var n=a.document,k,p=a.setTimeout,e=Date,g=e.now?function(){return e.now()}:function(){return(new e).getTime()},l=Math,h=l.min,q=g();a.performance=a.performance||a.webkitPerformance||a.msPerformance||
a.mozPerformance;if(void 0===a.performance){a.performance={};a.performance.timing={domComplete:0,domContentLoadedEventEnd:0,domContentLoadedEventStart:0,domInteractive:0,domLoading:0,legacyNavigationStart:q,loadEventEnd:0,loadEventStart:0,navigationStart:q,unloadEventEnd:0,unloadEventStart:0};k=["load",f,!1];if("complete"!==n.readyState)try{a.addEventListener.apply(a,k)}catch(r){try{a.attachEvent("onload",f)}catch(t){}}p(m,0)}})(this);

@MeowKim
Copy link

MeowKim commented Feb 23, 2017

line 29 need to modified :

timing.domComplete = timing.loadingEventEnd = timing.loadingEventStart = M.max(timing.domContentLoadedEventEnd, time);
=>
timing.domComplete = timing.loadEventEnd = timing.loadEventStart = M.max(timing.domContentLoadedEventEnd, time);

thx for ur work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment