Skip to content

Commit

Permalink
Merge pull request #40 from mariusandra/master
Browse files Browse the repository at this point in the history
Cache collected elements and support ">" in selectors
  • Loading branch information
Georgegriff authored Oct 9, 2020
2 parents 86fe168 + 83b202f commit 69a5db2
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 25 deletions.
69 changes: 46 additions & 23 deletions src/querySelectorDeep.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
* Another example querySelectorAllDeep('#downloads-list div#title-area + a');
e.g.
*/
export function querySelectorAllDeep(selector, root = document) {
return _querySelectorDeep(selector, true, root);
export function querySelectorAllDeep(selector, root = document, allElements = null) {
return _querySelectorDeep(selector, true, root, allElements);
}

export function querySelectorDeep(selector, root = document) {
return _querySelectorDeep(selector, false, root);
export function querySelectorDeep(selector, root = document, allElements = null) {
return _querySelectorDeep(selector, false, root, allElements);
}

function _querySelectorDeep(selector, findMany, root) {
function _querySelectorDeep(selector, findMany, root, allElements = null) {
selector = normalizeSelector(selector)
let lightElement = root.querySelector(selector);

Expand All @@ -48,10 +48,14 @@ function _querySelectorDeep(selector, findMany, root) {
//remove white space at start of selector
.replace(/^\s+/g, '')
.replace(/\s*([>+~]+)\s*/g, '$1'), ' ')
// filter out entry white selectors
.filter((entry) => !!entry);
// filter out entry white selectors
.filter((entry) => !!entry)
// convert "a > b" to ["a", "b"]
.map((entry) => splitByCharacterUnlessQuoted(entry, '>'));

const possibleElementsIndex = splitSelector.length - 1;
const possibleElements = collectAllElementsDeep(splitSelector[possibleElementsIndex], root);
const lastSplitPart = splitSelector[possibleElementsIndex][splitSelector[possibleElementsIndex].length - 1]
const possibleElements = collectAllElementsDeep(lastSplitPart, root, allElements);
const findElements = findMatchingElement(splitSelector, possibleElementsIndex, root);
if (findMany) {
acc = acc.concat(possibleElements.filter(findElements));
Expand Down Expand Up @@ -79,7 +83,23 @@ function findMatchingElement(splitSelector, possibleElementsIndex, root) {
let parent = element;
let foundElement = false;
while (parent && !isDocumentNode(parent)) {
const foundMatch = parent.matches(splitSelector[position]);
let foundMatch = true
if (splitSelector[position].length === 1) {
foundMatch = parent.matches(splitSelector[position]);
} else {
// selector is in the format "a > b"
// make sure a few parents match in order
const reversedParts = ([]).concat(splitSelector[position]).reverse()
let newParent = parent
for (const part of reversedParts) {
if (!newParent || !newParent.matches(part)) {
foundMatch = false
break
}
newParent = findParentOrHost(newParent, root);
}
}

if (foundMatch && position === 0) {
foundElement = true;
break;
Expand Down Expand Up @@ -133,27 +153,30 @@ function findParentOrHost(element, root) {
* @author ebidel@ (Eric Bidelman)
* License Apache-2.0
*/
function collectAllElementsDeep(selector = null, root) {
const allElements = [];

const findAllElements = function(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
allElements.push(el);
// If the element has a shadow root, dig deeper.
if (el.shadowRoot) {
findAllElements(el.shadowRoot.querySelectorAll('*'));
export function collectAllElementsDeep(selector = null, root, cachedElements = null) {
let allElements = [];

if (cachedElements) {
allElements = cachedElements;
} else {
const findAllElements = function(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
allElements.push(el);
// If the element has a shadow root, dig deeper.
if (el.shadowRoot) {
findAllElements(el.shadowRoot.querySelectorAll('*'));
}
}
}
};
if(root.shadowRoot) {
findAllElements(root.shadowRoot.querySelectorAll('*'));
if(root.shadowRoot) {
findAllElements(root.shadowRoot.querySelectorAll('*'));
}
findAllElements(root.querySelectorAll('*'));
}
findAllElements(root.querySelectorAll('*'));

return allElements.filter(el => el.matches(selector));
}


// normalize-selector-rev-02.js
/*
author: kyle simpson (@getify)
Expand Down
49 changes: 47 additions & 2 deletions test/basic.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { querySelectorAllDeep, querySelectorDeep } from '../src/querySelectorDeep.js';
import { querySelectorAllDeep, querySelectorDeep, collectAllElementsDeep } from '../src/querySelectorDeep.js';
import { createTestComponent, createNestedComponent, COMPONENT_NAME, createChildElements } from './createTestComponent.js';


Expand Down Expand Up @@ -33,6 +33,10 @@ describe("Basic Suite", function() {
expect(querySelectorDeep).toEqual(jasmine.any(Function));
});

it("exports collectAllElementsDeep function", function() {
expect(collectAllElementsDeep).toEqual(jasmine.any(Function));
});

it("querySelectorDeep returns null when not found", function() {
expect(querySelectorDeep('whatever')).toBeNull();
});
Expand Down Expand Up @@ -138,6 +142,18 @@ describe("Basic Suite", function() {
expect(testComponents.length).toEqual(1);
});

it('can see inside the shadowRoot with ">" in selector', function() {
const testComponent = createTestComponent(parent, {
childClassName: 'header-1',
internalHTML: '<div class="header-2"><div class="find-me"></div></div>'
});
testComponent.shadowRoot.querySelector('.header-2').host = "test.com";
testComponent.classList.add('container');
const testComponents = querySelectorAllDeep(`.container > div > .header-2 > .find-me`);
expect(testComponents.length).toEqual(1);
expect(testComponents[0].classList.contains('find-me')).toEqual(true);
});

it('can handle extra white space in selectors', function() {
const testComponent = createTestComponent(parent, {
childClassName: 'header-1',
Expand Down Expand Up @@ -313,6 +329,35 @@ describe("Basic Suite", function() {

});

it('can cache collected elements with collectAllElementsDeep', function() {
const root = document.createElement('div');
parent.appendChild(root);

createTestComponent(root, {
childClassName: 'inner-content'
});

createTestComponent(parent, {
childClassName: 'inner-content'
});
const collectedElements = collectAllElementsDeep('', root)
expect(collectedElements.length).toEqual(4);

const testComponents = querySelectorAllDeep('.inner-content', root, collectedElements);
expect(testComponents.length).toEqual(1);

// remove element from dom
testComponents[0].remove()

// not found in dom
const testComponents2 = querySelectorAllDeep('.inner-content', root);
expect(testComponents2.length).toEqual(0);

// still there with cached collectedElements
const testComponents3 = querySelectorAllDeep('.inner-content', root, collectedElements);
expect(testComponents3.length).toEqual(1);
});

it('can query nodes in an iframe', function(done) {

const innerframe = `<p class='child'>Content</p>`;
Expand Down Expand Up @@ -352,4 +397,4 @@ describe("Basic Suite", function() {
});


});
});

0 comments on commit 69a5db2

Please sign in to comment.