Last active
August 29, 2015 14:22
-
-
Save jonathantneal/446aea88f6d49b665dcc to your computer and use it in GitHub Desktop.
query.js: an IE8+ DOM4 Elements, query, and queryAll polyfill that works without modifying the DOM (CC0 License)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
// cache Array filter | |
var filter = Array.prototype.filter || function (callback) { | |
for (var index = 0, array = []; index < this.length; ++index) { | |
if (callback(this[index], index)) { | |
array.push(this[index]); | |
} | |
} | |
return array; | |
}; | |
// cache Array forEach | |
var forEach = Array.prototype.forEach || function (callback) { | |
for (var index = 0; index < this.length; ++index) { | |
callback(this[index], index); | |
} | |
}; | |
// cache Array push | |
var push = Array.prototype.push; | |
// cache Array sort | |
var sort = Array.prototype.sort; | |
// cache escape character | |
var escapeChar = '\\'; | |
// cache CSS nester characters object | |
var nesters = [{ | |
start: '[', | |
end: ']', | |
depth: 0 | |
}, { | |
start: '(', | |
end: ')', | |
depth: 0 | |
}, { | |
start: '"', | |
end: '"', | |
depth: 0 | |
}, { | |
start: '\'', | |
end: '\'', | |
depth: 0 | |
}]; | |
// split string by separator while honoring | |
function split(string, separator) { | |
var array = []; | |
var index = -1; | |
var lastIndex = 0; | |
var totalDepth = 0; | |
var letter; | |
while (++index < string.length) { | |
letter = string.charAt(index); | |
lettering: { | |
if (escapeChar === letter) { | |
++index; | |
} else { | |
for (var nesterIndex = 0; nesterIndex < nesters.length; ++nesterIndex) { | |
nester = nesters[nesterIndex]; | |
if (nester.depth && nester.end === letter) { | |
--nester.depth; | |
--totalDepth; | |
break lettering; | |
} else if (nester.start === letter) { | |
++nester.depth; | |
++totalDepth; | |
break lettering; | |
} | |
} | |
if (!totalDepth && separator === letter) { | |
array.push(string.slice(lastIndex, index)); | |
lastIndex = ++index; | |
} | |
} | |
} | |
} | |
array.push(string.slice(lastIndex, index)); | |
return array; | |
} | |
// querySelectorAllElements | |
function querySelectorAllElements(elements, selector) { | |
// filtered elements | |
var filteredElements = []; | |
// for each element | |
forEach.call(elements, function (element) { | |
// add all queried elements into the filtered elements | |
push.apply(filteredElements, element.querySelectorAll(selector)); | |
}); | |
// return non-duplicate elements | |
return filter.call(filteredElements, function (element, index) { | |
return index === filteredElements.indexOf(element); | |
}); | |
} | |
// polyfill a constructor | |
function polyfill(constructor) { | |
// queryAll | |
function queryAll(selector) { | |
// cache elements | |
var elements = new Elements(); | |
// push this element into elements | |
push.call(elements, this); | |
// for each selector | |
forEach.call(split(selector, ','), function (selector) { | |
// selector, split by spaces | |
var selectorFragments = split(selector.trim(), ' '); | |
// cache elements matching the first selector fragment | |
elements = querySelectorAllElements(elements, selectorFragments[0]); | |
// if there is more than one selector fragment | |
if (1 < selectorFragments.length) { | |
// cache elements matching the entire selector | |
elements = querySelectorAllElements(elements, selector); | |
} | |
}); | |
// sort elements by position | |
sort.call(elements, function (elementA, elementB) { | |
return 3 - (elementA.compareDocumentPosition(elementB) & 6); | |
}); | |
// return elements | |
return elements; | |
} | |
// query | |
function query(selector) { | |
// return first element of queryAll or null | |
return queryAll.call(this, selector)[0] || null; | |
} | |
// add queryAll to the constructor’s prototype | |
constructor.prototype.queryAll = queryAll; | |
// add query to the constructor’s prototype | |
constructor.prototype.query = query; | |
} | |
// Elements constructor (https://dom.spec.whatwg.org/#element-collections) | |
function Elements() {} | |
// extend Elements constructor with Array | |
Elements.prototype = new Array(); | |
// restore Elements constructor | |
Elements.prototype.constructor = Elements; | |
// polyfill Elements | |
window.Elements = Elements; | |
// polyfill Document#queryAll, Document#query (https://dom.spec.whatwg.org/#dom-parentnode-queryrelativeselectors) | |
polyfill(Document || HTMLDocument); | |
// polyfill Element#queryAll, Element#query (https://dom.spec.whatwg.org/#dom-parentnode-queryrelativeselectors) | |
polyfill(Element); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment