Hello,

For a web app I am building, I am dynamically loading additional JavaScript and CSS files on demand (after the initial page load). According to the html specs, both the <script> and <link> elements support the onload event. I can get <script> tags to load and fire an onload event, but I am having issues with CSS link elements... Only IE seems to acknowledge a <link>'s onload event (since when does IE follow the rules??).

Here is some example code that works in IE, but fails in other browsers...

var link = document.createElement("link");
  
link.onload = function() {
  alert("yay!");
}

link.type = "text/css";
link.rel = "stylesheet";
link.href = "somefile.css"; // change this to an actual CSS file

document.getElementsByTagName("head")[0].appendChild(link);

Any ideas about how to achieve a cross-browser compatible solution to this? Please don't question the reasons for such an implementation, this example is stripped down to the minimum for example purposes.

Dani AI

Generated

Short answer: the <link> element does have load/error events in the spec, but real-world support used to be inconsistent — so relying on link.onload alone will fail in some browsers. Use a combined strategy: prefer the modern rel="preload"/onload or the media-toggle pattern, and add a polling fallback that watches link.sheet/cssRules. (developer.mozilla.org)

A practical, robust pattern is to try rel="preload" as="style" with an onload that flips rel to stylesheet, and support older browsers with a short polling loop that inspects the stylesheet object. The Filament Group’s loadCSS work documents both the preload/media tricks and an onload/polling helper for when onload is spotty. Here’s a compact helper illustrating the approach:

function loadStylesheet(href, cb, timeoutMs) {
  timeoutMs = timeoutMs || 5000;
  var link = document.createElement('link');
  link.rel = 'preload';
  link.as = 'style';
  link.href = href;

  link.onload = function() {
    link.rel = 'stylesheet';
    link.onload = link.onerror = null;
    if (cb) cb(null, link);
  };
  link.onerror = function(e) { if (cb) cb(e || new Error('CSS failed')); };
  document.head.appendChild(link);

  // polling fallback for browsers without reliable link.onload
  var tries = 0, interval = 50, max = Math.ceil(timeoutMs / interval);
  var id = setInterval(function() {
    var sheet = link.sheet || link.styleSheet;
    if (sheet) {
      try {
        var rules = sheet.cssRules || sheet.rules;
        clearInterval(id);
        if (cb) cb(null, link);
      } catch (err) {
        // SecurityError usually means stylesheet is loaded but cross-origin
        if (err.name === 'SecurityError' || err.code === 18) {
          clearInterval(id);
          if (cb) cb(null, link);
        }
      }
    }
    if (++tries >= max) { clearInterval(id); if (cb) cb(new Error('timeout'), link); }
  }, interval);
}

Polling + try/catch is exactly the tactic pointed toward. Note the caveats: accessing cssRules can throw a SecurityError for cross-origin stylesheets (this is by spec and how browsers enforce same-origin rules), so the try/catch is necessary and commonly used as a signal that the sheet is present but tainted. For production, use a battle‑tested helper like loadCSS or the small onloadCSS helper rather than rolling ad-hoc code. (github.com)

Practical tips: include an onerror and a timeout, provide a <noscript> fallback for critical styles, and avoid relying on reading rules when loading third‑party CSS unless CORS headers permit it.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.