Is there a client-side way to detect X-Frame-Options? - javascript

Is there any good way to detect when a page isn't going to display in a frame because of the X-Frame-Options header? I know I can request the page serverside and look for the header, but I was curious if the browser has any mechanism for catching this error.

OK, this one is old but still relevant.
Fact:
When an iframe loads a url which is blocked by a X-Frame-Options the loading time is very short.
Hack:
So if the onload occurs immediately I know it's probably a X-Frame-Options issue.
Disclaimer:
This is probably one of the 'hackiest' code I've written, so don't expect much:
var timepast=false;
var iframe = document.createElement("iframe");
iframe.style.cssText = "position:fixed; top:0px; left:0px; bottom:0px; right:0px; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;";
iframe.src = "http://pix.do"; // This will work
//iframe.src = "http://google.com"; // This won't work
iframe.id = "theFrame";
// If more then 500ms past that means a page is loading inside the iFrame
setTimeout(function() {
timepast = true;
},500);
if (iframe.attachEvent){
iframe.attachEvent("onload", function(){
if(timepast) {
console.log("It's PROBABLY OK");
}
else {
console.log("It's PROBABLY NOT OK");
}
});
}
else {
iframe.onload = function(){
if(timepast) {
console.log("It's PROBABLY OK");
}
else {
console.log("It's PROBABLY NOT OK");
}
};
}
document.body.appendChild(iframe);

Disclaimer: this answer I wrote in 2012(Chrome was version ~20 at that time) is outdated and I'll keep it here for historical purposes only. Read and use at your own risk.
Ok, this is a bit old question, but here's what I found out (it's not a complete answer) for Chrome/Chromium.
the way do detect if a frame pointing to a foreign address has loaded is simply to try to access its contentWindow or document.
here's the code I used:
element.innerHTML = '<iframe class="innerPopupIframe" width="100%" height="100%" src="'+href+'"></iframe>';
myframe = $(element).find('iframe');
then, later:
try {
var letstrythis = myframe.contentWindow;
} catch(ex) {
alert('the frame has surely started loading');
}
the fact is, if the X-Frame-Options forbid access, then myFrame.contentWindow will be accessible.
the problem here is what I called "then, later". I haven't figured out yet on what to rely, which event to subsribe to find when is the good time to perform the test.

This is based on #Iftach's answer, but is a slightly less hacky.
It checks to see if iframe.contentWindow.length > 0 which would suggest that the iframe has successfully loaded.
Additionally, it checks to see if the iframe onload event has fired within 5s and alerts that too. This catches failed loading of mixed content (in an albeit hacky manner).
var iframeLoaded = false;
var iframe = document.createElement('iframe');
// ***** SWAP THE `iframe.src` VALUE BELOW FOR DIFFERENT RESULTS ***** //
// iframe.src = "https://davidsimpson.me"; // This will work
iframe.src = "https://google.com"; // This won't work
iframe.id = 'theFrame';
iframe.style.cssText = 'position:fixed; top:40px; left:10px; bottom:10px;'
+ 'right:10px; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;';
var iframeOnloadEvent = function () {
iframeLoaded = true;
var consoleDiv = document.getElementById('console');
if (iframe.contentWindow.length > 0) {
consoleDiv.innerHTML = 'āœ” Content window loaded: ' + iframe.src;
consoleDiv.style.cssText = 'color: green;'
} else {
consoleDiv.innerHTML = 'āœ˜ Content window failed to load: ' + iframe.src;
consoleDiv.style.cssText = 'color: red;'
}
}
if (iframe.attachEvent){
iframe.attachEvent('onload', iframeOnloadEvent);
} else {
iframe.onload = iframeOnloadEvent;
}
document.body.appendChild(iframe);
// iframe.onload event doesn't trigger in firefox if loading mixed content (http iframe in https parent) and it is blocked.
setTimeout(function () {
if (iframeLoaded === false) {
console.error('%cāœ˜ iframe failed to load within 5s', 'font-size: 2em;');
consoleDiv.innerHTML = 'āœ˜ iframe failed to load within 5s: ' + iframe.src;
consoleDiv.style.cssText = 'color: red;'
}
}, 5000);
Live demo here - https://jsfiddle.net/dvdsmpsn/7qusz4q3/ - so you can test it in the relevant browsers.
At time of writing, it works on the current version on Chrome, Safari, Opera, Firefox, Vivaldi & Internet Explorer 11. I've not tested it in other browsers.

