11

I have a script that detects a button click on which it will attach a CSS stylesheet to the "head" with jQuery like so:

const link = "<link class='listcss' rel='stylesheet' href='http://....list.css'>";
$("head").append(link);

Then I need to do some calculations based on the css width and height properties:

function CSSDone(){
            if($(window).width()>640){
                $(".grid-item").css({"height":$(".grid-item").width()*0.2});
                $(".grid_item_img").css({"height":$(".grid-item").width()*0.2});
            console.log("a");
            }else{
                $(".grid-item").css({"height":$(".grid-item").width()*0.33});
                $(".grid_item_img").css({"height":$(".grid-item").width()*0.33});
            console.log("b");       
            }
        }
        CSSDone();

However, if I launch CSSDone(); right after adding the stylesheet, the calculations happen before the css is loaded. I searched the web like crazy but everything I tried doesn't work:

I tried these options:

  1. Does not work:

    link.onload = function () { CSSDone(); }

  2. Does not work:

     if (link.addEventListener) {
       link.addEventListener('load', function() {
         CSSDone();
       }, false);
     }
    
  3. Does not work:

     link.onreadystatechange = function() {
       var state = link.readyState;
       if (state === 'loaded' || state === 'complete') {
         link.onreadystatechange = null;
         CSSDone();
       }
     };
    
  4. Does not work:

     $(window).load(function () {
         CSSDone();
     });
    
  5. Does not work:

     $(window).bind("load", function() {
         CSSDone();
     });
    

Believe me. Nothing works......


**One workaround:**

I load the css in the DOM and immidiately remove it again with jQuery. That way when adding the link to the head later it's fast enough.

mesqueeb
  • 5,277
  • 5
  • 44
  • 77
  • Possible duplicate of [change css link and wait till new css is loaded](https://stackoverflow.com/questions/12272248/change-css-link-and-wait-till-new-css-is-loaded). Voting to close as dupe, not because this is a bad question but in the interest of consolidating information. – James McMahon Aug 30 '19 at 20:18
  • I would rather vote to close the other question instead of this one. – mesqueeb Aug 30 '19 at 22:23

4 Answers4

27

You need to insert a link node in the head and then attach onload event to that. In your code link is a String. See sample code below on how to implement what you want.

var link = document.createElement('link');
link.setAttribute("rel", "stylesheet");
link.setAttribute("type", "text/css");
link.onload = CSSDone;
link.setAttribute("href", 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css');
document.getElementsByTagName("head")[0].appendChild(link);

Check jsfiddle.

MDN reference on Link element in case you are curious

TeaCoder
  • 1,958
  • 13
  • 13
2

Using jQuery:

var head = $('head');
var link = $('<link>')
    .attr('rel', 'stylesheet')
    .attr('type', 'text/css')
    .attr('href', url)
                        .on('load', callback)
                        .on('error', error)
                       .appendTo(head);

2

To get around race conditions when inserting a style sheet link dynamically and then relying on those calculations:

function downloadCSS(url) {
    function insertCss(code) {
        var style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = code;
        document.querySelector("head").appendChild(style);
    }

    return new Promise(async (resolve, reject) => {
        try {
            let src = await axios.get(url);
            insertCss(src.data);
        } catch (e) {
            return reject(e);
        }

        requestAnimationFrame(function () {
            resolve();
        });
    });
}

(async function setup() {
    try {
        await downloadCSS('https://myapp.com/style.css');        
    } catch (e) {
        return reject(e);
    }
    
    //rest of code
})();

I'm using the axios library for HTTP requests, but you can use anything you like.

The idea is that you await the downloadCSS promise which downloads the url of your CSS, then inserts the code as a style as opposed to link element, then on the next frame resolves the promise and continues from there.

egekhter
  • 2,067
  • 2
  • 23
  • 32
1

You can try this i believe.. include the CSSDone() code in $("head").append(link)'s callback

$(document).ready(function(){
$('.btn').on('click', function(){
link = "<link class='listcss' rel='stylesheet' href='/list.css'>";
        $("head").append(link, function(){
        if($(window).width()>640){
                $(".grid-item").css({"height":$(".grid-item").width()*0.2});
                $(".grid_item_img").css({"height":$(".grid-item").width()*0.2});
            console.log("a");
            }else{
                $(".grid-item").css({"height":$(".grid-item").width()*0.33});
                $(".grid_item_img").css({"height":$(".grid-item").width()*0.33});
            console.log("b");       
            }

        }); // append link

        }); // button click
}); // document ready
RRR
  • 1,074
  • 8
  • 11