javascriptcssyepnope

Using yepnope, how can I know if a css resource was loaded successfully?


I'm developping a webapp for internal use in a company with a strict (and evolving) firewall.

I'm using yepnope.js / Modernizr.load to load js and css files from CDN with fallbacks.

But how do I know if a css was loaded successfully ? Is there a way to test successful loading and activation of a css ?


Solution

  • Thanks to the hint from @Morpheus, here is a solution :

    First, in order to not depend on anything (jQuery, etc.) we need to define 2 functions :

    from http://robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/

    function getStyle(oElm, strCssRule) {
        var strValue = "";
        if(document.defaultView && document.defaultView.getComputedStyle){
            strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
        }
        else if(oElm.currentStyle){
            strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
                return p1.toUpperCase();
            });
            strValue = oElm.currentStyle[strCssRule];
        }
        return strValue;
    },
    

    from https://stackoverflow.com/a/8122005/587407

    function test_css(element_name, element_classes, expected_styles, rsrc_name, callback) {
      // create an element of given type
      var elem = document.createElement(element_name);
      // immediately hide it
      elem.style.display = 'none';
      // give it the class(es)
      for (var i = 0; i < element_classes.length; i++) {
        elem.className = elem.className + " " + element_classes[i];
      }
      // and add it to the DOM
      document.body.appendChild(elem);
    
      // test the given properties
      // it happens (~1/3 times in local)
      // that the css takes a few ms to apply.
      // so we need a function that we can try directly first
      // and retry again later in case of failure
      var handle = -1;
      var try_count = 0;
      var test_function = function() {
        var match = true; // so far
        try_count++;
        for (var key in expected_styles) {
          console.log("[CSS loader] testing " + rsrc_name + " css : " + key + " = '" + expected_styles[key] + "', actual = '" + get_style_of_element(elem, key) + "'");
          if(get_style_of_element(elem, key) === expected_styles[key]) {
            match = match && true;
          } else {
            console.error("[CSS loader] css " + rsrc_name + " test failed (not ready yet ?) : " + key + " = " + expected_styles[key] + ", actual = " + get_style_of_element(elem, key) );
            match = false;
          }
        }
    
        if (match === true || try_count >= 3) {
          if (handle >= 0)
            window.clearTimeout(handle);
          // remove our test element from the DOM
          document.body.removeChild(elem);
          if (!match)
            console.error("[CSS loader] giving up on css " + rsrc_name + "..." );
          else
            console.log("[CSS loader] css " + rsrc_name + " load success !" );
          callback(rsrc_name, match);
        }
        return match;
      }
    
      // use and program the function
      if(! test_function() ) {
        console.info("" + rsrc_name + " css test failed, programming a retry...");
        handle = window.setInterval(test_function, 100);
      }
    }
    

    Note that the previous function is tricky because of the delayed loading of css.

    We can now use yepnope like this :

    {
      load: { 'bootstrap-css': 'http//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css' },
      callback: function (url, result, key) {
      // here for bootstrap, I test presence of .span1 class
      test_css('span', [ 'span1' ], { 'width': '60px' }, function(match) {
        if( match )  {
          // it worked !
        }
        else {
          // ... fallback on another CDN or local rsrc...
        }
      }
    }