The only thing I can think of is to proxy an AJAX request for the url, then look at the headers, and if it doesn't have X-Frame-Options, then show it in the iframe. Far from ideal, but better than nothing.

At least in Chrome, you can notice the failure to load because the iframe.onload event doesn't trigger. You could use that as an indicator that the page might not allow iframing.

Online test tools might be useful.
I used https://www.hurl.it/.
you can clearly see the response header.
Look for X-frame-option. if value is deny - It will not display in iframe.
same origin- only from the same domain,
allow- will allow from specific websites.
If you want to try another tool, you can simply google for 'http request test online'.

This is how I had checked for X-Frames-Options for one of my requirements. On load of a JSP page, you can use AJAX to send an asynchronous request to the specific URL as follows:
var request = new XMLHttpRequest();
request.open('GET', <insert_URL_here>, false);
request.send(null);
After this is done, you can read the response headers received as follows:
var headers = request.getAllResponseHeaders();
You can then iterate over this to find out the value of the X-Frames-Options. Once you have the value, you can use it in an appropriate logic.

This can be achieved through
a) Create a new IFrame through CreateElement
b) Set its display as 'none'
c) Load the URL through the src attribute
d) In order to wait for the iframe to load, use the SetTimeOut method to delay a function call (i had delayed the call by 10 sec)
e) In that function, check for the ContentWindow length.
f) if the length > 0, then the url is loaded else URL is not loaded due to X-Frame-Options
Below is the sample code:
function isLoaded(val) {
var elemId = document.getElementById('ctlx');
if (elemId != null)
document.body.removeChild(elemId);
var obj= document.createElement('iframe');
obj.setAttribute("id", "ctlx");
obj.src = val;
obj.style.display = 'none';
document.body.appendChild(obj);
setTimeout(canLoad, 10000);
}
function canLoad() {
//var elemId = document.getElementById('ctl100');
var elemId = document.getElementById('ctlx');
if (elemId.contentWindow.length > 0) {
elemId.style.display = 'inline';
}
else {
elemId.src = '';
elemId.style.display = 'none';
alert('not supported');
}
}

Related

Reading iframe content doesn't work under Chrome

