Last active
June 26, 2018 09:26
-
-
Save c7x43t/b6cd499910bc5ea43473318282e5748a to your computer and use it in GitHub Desktop.
This file contains hidden or 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(global) { | |
// Instead of looping trough all possibilities a object property lookup is used | |
// The object construction is in addition memoized, since it's relatively expensive | |
var hashFindHash = {}; | |
function hashFind(str, hash, delimiter) { | |
delimiter = delimiter || ";"; | |
if (hashFindHash.hasOwnProperty(hash)) { | |
return hashFindHash[hash].hasOwnProperty(str); | |
} else { | |
var obj = {}; | |
map(hash.split(delimiter), function(e) { | |
obj[e] = true; | |
}); | |
hashFindHash[hash] = obj; | |
return hashFind(str, hash); | |
} | |
} | |
// loops once trough the string parsing and interpreting it | |
var activeElements = []; | |
var activePosition = 0; | |
var pos = 0; | |
var sel; | |
function fastQuery(selector) { | |
sel = selector; | |
while (pos < sel.length) { | |
//console.log({pos:pos,len:sel.length}); | |
//console.log({pos:pos[0],len:sel.length}) | |
main(); | |
//console.log({context:arguments.callee.name,activeElements:activeElements}) | |
} | |
return activeElements; | |
} | |
function handleClass() { | |
// console.log({context:arguments.callee.name}); | |
// find index of first character after the className | |
var className = indexOfAny(sel, "[;#;:;.;::;~;+;>; ;,", pos); | |
//console.log({className:className}) | |
var tmp; | |
if (activeElements[activePosition] instanceof Element) { | |
tmp = document.getElementsByClassName(className); | |
//console.log({tmp:tmp}) | |
map(tmp, function(e) { | |
//console.log(e); | |
activeElements.push(e); | |
}, activePosition); | |
} else { | |
if (sel[pos - 1] === " ") { | |
map(activeElements, function(e) { | |
tmp = e.getElementsByClassName(className); | |
map(tmp, function(e) { | |
activeElements.push(e); | |
}); | |
}, activePosition); | |
} else { | |
map(activeElements, function(e) { | |
classList = e.classList; | |
map(classList, function(c) { | |
if (className === c) { | |
activeElements.push(e); | |
} | |
}); | |
}, activePosition); | |
} | |
} | |
} | |
function map(arr, f, startIndex) { | |
var tmp = []; | |
for (var i = startIndex || 0, l = arr.length; i < l; i++) { | |
tmp[i] = f(arr[i], i, arr); | |
} | |
return tmp; | |
} | |
function handleID() { | |
var idName = indexOfAny(sel, "[;#;:;.;::;~;+;>; ;,", pos); | |
// console.log({context:arguments.callee.name,idName:idName}); | |
activeElements.length = activePosition; | |
activeElements.push(document.getElementById(idName)); | |
} | |
function handleTag() { | |
var tagName = indexOfAny(sel, "[;#;:;.;::;~;+;>; ;,", pos); | |
//console.log({context:arguments.callee.name,tagName:tagName}); | |
var tmp; | |
if (activeElements[activePosition] instanceof Element) { | |
tmp = document.getElementsByTagName(tagName); | |
map(tmp, function(e) { | |
//console.log(e); | |
activeElements.push(e); | |
}, activePosition); | |
} else { | |
map(activeElements, function(e) { | |
tmp = e.getElementsByTagName(tagName); | |
map(tmp, function(e) { | |
activeElements.push(e); | |
}); | |
}, activePosition); | |
} | |
} | |
function handleStar() { | |
activeElements = document.getElementsByTagName("*"); | |
} | |
function indexOfAny(str, els) { | |
// console.log({context:arguments.callee.name}); | |
var substr = ""; | |
var i = pos || 0, | |
l = str.length; | |
for (; i < l; i++) { | |
if (hashFind(str[i], els)) { | |
pos = i; | |
return substr; | |
} | |
substr += str[i]; | |
} | |
// console.log({settingPos:i}) | |
pos = i; | |
return substr; | |
} | |
function main() { | |
pos++; | |
switch (sel[pos - 1]) { | |
case "*": | |
return handleStar(); | |
case ".": | |
return handleClass(); | |
case "#": | |
return handleID(); | |
case "[": | |
return handleAttribute(); | |
case ":": | |
// : | |
if (sel[pos + 1] !== ":") { | |
return handlePseudo(); | |
// :: | |
} else { | |
pos++; | |
return handleCSS(); | |
} | |
case ",": | |
return handleComma(); | |
// new activeElements array or with pointer | |
case "~": | |
break; | |
// a.parentElement.children filter: e===b | |
case " ": | |
break; | |
// new query using active Elements as base | |
case ">": | |
break; | |
// either a.children filter: e===b or filter: b.parentElement===a | |
case "+": | |
break; | |
// a+b: filter: a.nextElementSibling === b | |
// TAGs | |
default: | |
//console.log({main:"TAG found",pos:pos}); | |
pos--; | |
return handleTag(); | |
} | |
} | |
global.fastQuery = fastQuery; | |
}(window)); |
This file contains hidden or 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
// Note: don't use firstChild/lastChild because it caches empty Text Nodes (false positive) | |
// use .children instead. | |
// principle | |
// 2 active element stacks, one holds intermediate elements, the other the next filterstep | |
// swap back and forth by pointer | |
//.intro | |
//if no activeElements: .b | |
document.getElementsByClassName(className) map: activeElements.push(e) | |
// if activeElements: a .b | |
map(activeElements, ...: e.getElementsByClassName(className) map: activeElements2.push(f) | |
// if filter: a.b | |
map: a.classList filter: e === className | |
// #Lastname | |
activeElements = [] activeElements.push(document.getElementById(id)) | |
//.intro, #Lastname | |
// push result before , into finalElements (by pointer) | |
// at the end return finalElements or else activeElements | |
//h1 | |
//if no activeElements: b | |
document.getElementsByTagName(tagName) map: activeElements.push(e) | |
// if activeElements: a b | |
map(activeElements, ...: e.getElementsByTagName(tagName) map: activeElements2.push(f) | |
//div p | |
// buildin as activeElements filter | |
//div > p: a > b | |
a.children filter: e === b //O(a.children*b) <-reduce complexity | |
// with e.query b reduce: acc&= x === e | |
//ul + p: a + b | |
filter: a.nextElementSibling === b | |
//ul ~ table: a ~ b | |
a.parentElement.children filter: e === b // O(a.parentElement.children*b) <-reduce complexity | |
//* => immediatly return: | |
document.getElementsByTagName("*"); | |
//[id] | |
//[id=my-Address] | |
//[id$=ess] | |
//[id|=my] | |
//[id^=L] | |
//[title~=beautiful] | |
//[id*=s] | |
// use getElementsByName if id===name else use querySelectorAll | |
// if no activeElements: [id] | |
// else if activeElements: a [id]: a.map: e.getElementsByName | |
//:checked | |
//:disabled | |
//:enabled | |
//:empty | |
//:focus | |
//p:first-child: a:first-child | |
function helper(e) { | |
return e.parentElement.firstChild === e | |
}; filter(activeElements, helper) | |
//p::first-letter | |
//p::first-line | |
//p:first-of-type === li:nth-of-type(1) | |
//h1:hover | |
//input:in-range | |
function helper(e) { | |
return e.min !== "" && e.max !== "" && e.value !== "" && +e.min <= +e.value && +e.value <= +e.max | |
}; filter(document.getElementsByTagName("input"), helper) | |
//input:out-of-range | |
function helper(e) { | |
return e.min !== "" && e.max !== "" && e.value !== "" && +e.min > +e.value && +e.value > +e.max | |
}; filter(document.getElementsByTagName("input"), helper) | |
//input:invalid | |
//input:valid | |
//p:lang(it) | |
//p:last-child: fix: multiple parent nodes | |
var p = "p"; | |
function helper(e) { | |
return e.parentElement.lastChild === e | |
}; filter(document.getElementsByTagName(p), helper) | |
//p:last-of-type // works === li:nth-last-of-type(1) | |
var p = "p"; | |
function helper(e) { | |
var candidates = e.parentElement.getElementsByTagName(p); | |
for (var i = candidates.length - 1; i >= 0; i--) { | |
if (e.parentElement === candidates[i].parentElement) { | |
return e === candidates[i]; | |
} | |
} | |
return false; | |
}; filter(document.getElementsByTagName(p), helper) | |
//tr:nth-child(even) <- even/odd way too inefficient (75x) | |
var tr = "tr"; | |
function helper(e, i) { | |
var children = e.parentElement.children; | |
for (var i = 0; i < (children.length - children.length % 2) / 2; i++) { | |
if (children[i] === e) return true; | |
} | |
return false; | |
}; filter(document.getElementsByTagName(tr), helper) | |
//tr:nth-child(odd) | |
//li:nth-child(n) //works | |
function helper(e, i) { | |
return e === e.parentElement.children[n - 1] | |
}; filter(document.getElementsByTagName(li), helper) | |
//li:nth-last-child(1) | |
function helper(e, i) { | |
var children = e.parentElement.children; | |
return e === children[children.length - n] | |
}; filter(document.getElementsByTagName(li), helper) | |
//li:nth-of-type(2) // works | |
var li = "li", n = 2; | |
function helper(e) { | |
var candidates = e.parentElement.getElementsByTagName(li); | |
for (var i = n - 1; i < candidates.length; i++) { | |
if (e.parentElement === candidates[i].parentElement) { | |
return e === candidates[i]; | |
} | |
} | |
return false; | |
}; filter(document.getElementsByTagName(li), helper) | |
//li:nth-last-of-type(2) | |
var li = "li", n = 2; | |
function helper(e) { | |
var candidates = e.parentElement.getElementsByTagName(li); | |
for (var i = candidates.length - n; i >= 0; i--) { | |
if (e.parentElement === candidates[i].parentElement) { | |
return e === candidates[i]; | |
} | |
} | |
return false; | |
}; filter(document.getElementsByTagName(li), helper) | |
//b:only-child // works | |
function helper(e) { | |
return e.parentElement.children.length === 1 | |
}; filter(activeElements, helper) | |
//h3:only-of-type // -> too slow | |
var h3 = "h3"; | |
function helper(e) { | |
var n = 0; | |
map(e.parentElement.getElementsByTagName(h3), function(candidate) { | |
if (e.parentElement === candidate.parentElement) n++; | |
}); | |
return n === 1; | |
}; filter(document.getElementsByTagName(h3), helper) | |
//:root | |
var HTML = "html"; document.getElementsByTagName(HTML); | |
//Helper | |
function filter(arr, f, startIndex) { | |
var tmp = []; | |
for (var i = startIndex || 0, l = arr.length; i < l; i++) { | |
if (f(arr[i], i, arr)) tmp.push(arr[i]); | |
} | |
return tmp; | |
} | |
function map(arr, f, startIndex) { | |
var tmp = []; | |
for (var i = startIndex || 0, l = arr.length; i < l; i++) { | |
tmp[i] = f(arr[i], i, arr); | |
} | |
return tmp; | |
} | |
//Benchmark | |
function helper(e, i) { | |
return i % 2 === 0 | |
}; | |
var funcs = [ | |
e => filter(document.getElementsByTagName("tr"), helper), | |
e => { | |
document.querySelectorAll("tr:nth-child(even)") | |
}, | |
]; benchmark(funcs, 1e5, ["it"]) |
This file contains hidden or 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
var selectorAtomizer = /(((.*?)(\[))?|(\.|::|:|#| |~|\+|>)?)(.*?)(\.|~=|\$=|\|=|\*=|\^=|=|::|:|\]|#|\(.*?\)| |~|\+|>|$)/g; | |
var sanitizeCombinators = / *(~=|\$=|\|=|\*=|\^=|=|>|~|\+|,|\[|\(|\)) */g; | |
var sanitizeSpaces = / +/g; | |
regExCommaSplit = / *, */; | |
var pointers = [3, 4, 6, 7]; | |
function fastSelector(element, selector) { | |
element = element || document; | |
// clean up selector string | |
selector = selector.replace(sanitizeCombinators, '$1'); | |
selector = selector.replace(sanitizeSpaces, ' '); | |
// splitting non related parts | |
var selectors = selector.split(regExCommaSplit); | |
// pointers to relevant regEx matching groups | |
var result = []; | |
selectors.forEach(function(selector) { | |
var activeElement = element; | |
var regex = new RegExp(selectorAtomizer); | |
do { | |
var tmp = regex.exec(selector); | |
// this is akin to looping over a sparse array | |
pointers.forEach(function(pointer) { | |
var current = tmp[pointer]; | |
if (current && current !== "") { | |
// interpret chunk | |
result.push(current); | |
} | |
}); | |
//result.push(tmp); | |
} while (tmp.index < selector.length); | |
}); | |
return result; | |
} | |
//fastSelector("a, b c d + e ~ f > g [efe]") | |
var str = "div:nth-child( 2n ),div#scene1 div.dialog div,div[attribute=value]"; | |
fastSelector(document, str); | |
// split at: | |
// : :: [ [space] ~ + > # . | |
// https://regex101.com/r/gy5IGh/1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment