Example: querySelector polyfill for very old browsers
// querySelector polyfill (basic - uses Sizzle or native methods)if (!document.querySelector) { document.querySelector = function(selector) { // Simple ID selector if (selector.charAt(0) === '#') { return document.getElementById(selector.slice(1)); } // Simple class selector if (selector.charAt(0) === '.') { var elements = document.getElementsByClassName ? document.getElementsByClassName(selector.slice(1)) : document.getElementsByTagName('*'); return elements[0] || null; } // Simple tag selector var elements = document.getElementsByTagName(selector); return elements[0] || null; };}// querySelectorAll polyfill (basic)if (!document.querySelectorAll) { document.querySelectorAll = function(selector) { var style = document.createElement('style'); var elements = []; var element; document.documentElement.firstChild.appendChild(style); document._qsa = []; style.styleSheet.cssText = selector + '{x-qsa:expression(document._qsa && document._qsa.push(this))}'; window.scrollBy(0, 0); while (document._qsa.length) { element = document._qsa.shift(); element.style.removeAttribute('x-qsa'); elements.push(element); } document.documentElement.firstChild.removeChild(style); return elements; };}// Modern approach: Use Sizzle library for complex selectors// https://github.com/jquery/sizzle
Example: getElementsByClassName polyfill for IE8
// getElementsByClassName polyfillif (!document.getElementsByClassName) { document.getElementsByClassName = function(className) { return document.querySelectorAll('.' + className); }; Element.prototype.getElementsByClassName = function(className) { return this.querySelectorAll('.' + className); };}// Alternative implementation without querySelectorAllif (!document.getElementsByClassName) { document.getElementsByClassName = function(className) { var elements = document.getElementsByTagName('*'); var result = []; var pattern = new RegExp('(^|\\s)' + className + '(\\s|$)'); for (var i = 0; i < elements.length; i++) { if (pattern.test(elements[i].className)) { result.push(elements[i]); } } return result; };}
Note: For complex CSS selector support in legacy browsers, use Sizzle library (jQuery's selector engine) or limit to simple selectors (ID, class,
tag).
2. Element.classList and className Manipulation
Method
Syntax
Description
Returns
classList.add
element.classList.add('class1', 'class2')
Adds one or more classes
undefined
classList.remove
element.classList.remove('class1')
Removes one or more classes
undefined
classList.toggle
element.classList.toggle('class')
Toggles class presence
Boolean - true if added
classList.contains
element.classList.contains('class')
Checks if class exists
Boolean
classList.replace
element.classList.replace('old', 'new')
Replaces one class with another
Boolean - true if replaced
classList.item
element.classList.item(index)
Returns class at index
String or null
Example: classList polyfill for IE9
// classList polyfillif (!('classList' in document.createElement('_'))) { (function(view) { 'use strict'; if (!('Element' in view)) return; var classListProp = 'classList', protoProp = 'prototype', elemCtrProto = view.Element[protoProp], objCtr = Object, strTrim = String[protoProp].trim || function() { return this.replace(/^\s+|\s+$/g, ''); }, arrIndexOf = Array[protoProp].indexOf || function(item) { var i = 0, len = this.length; for (; i < len; i++) { if (i in this && this[i] === item) { return i; } } return -1; }; var DOMTokenList = function(elem) { var classes = strTrim.call(elem.getAttribute('class') || ''); var tokens = classes ? classes.split(/\s+/) : []; var i = 0; var len = tokens.length; for (; i < len; i++) { this.push(tokens[i]); } this._updateClassName = function() { elem.setAttribute('class', this.toString()); }; }; var classListProto = DOMTokenList[protoProp] = []; classListProto.item = function(i) { return this[i] || null; }; classListProto.contains = function(token) { token += ''; return arrIndexOf.call(this, token) !== -1; }; classListProto.add = function() { var tokens = arguments; var i = 0; var len = tokens.length; var token; var updated = false; do { token = tokens[i] + ''; if (arrIndexOf.call(this, token) === -1) { this.push(token); updated = true; } } while (++i < len); if (updated) { this._updateClassName(); } }; classListProto.remove = function() { var tokens = arguments; var i = 0; var len = tokens.length; var token; var updated = false; var index; do { token = tokens[i] + ''; index = arrIndexOf.call(this, token); while (index !== -1) { this.splice(index, 1); updated = true; index = arrIndexOf.call(this, token); } } while (++i < len); if (updated) { this._updateClassName(); } }; classListProto.toggle = function(token, force) { token += ''; var result = this.contains(token); var method = result ? force !== true && 'remove' : force !== false && 'add'; if (method) { this[method](token); } return (force === true || force === false) ? force : !result; }; classListProto.toString = function() { return this.join(' '); }; // Expose as property if (objCtr.defineProperty) { var classListPropDesc = { get: function() { return new DOMTokenList(this); }, enumerable: true, configurable: true }; try { objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); } catch(ex) { // IE8 doesn't support enumerable: true objCtr.defineProperty(elemCtrProto, classListProp, { get: classListPropDesc.get }); } } }(window));}
Note:classList is supported in IE10+. For IE9 and below, this polyfill provides
full API compatibility including multiple class names in add/remove.
3. addEventListener and Event Model Polyfills
Method
Standard
IE Legacy
Key Difference
Add Listener
addEventListener(type, fn, capture)
attachEvent('on'+type, fn)
Event name prefix, this context
Remove Listener
removeEventListener(type, fn, capture)
detachEvent('on'+type, fn)
Must use same function reference
Event Object
Passed as parameter
window.event
Access method differs
Prevent Default
event.preventDefault()
event.returnValue = false
Different property
Stop Propagation
event.stopPropagation()
event.cancelBubble = true
Different property
Event Target
event.target
event.srcElement
Different property name
Example: addEventListener polyfill for IE8
// addEventListener polyfill for IE8if (!Element.prototype.addEventListener) { Element.prototype.addEventListener = function(type, fn, capture) { var element = this; // Create wrapper to fix context and event object var wrapper = function(e) { e = e || window.event; // Fix event object e.target = e.target || e.srcElement; e.currentTarget = element; e.preventDefault = e.preventDefault || function() { e.returnValue = false; }; e.stopPropagation = e.stopPropagation || function() { e.cancelBubble = true; }; // Call with correct context return fn.call(element, e); }; // Store wrapper for removal if (!element._eventListeners) { element._eventListeners = {}; } if (!element._eventListeners[type]) { element._eventListeners[type] = []; } element._eventListeners[type].push({ listener: fn, wrapper: wrapper }); // Attach event element.attachEvent('on' + type, wrapper); }; Element.prototype.removeEventListener = function(type, fn, capture) { if (!this._eventListeners || !this._eventListeners[type]) { return; } var listeners = this._eventListeners[type]; for (var i = 0; i < listeners.length; i++) { if (listeners[i].listener === fn) { this.detachEvent('on' + type, listeners[i].wrapper); listeners.splice(i, 1); break; } } }; // Also add to Window and Document if (!window.addEventListener) { Window.prototype.addEventListener = Element.prototype.addEventListener; Window.prototype.removeEventListener = Element.prototype.removeEventListener; Document.prototype.addEventListener = Element.prototype.addEventListener; Document.prototype.removeEventListener = Element.prototype.removeEventListener; }}// Event object normalizationfunction normalizeEvent(e) { e = e || window.event; if (!e.target) e.target = e.srcElement; if (!e.currentTarget) e.currentTarget = this; if (!e.relatedTarget) e.relatedTarget = e.fromElement || e.toElement; if (!e.which) e.which = e.charCode || e.keyCode; if (!e.preventDefault) { e.preventDefault = function() { e.returnValue = false; }; } if (!e.stopPropagation) { e.stopPropagation = function() { e.cancelBubble = true; }; } return e;}
Warning: IE8's attachEvent executes handlers in reverse
order and with different this context (window instead of element). The polyfill fixes
these issues.