Search code examples
cssbrowsercss-specificitycss-cascade

What determines the order CSS is "specified" between two elements when loaded from different files?


I'm helping to debug a CSS issue where two identical selectors are loading in a different order between two identically configured servers. One rule gets loaded from a stylesheet defined in the page and another gets loaded by Javascript injecting the stylesheet file.

According to the cascade rules, as I read them, it should come down to the order in which the rules are specified. It appears as though the issue is a race condition, but it's not clear what the basis for the race condition is. In Chrome's network tab, the files are listed in the same order between the two servers; however, when you drill down to the element level in the Elements tab, the rule taking precedence on a given server is listed first.

What determines the order in which the CSS is "specified" between two elements when loaded like this?


Solution

  • If the selectors are identical, it comes down to order... order within the CSS and the DOM. So, the question is, where did you place the dynamically generated <link> (or <style>) in relation to the original <link> (or <style>)? The order within the DOM matters.

    If you'd like to make sure that your dynamically created CSS gets evaluated first, add it to the top of the <head>:

    document.head.insertBefore(myLink, document.head.firstChild);
    

    If you'd like it to be evaluated last, append it to the <body>:

    document.body.appendChild(myLink);
    

    Whichever rule is evaluated last will be the one applied (barring use of !important)

    Here's an example:

    <!DOCTYPE html>
    <html>
      <head>
        <style>
          p {
            color: green;
          }
        </style>
      </head>
      <body>
        <p>test</p>  
        <script>
          (function() {
    
            var red = document.createElement("style");
            red.textContent = "p { color: red }";
            // We'll add this to the top of head. You'll never even notice because it will
            // be overridden by the existing green style.
            document.head.insertBefore(red, document.head.firstChild);
    
            var blue = document.createElement("style");
            blue.textContent = "p { color: blue }";
            // We'll add this to the end of the body after a three second delay. It will 
            // override the other styles.
            setTimeout(function() {
              document.body.appendChild(blue);
            }, 3000);
    
          })();
        </script>
      </body>
    </html>