Search code examples
javascriptcodeigniterasynchronous

Lazy loading JavaScript and Inline JavaScript


I noticed in the <head> of my site (for work), there are a lot of <link rel="stylesheet" type="text/css" href="" /> and <script type="text/javascript" src=""> tags. There are even more JavaScript/CSS files that are only loaded for specific pages (we're using CodeIgniter, and the file paths are passed to the header view).

I was considering using a conditional/asynchronous loader (eg. yepnope.js, head.js, etc.), but I noticed a small problem with doing this.

In our views, there is inline JavaScript, some uses $(function(){}) some uses $(document).ready(function(){}), and some just has code (using jQuery) that's not in a ready block.

Without editing EVERY view file to wrap its code in a function and calling that when the JS files are loaded, is there a way to delay the inline code until the JavaScript is asynchronously loaded?


Solution

  • You can actually lazyload inline javascript: 1- Change the type parameter in the inline script to: text/delayscript

    FROM

        <!– Inline Script –>
    <script type="text/javascript" language="javaScript">
                 /* Code */
    </script>
    

    To

        <!– Inline Script –>
    <script type="text/delayscript">
                 /* Code */
    </script>
    

    Giving the script tag a custom Mime type text/delayscript forces the browser to ignore its content (Please note that leaving it out entirely will default to text/javascript).

    2- Lazy load all inline scripts Once heads.js (Or an other framework you might be using) confirms that it lazy loaded all your external JS, you can then grab the content of all your custom script tags and inject them in the page:

    <script>
    head.ready(function() {
        var 
            _head = document.getElementsByTagName("head")[0],
            _script = document.createElement('script'),
            _scripts = document.getElementsByTagName("script"),
            _txt = "text/delayscript",
            _contents = []
        ;
    
        for(var i=0,l=_scripts.length;i<l;i++){
            var _type = _scripts[i].getAttribute("type");
                if(_type && _type.toLowerCase() ==_txt)
                    _contents.push(_scripts[i].innerHTML)
        }
    
    
        _script.type = 'text/javascript';
        _script.innerHTML = _contents.join(" ");
        _head.appendChild(_script);
    
    });
    

    To be even more graceful, you can actually keep the inline scripts in their original hierarchy in the DOM tree instead of jamming all their content in one script, as I have suggested above, by replacing the marked inline script tag by a new one that has mime type text/javascript:

    head.ready(function() {
    var 
        _scripts = document.getElementsByTagName("script"),
        _doc = document,
        _txt = "text/delayscript"
    ;
    
    for(var i=0,l=_scripts.length;i<l;i++){
        var _type = _scripts[i].getAttribute("type");
            if(_type && _type.toLowerCase() ==_txt)
                _scripts[i].parentNode.replaceChild((function(sB){
                    var _s = _doc.createElement('script');
                    _s.type = 'text/javascript';
                    _s.innerHTML = sB.innerHTML;
    
                    return _s;
                })(_scripts[i]), _scripts[i]);
    }
    });