I need to use a content of a .txt file on a web page. The problem is that I can't do it easy way (server-side php). I figured out the trick of opening a text file in the iframe and then asking for innerHTML/innerText. It turns out that people were there before - found the following code, much cleaner than my attempts, at https://zipcon.net/~swhite/docs/computers/browsers/extern_via_iframe.html
It works locally under FireFox and IE, but does not under Chrome. How to make it work under Chrome?
function getIframeContentText( frameID )
{
var elt = document.getElementById(frameID);
//alert( "getIframeContentText:" + elt );
//alert( "getIframeContentText Content:" + elt.contentDocument );
if( elt.contentDocument ) // DOM
{
var iframe_doc = elt.contentDocument;
var range = iframe_doc.createRange();
range.selectNodeContents( iframe_doc.body );
return range.toString();
}
else // IE6
{
var iframe_doc = document.all[frameID].contentWindow.document;
//return iframe_doc.body.innerHTML; // gets HTML
return iframe_doc.body.outerText;
}
}
This is because handling iFrames in ie and firefox is differentthan chrome ,because of chromes same-origin policy
start the browser using chrome.exe --user-data-dir="." --disable-web-security
and you have to wait for the document to load fully ie all the contents of the page ,it may happen there may be iFrame inside iFrame (for generic solution )
$(window).load(function()
{
chromeFrameInitializers(document);//for handling frames and IFrames
});
function chromeFrameInitializers(document)
{
var frame=document.getElementsByTagName("frame");
for(var i=0;i<frame.length;i++)
{
var subDocFrame=frame[i].contentDocument;
//do your stuf
chromeFrameInitializers(subDocFrame)
}
var iFrame=document.getElementsByTagName("iFrame");
for(var i=0;i<iFrame.length;i++)
{
var subDocIFrame=iFrame[i].contentDocument;
//do your stuf
chromeFrameInitializers(subDocIFrame)
}
}
`
it handles for both frame and iFrame (recursively ). for this $(window).load jquery needed

How to determine when document has loaded after loading external css

How to determine when document has loaded(or is loading) after loading external css?
Normal page has loaded and complete at first time(with using document.onreadystatechange or document.readyStage), but after time script will call function to place a new stylesheet CSS into HTML for changing a background or images. During change stylesheet, document has still stage complete. Stage never has been changed after calling function? Why?
Timeline(example):
Visit one page : localhost/index.html
Document has stage loading
Document has stage complete
User was trying to change a theme, at this time stage hasnt been changed yet.
UPDATE: Without jQuery:)
UPDATE:
Example problem with using one image:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<script>
document.onreadystatechange = function(){
console.log(document.readyState);
};
function checkDocumentState(){
console.log(document.readyState);
return setTimeout(function(){
checkDocumentState();
}, 1000);
}
checkDocumentState();
</script>
</head>
<body>
<img src="" onclick="this.setAttribute('src','http://i.imgur.com/uRBtadp.jpg')" style="width:50px; height:50px; background-color:gray; " /> Press empty image and open new image.
</body>
</html>
FOUND ANSWER: How can I tell when a CSS background image has loaded? Is an event fired?
But hopeless .. lack of universality...
CSS is called after DOM elements are populated. This is why in the days of dial up internet, the page would load all funky looking, and then all of a sudden start to develop into the desired page bit by bit. I would suggest using Jquery instead, where you could use the following code to be able to ensure the document is fully loaded and the CSS is already implemented
$document.ready(function() {
//Insert Code here
}
Hope that helps
Answering the question, how to determine the document has loaded after dynamically loading a css file depends upon the different browser vendors out there. There is not a single sure shot way for all the browsers, but lets tackle the problem one by one for each of these browsers.
Preface
var url = "path_to_some_stylesheet.css",
head = document.getElementsByTagName('head')[0];
link = document.createElement('link');
link.type = "text/css";
link.rel = "stylesheet"
link.href = url;
head.appendChild(link);
Once that appending is done:
Internet Explorer : fires readystatechange and load.
Opera : fires load event via onload.
Chrome : Doesnt fire an event but increments document.styesheets.length only after the file has arrived.
Firefox: I was not able to reliably get anything other than mozAfterPaint.
I wrote this code, what i wanted and worked for me:
window.engineLoading = {images_count:0, images_loaded_count:0, fonts_count:0, fonts_loaded_count:0 };
document.querySelector("a").onclick = function(){ // first elemnet a
var before_stylesheets_length = document.styleSheets.length;
var before_fonts_size = document.fonts.size;
document.fonts.onloadingerror = function(a){
window.engineLoading.fonts_loaded_count++;
}
document.fonts.onloading = function(a){
window.engineLoading.fonts_count++;
}
document.fonts.onloadingdone = function(a){
window.engineLoading.fonts_loaded_count++;
}
var head= document.getElementsByTagName('head')[0];
var style= document.createElement('link');
style.rel= 'stylesheet';
style.setAttribute("href","./new_style.css");
style.onload = function(){
for(i=before_stylesheets_length; i<document.styleSheets.length; i++){
var rules = document.styleSheets[i].rules;
for(q=0; q<rules.length; q++){
var styles = rules[q].style;
for(s=0; s<styles.length; s++){
console.log(styles[s]);
if((styles[s] == "background-image" || styles[s] == "background") && styles.backgroundImage.length > 0){
window.engineLoading.images_count++;
var body= document.getElementsByTagName('body')[0];
var image = document.createElement('img');
var url = styles.backgroundImage;
url = url.replace(/^url\(["']?/, '').replace(/["']?\)$/, '');
image.src = url;
image.width = 0;
image.height = 0;
image.setAttribute("class","pace-load-style");
image.onload = function(e){
console.log(e);
window.engineLoading.images_loaded_count++;
};
image.onerror = function(e){
window.engineLoading.images_laoded_count++;
}
body.appendChild(image);
break;
}
}
}
}
};
style.onerror = function(){};
head.appendChild(style);
setTimeout(function(){
checkCurrentState();
}, 1000);
return false;
};
function checkCurrentState(){
if(window.engineLoading.images_count == window.engineLoading.images_loaded_count && window.engineLoading.fonts_count == window.engineLoading.fonts_loaded_count){
console.log("loaded"); return true;
}console.log("still loading...");
return setTimeout(function(){
checkCurrentState();
}, 1000);
};
UPDATE: Scipt has bug on localfile because of empty rule. CSSRules is empty I don't worry about it , and no need fix it.
UPDATE: Mozilla Firefox hasnt reference document.fonts.

Correct access of dynamic iFrame in IE

Plain JS - please no jQuery suggestions - it is for a bookmarklet that needs to use as plain JS as possible.
I hope someone KNOWS the answer since I cannot reliable create a fiddle.
This code will run in the scope of the page it is inserted in - it works perfectly in Fx6-9, safari and latest Chromes on Windows XP and OSX - Only IE gives me undefined when I try to access the iFrame
var zContainer = document.getElementById('zContainer');
if (zContainer==null) {
zContainer = document.createElement("div");
zContainer.id="zContainer";
document.body.appendChild(zContainer);
}
var zStuff = {}; // minimise window var footprint
zStuff.html = '<body>Hello</body>';
if (!zFrame) { // did we already have one?
zFrame = document.createElement("iframe");
zFrame.id="zIframeId"; zFrame.name="zIframeName"; zFrame.frameBorder="0";
zIFS = zFrame.style; zIFS.border="0"; zIFS.width="500px"; zIFS.height="500px"; zIFS.backgroundColor="white"; zIFS.display="block";
zContainer.appendChild(zFrame); // append to div
zFrame = window.frames["zIframeName"]; // undefined in IE8 !!!!!
// zFrame = document.getElementById("zIframeId"); // undefined in IE8 !!!!!
zFrame.src="javascript:'<body></body>'"; // initialise body
zFrame.document.write(zStuff.html); // or zFrame.contentDocument.write
zFrame.document.close();
// zFrame.document.body.innerHTML=zStuff.html; // also does not work
// zFrame.src="javascript:'"+zStuff.html+"'"; // alternative method - either one works in Fx/Chrome
}
Thanks for any hints and for not voting this down. I hope the SO community will be as
helpful to me as I have been to it over the last year and a half...
Update - since the code I posted had some remnants of desperation, I changed it to
if (!zFrame) { // did we already have one?
zFrame = document.createElement("iframe");
zFrame.id="zIframeId"; zFrame.name="zIframeName"; zFrame.frameBorder="0";
zIFS = zFrame.style; zIFS.border="0"; zIFS.width="500px"; zIFS.height="500px"; zIFS.backgroundColor="white"; zIFS.display="block";
zContainer.appendChild(zFrame); // append to div
zFrame.src="javascript:'<body></body>'"; // initialise body
zFrame.document.write(zStuff.html); // or zFrame.contentDocument.write
zFrame.document.close();
}
the above now replaces the page I am on with the code in the zStuff.html
instead of replacing only the iFrame content - it also broke in Fx
Now I have to do this in Fx which IE also does not mind but still replaces the window and not the iFrame
if (!zFrame) {
zFrame = document.createElement("iframe");
zFrame.scrolling="no"; zFrame.id="zIframeId"; zFrame.name="zIframeName"; zFrame.frameBorder="0";
zIFS = zFrame.style; zIFS.border="0px none"; zIFS.width="549px"; zIFS.height="510px"; zIFS.backgroundColor="white"; zIFS.display="block";
zDRContainer.appendChild(zFrame);
zFrame.src="javascript:'<body></body>'";
setTimeout(function() {
var zFrame = window.frames["zIframeName"]; // this is needed for the document.write
zFrame.document.write(zStuff.html);
zFrame.document.close();
},100);
}
The hack I suggested in chat:
var a = setInterval( function(){
try{
zFrame.contentWindow.document.write(zStuff.html);
zFrame.contentWindow.document.close();
clearInterval(a);
}
catch(e){}
}, 10 );
Since it is not known when IE allows accessing contentWindow properties, this will keep trying until it is allowed.

Reading Parent's URL

I have an iFrame that is running some Javascript and I want the iFrame to behave differently depending on which page it is loaded into. I found this code which works brilliantly but it shows me the url of the iFrame not the parent.
var Page1 = "page1.html";
var Page2 = "page2.html";
var thisUrl = decodeURI(window.location);
var urlChunks = thisUrl.split("/");
for (var chunk in urlChunks) {
alert('chunk: ' + chunk);
alert('urlChunks[chunk]: ' + urlChunks[chunk]);
if (urlChunks[chunk] == Page1) {
alert('inside index.html');
}
else if (urlChunks[chunk] == Page2) {
}
else
{
}
}
What can I change the
decodeURI(window.location);
to in order to get it to read from the parent.
window.parent.location
Remember that JavaScript has the Same-Origin restriction, so when the parent document is a different origin (eg. domain), you will most probably get an access-denied exception.

link element onload

Is there anyway to listen to the onload event for a <link> element?
F.ex:
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'styles.css';
link.onload = link.onreadystatechange = function(e) {
console.log(e);
};
This works for <script> elements, but not <link>. Is there another way?
I just need to know when the styles in the external stylesheet has applied to the DOM.
Update:
Would it be an idea to inject a hidden <iframe>, add the <link> to the head and listen for the window.onload event in the iframe? It should trigger when the css is loaded, but it might not guarantee that it's loaded in the top window...
Today, all modern browsers support the onload event on link tags. So I would guard hacks, such as creating an img element and setting the onerror:
if !('onload' in document.createElement('link')) {
imgTag = document.createElement(img);
imgTag.onerror = function() {};
imgTag.src = ...;
}
This should provide a workaround for FF-8 and earlier and old Safari & Chrome versions.
minor update:
As Michael pointed out, there are some browser exceptions for which we always want to apply the hack. In Coffeescript:
isSafari5: ->
!!navigator.userAgent.match(' Safari/') &&
!navigator.userAgent.match(' Chrom') &&
!!navigator.userAgent.match(' Version/5.')
# Webkit: 535.23 and above supports onload on link tags.
isWebkitNoOnloadSupport: ->
[supportedMajor, supportedMinor] = [535, 23]
if (match = navigator.userAgent.match(/\ AppleWebKit\/(\d+)\.(\d+)/))
match.shift()
[major, minor] = [+match[0], +match[1]]
major < supportedMajor || major == supportedMajor && minor < supportedMinor
This is kind of a hack, but if you can edit the CSS, you could add a special style (with no visible effect) that you can listen for using the technique in this post: http://www.west-wind.com/weblog/posts/478985.aspx
You would need an element in the page that has a class or an id that the CSS will affect. When your code detects that its style has changed, the CSS has been loaded.
A hack, as I said :)
The way I did it on Chrome (not tested on other browsers) is to load the CSS using an Image object and catching its onerror event. The thing is that browser does not know is this resource an image or not, so it will try fetching it anyway. However, since it is not an actual image it will trigger onerror handlers.
var css = new Image();
css.onerror = function() {
// method body
}
// Set the url of the CSS. In link case, link.href
// This will make the browser try to fetch the resource.
css.src = url_of_the_css;
Note that if the resource has already been fetched, this fetch request will hit the cache.
E.g. Android browser doesn't support "onload" / "onreadystatechange" events for element: http://pieisgood.org/test/script-link-events/
But it returns:
"onload" in link === true
So, my solution is to detect Android browser from userAgent and then wait for some special css rule in your stylesheet (e.g., reset for "body" margins).
If it's not Android browser and it supports "onload" event- we will use it:
var userAgent = navigator.userAgent,
iChromeBrowser = /CriOS|Chrome/.test(userAgent),
isAndroidBrowser = /Mozilla\/5.0/.test(userAgent) && /Android/.test(userAgent) && /AppleWebKit/.test(userAgent) && !iChromeBrowser;
addCssLink('PATH/NAME.css', function(){
console.log('css is loaded');
});
function addCssLink(href, onload) {
var css = document.createElement("link");
css.setAttribute("rel", "stylesheet");
css.setAttribute("type", "text/css");
css.setAttribute("href", href);
document.head.appendChild(css);
if (onload) {
if (isAndroidBrowser || !("onload" in css)) {
waitForCss({
success: onload
});
} else {
css.onload = onload;
}
}
}
// We will check for css reset for "body" element- if success-> than css is loaded
function waitForCss(params) {
var maxWaitTime = 1000,
stepTime = 50,
alreadyWaitedTime = 0;
function nextStep() {
var startTime = +new Date(),
endTime;
setTimeout(function () {
endTime = +new Date();
alreadyWaitedTime += (endTime - startTime);
if (alreadyWaitedTime >= maxWaitTime) {
params.fail && params.fail();
} else {
// check for style- if no- revoke timer
if (window.getComputedStyle(document.body).marginTop === '0px') {
params.success();
} else {
nextStep();
}
}
}, stepTime);
}
nextStep();
}
Demo: http://codepen.io/malyw/pen/AuCtH
Since you didn't like my hack :) I looked around for some other way and found one by brothercake.
Basically, what is suggested is to get the CSS using AJAX to make the browser cache it and then treat the link load as instantaneous, since the CSS is cached. This will probably not work every single time (since some browsers may have cache turned off, for example), but almost always.
Another way to do this is to check how many style sheets are loaded. For instance:
With "css_filename" the url or filename of the css file, and "callback" a callback function when the css is loaded:
var style_sheets_count=document.styleSheets.length;
var css = document.createElement('link');
css.setAttribute('rel', 'stylesheet');
css.setAttribute('type', 'text/css');
css.setAttribute('href', css_filename);
document.getElementsByTagName('head').item(0).appendChild(css);
include_javascript_wait_for_css(style_sheets_count, callback, new Date().getTime());
function include_javascript_wait_for_css(style_sheets_count, callback, starttime)
/* Wait some time for a style sheet to load. If the time expires or we succeed
* in loading it, call a callback function.
* Enter: style_sheet_count: the original number of style sheets in the
* document. If this changes, we think we finished
* loading the style sheet.
* callback: a function to call when we finish loading.
* starttime: epoch when we started. Used for a timeout. 12/7/11-DWM */
{
var timeout = 10000; // 10 seconds
if (document.styleSheets.length!=style_sheets_count || (new Date().getTime())-starttime>timeout)
callback();
else
window.setTimeout(function(){include_javascript_wait_for_css(style_sheets_count, callback, starttime)}, 50);
}
This trick is borrowed from the xLazyLoader jQuery plugin:
var count = 0;
(function(){
try {
link.sheet.cssRules;
} catch (e) {
if(count++ < 100)
cssTimeout = setTimeout(arguments.callee, 20);
else
console.log('load failed (FF)');
return;
};
if(link.sheet.cssRules && link.sheet.cssRules.length == 0) // fail in chrome?
console.log('load failed (Webkit)');
else
console.log('loaded');
})();
Tested and working locally in FF (3.6.3) and Chrome (linux - 6.0.408.1 dev)
Demo here (note that this won't work for cross-site css loading, as is done in the demo, under FF)
You either need a specific element which style you know, or if you control the CSS file, you can insert a dummy element for this purpose. This code will exactly make your callback run when the css file's content is applied to the DOM.
// dummy element in the html
<div id="cssloaded"></div>
// dummy element in the css
#cssloaded { height:1px; }
// event handler function
function cssOnload(id, callback) {
setTimeout(function listener(){
var el = document.getElementById(id),
comp = el.currentStyle || getComputedStyle(el, null);
if ( comp.height === "1px" )
callback();
else
setTimeout(listener, 50);
}, 50)
}
// attach an onload handler
cssOnload("cssloaded", function(){
alert("ok");
});
If you use this code in the bottom of the document, you can move the el and comp variables outside of the timer in order to get the element once. But if you want to attach the handler somewhere up in the document (like the head), you should leave the code as is.
Note: tested on FF 3+, IE 5.5+, Chrome
The xLazyLoader plugin fails since the cssRules properties are hidden for stylesheets that belong to other domains (breaks the same origin policy). So what you have to do is compare the ownerNode and owningElements.
Here is a thorough explanation of what todo:
http://yearofmoo.com/2011/03/cross-browser-stylesheet-preloading/
// this work in IE 10, 11 and Safari/Chrome/Firefox/Edge
// if you want to use Promise in an non-es6 browser, add an ES6 poly-fill (or rewrite to use a callback)
let fetchStyle = function(url) {
return new Promise((resolve, reject) => {
let link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.onload = resolve;
link.href = url;
let headScript = document.querySelector('script');
headScript.parentNode.insertBefore(link, headScript);
});
};
This is a cross-browser solution
// Your css loader
var d = document,
css = d.head.appendChild(d.createElement('link'))
css.rel = 'stylesheet';
css.type = 'text/css';
css.href = "https://unpkg.com/tachyons#4.10.0/css/tachyons.css"
// Add this
if (typeof s.onload != 'undefined') s.onload = myFun;
} else {
var img = d.createElement("img");
img.onerror = function() {
myFun();
d.body.removeChild(img);
}
d.body.appendChild(img);
img.src = src;
}
function myFun() {
/* ..... PUT YOUR CODE HERE ..... */
}
The answer is based on this link that say:
What happens behind the scenes is that the browser tries to load the
CSS in the img element and, because a stylesheet is not a type of
image, the img element throws the onerror event and executes our
function. Thankfully, browsers load the entire CSS file before
determining its not an image and firing the onerror event.
In modern browsers you can do css.onload and add that code as a fallback to cover old browsers back to 2011 when only Opera and Internet Explorer supported the onload event and onreadystatechange respectively.
Note: I have answered here too and it is my duplicate and deserves to be punished for my honesteness :P

Categories

Resources