← Asynchronous CSS Include Download

/*-----------------------------------------------------------------------------\ | Asynchronously includes a CSS stylesheet. Can fall back to a second link. | +------------------------------------------------------------------------------+ | Usage: | | <script> | | cssAsync("https://cdn.example.com/css/style.css", "/css/style.css"); | | <script> | | <noscript> | | <link rel="stylesheet" href="/css/style.css" /> | | </noscript> | +---------+-----------+----------+---------------------------------------------| | @param | {string} | primary | Primary URL (typically a CDN). | | @param | {string} | fallback | Fallback URL (typically a local copy). | | @param | string | callback | Callback function after <link> loads. | | @return | {void} | | | \---------+-----------+----------+--------------------------------------------*/ function cssAsync(primary, fallback, callback){ /* Build AJAX request. */ var xhr = new XMLHttpRequest(); xhr.open("HEAD", primary); /* AJAX state change callback. */ xhr.onreadystatechange = function(){ /* AJAX completion. */ if(this.readyState === this.DONE){ /* Create <link> to hold stylesheet. */ var link = document.createElement("link"); link.rel = "stylesheet"; /* Use CDN on success. || Cloudflare returns 503 on HEADs. */ if(this.status === 200 || this.status === 503){ link.href = primary; }else{ link.href = fallback; } /* Add nonce if applicable. */ var nonce = ""; var nonces = document.querySelectorAll("[nonce]"); for(var i = nonces.length - 1; i >= 0; i--){ nonce = nonces[i].getAttribute("nonce"); if(nonce){ link.setAttribute("nonce", nonce); break; } } /* Apply callback. */ link.onload = callback; /* Insert into <head>. */ document.head.insertBefore(link, document.head.firstChild); } }; /* Send AJAX request. */ try{xhr.send()}finally{} }