Skip to content

Instantly share code, notes, and snippets.

@mwrouse
Last active June 2, 2020 00:55
Show Gist options
  • Save mwrouse/8e6bff2ed20851e2faf8e8f5398097a2 to your computer and use it in GitHub Desktop.
Save mwrouse/8e6bff2ed20851e2faf8e8f5398097a2 to your computer and use it in GitHub Desktop.
JavaScript addEventListener and removeEventListener polyfill for IE6+
/**
* Event polyfill
* Michael Rouse
* December 2016
*/
function polyfillEvent(e) {
e = e || window.event;
e.eventPhase = e.eventPhase || 0;
e.defaultPrevented = e.defaultPrevented || false;
e.bubbles = e.bubbles || true;
e.cancelable = e.cancelable || true;
e.view = e.view || undefined;
e.preventDefault = e.preventDefault || function () { e.returnValue = false; e.defaultPrevented = true; }
e.stopPropagation = e.stopPropagation || function () { e.cancelBubble = true }
return e;
}
/**
* addEventListener and removeEventListener Polyfill IE5+
* Michael Rouse
* December 2016
*/
(function() {
if (window.addEventListener && window.removeEventListener) return; // Polyfill is not needed :)
if (!window.attachEvent || !window.detachEvent) { console.warn("Can't apply polyfill: no attachEvent or detachEvent"); return; }
var IsFunction = function(toCheck) { return typeof toCheck === 'function'; };
var Phases = { None: 0, Capturing: 1, AtTarget: 2, Bubbling: 3 };
/**
* Add Event Listener
*/
function addEventListener(action, callback, capture) {
capture = capture ? true : false;
var self = this;
// Properties that hold the event listeners
self.__listeners = self.__listeners || { };
self.__listeners[action] = self.__listeners[action] || [ ];
// Listener object that will go in the array
var listener = {
fn: callback,
useCapture: capture
};
// Only register the event once
if (self.__listeners[action].length == 0) {
// Create event listener for the first time
self.__listeners['on' + action] = function(e) {
e = polyfillEvent(e);
e.target = e.srcElement || self;
var domList = [];
var node = e.target;
// Get all nodes for propagation of the event
while (node) {
domList.unshift(node);
node = node.parentNode;
}
if (domList[0] !== window) domList.unshift(window);
e.view = domList[0]; // e.view is a reference to the window object where the event was called
// Capturing Phase (Window -> Target)
for (var i = 0; i < domList.length; i++) {
var el = domList[i];
e.eventPhase = (self === el) ? Phases.AtTarget : Phases.Capturing;
e.currentTarget = el;
if (!el.__listeners || !el.__listeners[action]) continue; // No events on this element
// Call all the listeners on this element for this action
for (var j = 0; j < el.__listeners[action].length; j++) {
var listener = el.__listeners[action][j];
if (!listener.useCapture || !IsFunction(listener.fn)) continue; // Ivalid callback or not a capture
// Call the event listener
listener.fn.call(el, e);
// Do not continue if propagation was stopped
if (e.cancelBubble) return;
}
}
// Bubbling Phase (Target -> Window)
for (var i = domList.length - 1; i >= 0; i--) {
var el = domList[i];
e.eventPhase = (self === el) ? Phases.AtTarget : Phases.Bubbling;
e.currentTarget = el;
if (!el.__listeners || !el.__listeners[action]) continue; // No event listeners
// Call all the listeners on this element for this action
for (var j = 0; j < el.__listeners[action].length; j++) {
var listener = el.__listeners[action].length;
if (listener.useCapture || !IsFunction(listener.fn)) continue; // A capture event or invalid callback
// Call the event listener
listener.fn.call(el, e);
// Do not continue if propagation was stopped
if (e.cancelBubble) return;
}
}
e.cancelBubble = true;
};
// Use attachEvent to create the event listener
self.attachEvent('on' + action, self.__listeners['on' + action]);
}
// Add the listener to the polyfill
self.__listeners[action].push(listener);
}
/**
* Remove Event Listener
*/
function removeEventListener(action, callback, capture) {
capture = capture ? true : false;
var self = this;
// Properties that hold the event listeners
self.__listeners = self.__listeners || { };
self.__listeners[action] = self.__listeners[action] || [];
// Find the event listener and remove it
for (var i = 0; i < self.__listeners[action].length; i++) {
if (self.__listeners[action][i].fn == callback && self.__listeners[action][i].useCapture == capture) {
self.__listeners[action].splice(i, 1);
break;
}
}
// If no more events exist, remove the event listener
if (self.__listeners[action].length == 0 && IsFunction(self.__listeners['on' + action])) {
self.detachEvent('on' + action, self.__listeners['on' + action]);
}
}
// Applies the polyfill to an element
function polyfill(el) {
el.addEventListener = addEventListener;
el.removeEventListener = removeEventListener;
return el;
}
// Alters a function on the document
function ReplaceDocFunction(fn) {
var old = document[fn];
document[fn] = function(param) {
return polyfill(old(param));
};
}
// Apply the polyfills
polyfill(window);
polyfill(document);
// Add polyfill to window.Element if possible
if (window.Element && window.Element.prototype) {
polyfill(window.Element.prototype);
} else {
// Apply the polyfill to all of the elements in the document
for(var i = 0; i < document.all.length; i++) {
polyfill(document.all[i]);
}
// Change these important functions to return something that has the event listener functions
ReplaceDocFunction('getElementById');
ReplaceDocFunction('getElementsByTagName');
ReplaceDocFunction('createElement');
}
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment