Skip to content

Instantly share code, notes, and snippets.

@ShirtlessKirk
Created October 30, 2015 16:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ShirtlessKirk/c02a458b0e19a3976b9d to your computer and use it in GitHub Desktop.
Save ShirtlessKirk/c02a458b0e19a3976b9d to your computer and use it in GitHub Desktop.
DOM4 methods polyfill
/**
* Polyfill of DOM4 methods
*/
/*global define: false, module: false, console: false */
/*jslint bitwise: true, forin: true */
(function documentModule(global, definition) { // non-exporting module magic dance
'use strict';
var
amd = 'amd',
exports = 'exports'; // keeps the method names for CommonJS / AMD from being compiled to single character variable
if (typeof define === 'function' && define[amd]) {
define(function definer() {
return definition(global);
});
} else if (typeof module === 'function' && module[exports]) {
module[exports] = definition(global);
} else {
definition(global);
}
}(this, function documentPolyfill(global) {
'use strict';
if (!global.Element) {
return;
}
var
console = global.console || {},
document,
documentHead,
elementPrototype,
elementPrototypeFunctions,
forEach = Array.prototype.forEach || function forEach(fn, context) {
var
counter = 0,
object = ({}).valueOf.call(this),
length = object.length >>> 0;
while (counter < length) {
if (counter in object) {
fn.call(context, object[counter], counter, object);
}
counter += 1;
}
},
hasOwnProperty = Object.prototype.hasOwnProperty,
indexOf = Array.prototype.indexOf || function indexOf(search) {
var
cursor = 0,
object = ({}).valueOf.call(this),
length = object.length >>> 0;
while (cursor < length) {
if (cursor in object && object[cursor] === search) {
return cursor;
}
cursor += 1;
}
return -1;
},
keys = Object.keys || function keys(object) {
var
array = [],
key;
for (key in object) {
if (hasOwnProperty.call(object, key)) {
array.push(key);
}
}
return array;
},
unique = 0;
/**
* @private
*/
function castToNode(node) {
return typeof node === 'string' ? document.createTextNode(node) : node;
}
/**
* @private
*/
function createDocumentFragment(nodes) {
var
fragment;
if (nodes.length === 1) {
return castToNode(nodes[0]);
}
fragment = document.createDocumentFragment();
forEach.call(Array.prototype.slice.call(nodes), function appender(value) {
fragment.appendChild(castToNode(value));
});
return fragment;
}
/**
* @constructor
*/
function Elements() {
return this;
}
/**
* @private
*/
function attributeName() {
return 'dom4-element-id-' + String(Math.random()).replace(/\D/g, '');
}
/**
* @private
*/
function scope(selector, supportsScope, details) {
var
selectors = selector.split(/\s*,\s*/),
scopedSelectors = [];
forEach.call(selectors, function scopePusher(value) {
scopedSelectors.push(supportsScope ? ':scoped ' + value : '[' + details.attribute + '="' + details.value + '"] ' + value);
});
return scopedSelectors.join();
}
function after() {
var
documentFragment,
nextSibling,
parentNode = this.parentNode;
if (parentNode) {
documentFragment = createDocumentFragment(arguments);
nextSibling = this.nextSibling;
if (nextSibling) {
parentNode.insertBefore(documentFragment, nextSibling);
} else {
parentNode.appendChild(documentFragment);
}
}
}
function append() {
this.appendChild(createDocumentFragment(arguments));
}
function before() {
if (this.parentNode) {
this.parentNode.insertBefore(createDocumentFragment(arguments), this);
}
}
function closest(selector) {
var
matches = this,
parentNode = matches;
while ((matches && parentNode.matches) && !parentNode.matches(selector)) {
matches = parentNode;
parentNode = parentNode.parentNode;
}
return matches ? parentNode : null;
}
function getElementsByClassName(className) {
return this.querySelectorAll((' ' + className).replace(/ +/g, '.')); // faster than splitting on ' ' and joining with '.'
}
function matches(selector) {
var
parentNode = this.parentNode;
return !!parentNode && indexOf.call(parentNode.querySelectorAll(selector), this) !== -1;
}
function prepend() {
var
documentFragment = createDocumentFragment(arguments),
firstChild = this.firstChild;
if (firstChild) {
this.insertBefore(documentFragment, firstChild);
} else {
this.appendChild(documentFragment);
}
}
function queryAll(selector) {
var
attribute,
element = this === document ? document.documentElement : this,
elements,
result,
scopeSelector,
supportsScope = true;
function toArray(list) {
var
index = 0,
length = list.length,
array = new Array(length);
while (index < length) {
array[index] = list[index];
index += 1;
}
return array;
}
try {
document.createElement('i').querySelector(':scoped *');
} catch (ex) {
supportsScope = false;
}
if (!supportsScope) {
attribute = attributeName();
unique += 1;
element.setAttribute(attribute, unique);
}
scopeSelector = scope(selector, supportsScope, { attribute: attribute, value: unique });
elements = element.querySelectorAll(scopeSelector);
if (!supportsScope) {
element.removeAttribute(attribute);
}
result = new Elements();
result.push.apply(result, toArray(elements));
return result;
}
function query(selector) {
return queryAll.call(this, selector)[0] || null;
}
function remove() {
var
parentNode = this.parentNode;
if (parentNode) {
parentNode.removeChild(this);
}
}
function replaceWith() {
var
parentNode = this.parentNode;
if (parentNode) {
parentNode.replaceChild(createDocumentFragment(arguments), this);
}
}
function replace() {
if (console.warn || console.log) {
console[console.warn ? 'warn' : 'log']('`replace` is deprecated, use `replaceWith` instead');
}
replaceWith.apply(this, arguments);
}
elementPrototype = global.Element.prototype;
elementPrototypeFunctions = {
after: after,
append: append,
before: before,
closest: closest,
getElementsByClassName: getElementsByClassName,
prepend: prepend,
query: query,
queryAll: queryAll,
remove: remove,
replace: replace,
replaceWith: replaceWith
};
forEach.call(keys(elementPrototypeFunctions), function delegate(method) {
if (!hasOwnProperty.call(this, method)) {
this[method] = elementPrototypeFunctions[method];
}
}, elementPrototype);
if (!elementPrototype.matches) {
elementPrototype.matches = elementPrototype.matchesSelector ||
elementPrototype.webkitMatchesSelector ||
elementPrototype.khtmlMatchesSelector ||
elementPrototype.mozMatchesSelector ||
elementPrototype.msMatchesSelector ||
elementPrototype.oMatchesSelector ||
matches;
}
Elements.prototype = [];
forEach.call(['query', 'queryAll'], function delegate(method) {
this[method] = elementPrototypeFunctions[method];
}, Elements.prototype);
document = global.document;
if (!('head' in document)) {
Object.defineProperty(document, 'head', {
get: function get() {
documentHead = documentHead || document.getElementsByTagName('head')[0];
return documentHead;
}
});
document.head = document.getElementsByTagName('head')[0];
}
if (!document.getElementsByClassName) {
document.getElementsByClassName = getElementsByClassName;
}
forEach.call(['Document', 'DocumentFragment', 'document'], function iterator(object) {
if (!(object in global)) {
return;
}
forEach.call(['query', 'queryAll'], function delegate(method) {
if (!this[method]) {
this[method] = elementPrototypeFunctions[method];
}
}, global[object].prototype || global[object]);
});
}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment