Lazy loading excanvas.js

excanvas.js is a script that enables developers to use the canvas element in Internet Explorer; since IE doesn’t support canvas or the canvas API, the excanvas script converts (most) canvas commands to Vector Markup Language (VML), the only vector rendering language supported natively in Internet Explorer.

excanvas is designed specifically for Internet Explorer, so most people simply use a conditional comment to load it in IE and avoid loading it in other browsers:


<!--[if IE]>
<script type="text/javascript" src="/scripts/excanvas.js" src="/scripts/excanvas.js">
<![endif]-->

This works fine if you have access to the HTML file that needs to load excanvas, but what if you can’t edit the HTML? This was my predicament with a recent project; I decided to use a lazy loader approach (on-demand loading) and load excanvas.js dynamically.

I started by developing an HTML example page that used the canvas element and had the excanvas.js file hard-coded. Everything worked as planned. I then took out the hard-coded excanvas.js file and replaced it with a JavaScript-based lazy loader. Guess what? It didn’t work.

I scratched my head for a while and did some more testing. The strange thing was that excanvas.js was loading when and where it was supposed to, but the script itself wasn’t firing.

After digging around the excanvas.js source code a bit, I found the problem: the script contains an init function that only gets invoked when the document’s readystate changes. If the document is already loaded, the readystate won’t change and the init function will never fire!

A simple modification to the excanvas.js file fixed the problem:

Original code (starting at line 87 of excanvas.js)


init: function(opt_doc) {
    if (/MSIE/.test(navigator.userAgent) && !window.opera) {
        var doc = opt_doc || document;
        // Create a dummy element so that IE will allow canvas elements to be
        // recognized.
        doc.createElement('canvas');
        doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
    }
},

Modified version


init: function(opt_doc) {
    if (/MSIE/.test(navigator.userAgent) && !window.opera) {
        var doc = opt_doc || document;
        // Create a dummy element so that IE will allow canvas elements to be
        // recognized.
        doc.createElement('canvas');

        if(doc.readyState !== "complete"){

            doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));

        } else {

           this.init_(doc);

        }

    }
},

Basically all we’re doing is checking to see if the readystate is already “completed” before attempting to do attachEvent. If the state is completed, we don’t need attachEvent and can just invoke this.init_ directly.

Update: Alternate solution

January 2014. Unfortunately, the authors of ExCanvas haven’t addressed this issue yet. Since it has been over 3 years since I submitted an issue on their project site, I assume it will not be addressed. (At this point I can’t blame them, who wants to support old versions of IE anyway?)

However, there is some good news if you still need to use ExCanvas: Willis at badsyntax.co came up with a different solution that doesn’t require modifying the source code. I recommend using his technique, as it’s cleaner and allows you to use a CDN copy of ExCanvas instead of maintaining your own copy.

3 Replies to “Lazy loading excanvas.js”

  1. I have the same problem. So I load dinamically the excanvas.js, then create a canvas element, init that element with G_vmlCanvasManager.initElement(element) and draw on it, and it works.

    But there’s a problem. If the document already contains a canvas element and you’re trying to load excanvas when the document is fully loaded, you’re getting a Unknown error in js. So your method works if there’s no canvas element in the initial page…

  2. Good job on finding this, I’m using it to lazyload excanvas for use with highcharts, and your fix works great! Thanks!

Comments are closed.