Skip to content

Instantly share code, notes, and snippets.

@ryanmorr
Last active April 29, 2018 20:18
Show Gist options
  • Save ryanmorr/83fe9c9c04acd0c792b4e303c713482f to your computer and use it in GitHub Desktop.
Save ryanmorr/83fe9c9c04acd0c792b4e303c713482f to your computer and use it in GitHub Desktop.
A simple, context-aware selector engine
function query(selector, root = document) {
let context = [root];
const tokens = selector.trim().split(/\s*(>|\+(?!\d)|~(?!=)|\s)\s*/).filter(n => !!n);
while (tokens.length && context.length) {
let token = tokens.shift(), combinator;
if (/>|\+(?!\d)|~(?!=)|\s/.test(token)) {
combinator = token;
token = tokens.shift();
}
context = context.reduce((nodes, el) => {
let node;
switch (combinator) {
case '>':
const elements = el.children;
for (let i = 0, len = elements.length; i < len; i++) {
node = elements[i];
if (node.matches(token)) {
nodes.push(node);
}
}
return nodes;
case '+':
node = el.nextElementSibling;
if (node && node.matches(token)) {
nodes.push(node);
}
return nodes;
case '~':
node = el.nextElementSibling;
while (node) {
if (node.matches(token)) {
nodes.push(node);
}
node = node.nextElementSibling;
}
return nodes;
default:
return nodes.concat([].slice.call(el.querySelectorAll(token)));
}
}, []);
}
return context.sort((a, b) => 3 - (a.compareDocumentPosition(b) & 6));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment