Search code examples
javascriptangularjsinternet-explorer-8mootools

mootools 1.4.2 and angular 1.3 playing nicely together in ie8


I have a shimmed and polyfilled version of angularjs 1.3 working perfectly on ie8. Unfortunately when mootools is included on the page there are quite a few conflicts. I have managed to get a handle on all but one with the following which adds add / remove EventListener and dispatchEvent to Window.prototype, HTMLDocument.prototype and Element.prototype. It checks to see if mootools is loaded and if so it adds them differently.

!window.addEventListener && (function (WindowPrototype, DocumentPrototype, ElementPrototype, addEventListener, removeEventListener, dispatchEvent, registry) {
        var addEventListenerFn = function(type, listener) {
            var target = this;
            registry.unshift([target, type, listener,
                function (event) {
                    event.currentTarget = target;
                    event.preventDefault = function () {
                        event.returnValue = false;
                    };
                    event.stopPropagation = function () {
                        event.cancelBubble = true;
                    };
                    event.target = event.srcElement || target;
                    listener.call(target, event);
                }]);

            // http://msdn.microsoft.com/en-us/library/ie/hh180173%28v=vs.85%29.aspx
            if (type === 'load' && this.tagName && this.tagName === 'SCRIPT') {
                var reg = registry[0][3];
                this.onreadystatechange = function (event) {
                    if (this.readyState === "loaded" || this.readyState === "complete") {
                        reg.call(this, {
                            type: "load"
                        });
                    }
                }
            } else {
                this.attachEvent('on' + type, registry[0][3]);
            }
        };

        var removeEventListenerFn = function(type, listener) {
            for (var index = 0, register; register = registry[index]; ++index) {
                if (register[0] == this && register[1] == type && register[2] == listener) {
                    if (type === 'load' && this.tagName && this.tagName === 'SCRIPT') {
                        this.onreadystatechange = null;
                    }

                    return this.detachEvent('on' + type, registry.splice(index, 1)[0][3]);
                }
            }
        };

        var dispatchEventFn = function(eventObject) {
            return this.fireEvent('on' + eventObject.type, eventObject);
        };

        if(Element.prototype.$constructor && typeof Element.prototype.$constructor === 'function') {
            Element.implement(addEventListener, addEventListenerFn);
            Element.implement(removeEventListener, removeEventListenerFn);
            Element.implement(dispatchEvent, dispatchEventFn);
            Window.implement(addEventListener, addEventListenerFn);
            Window.implement(removeEventListener, removeEventListenerFn);
            Window.implement(dispatchEvent, dispatchEventFn);
        } else {
            WindowPrototype[addEventListener] = ElementPrototype[addEventListener] = addEventListenerFn;
            WindowPrototype[removeEventListener] = ElementPrototype[removeEventListener] = removeEventListenerFn;
            WindowPrototype[dispatchEvent] = ElementPrototype[dispatchEvent] = dispatchEventFn;
        }
        DocumentPrototype[addEventListener] = addEventListenerFn;
        DocumentPrototype[removeEventListener] = removeEventListenerFn;
        DocumentPrototype[dispatchEvent] = dispatchEventFn;
    })(Window.prototype, HTMLDocument.prototype, Element.prototype, 'addEventListener', 'removeEventListener', 'dispatchEvent', []);

This has resolved all my errors bar one. When this function is called in Angular, when mootools is on the page, and element is a form addEventListener is undefined.

addEventListenerFn = function(element, type, fn) {
      element.addEventListener(type, fn, false);
    }

specifically this function is called from angulars formDirective like so

addEventListenerFn(formElement[0], 'submit', handleFormSubmission);

Any ideas why the form element still dosn't have the addEventListener function available?


Solution

  • Extending native type via Element.prototype in IE8 is considered very unreliable as the prototype is only partially exposed and certain things are not inheriting from it / misbehave.

    http://perfectionkills.com/whats-wrong-with-extending-the-dom/

    What MooTools does in this case is rather than work around the quirks of all edgecases that don't adhere to the correct proto chain (and because of IE6/7 before that) is to COPY the Element prototypes on the objects of the DOM nodes as you pass it through the $ selector.

    This is not ideal, because.

    var foo = document.id('foo'); 
    // all known methods from Element.prototype are copied on foo, which now hasOwnProperty for them
    Element.prototype.bar = function(){};
    foo.bar(); // no own property bar, going up the chain may fail dependent on nodeType
    

    Anyway, that aside - you can fix your particular problem by copying your methods from the Element.prototype to the special HTMLFormElement.prototype - any any other elements constructors you may find to differ.

    It's not scalable, you may then get an error in say HTMLInputElement and so forth, where do you draw the line?