Skip to content

Commit 1a9ae20

Browse files
committed
add support for alternative document for issue #5
1 parent ea3eed5 commit 1a9ae20

File tree

7 files changed

+101
-31
lines changed

7 files changed

+101
-31
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,16 @@ const path = require('path');
4343

4444
})()
4545
```
46+
### Provide alternative node
47+
```javascript
48+
// query from another node
49+
querySelectorShadowDom.querySelectorAllDeep('child', document.querySelector('#startNode'));
50+
// query an iframe
51+
querySelectorShadowDom.querySelectorAllDeep('child', iframe.contentDocument);
52+
```
4653

54+
This library does not allow you to query across iframe boundaries, you will ned to get a reference to the iframe you want to interact with. </br>
55+
If your iframe is inside of a shadow root you could cuse `querySelectorDeep` to find the iframe, then pass the `contentDocument` into the 2nd argument of `querySelectorDeep` or `querySelectorAllDeep`.
4756

4857

4958
### Chrome downloads page

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "query-selector-shadow-dom",
3-
"version": "0.2.5",
3+
"version": "0.3.0",
44
"description": "use querySelector syntax to search for nodes inside of (nested) shadow roots",
55
"main": "src/querySelectorDeep.js",
66
"scripts": {

src/querySelectorDeep.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@
1717
* Another example querySelectorAllDeep('#downloads-list div#title-area + a');
1818
e.g.
1919
*/
20-
export function querySelectorAllDeep(selector) {
21-
return _querySelectorDeep(selector, true);
20+
export function querySelectorAllDeep(selector, root = document) {
21+
return _querySelectorDeep(selector, true, root);
2222
}
2323

24-
export function querySelectorDeep(selector) {
25-
return _querySelectorDeep(selector);
24+
export function querySelectorDeep(selector, root = document) {
25+
return _querySelectorDeep(selector, false, root);
2626
}
2727

28-
function _querySelectorDeep(selector, findMany) {
29-
let lightElement = document.querySelector(selector);
28+
function _querySelectorDeep(selector, findMany, root) {
29+
let lightElement = root.querySelector(selector);
3030

3131
if (document.head.createShadowRoot || document.head.attachShadow) {
3232
// no need to do any special if selector matches something specific in light-dom
@@ -50,8 +50,8 @@ function _querySelectorDeep(selector, findMany) {
5050
// filter out entry white selectors
5151
.filter((entry) => !!entry);
5252
const possibleElementsIndex = splitSelector.length - 1;
53-
const possibleElements = collectAllElementsDeep(splitSelector[possibleElementsIndex]);
54-
const findElements = findMatchingElement(splitSelector, possibleElementsIndex);
53+
const possibleElements = collectAllElementsDeep(splitSelector[possibleElementsIndex], root);
54+
const findElements = findMatchingElement(splitSelector, possibleElementsIndex, root);
5555
if (findMany) {
5656
acc = acc.concat(possibleElements.filter(findElements));
5757
return acc;
@@ -66,13 +66,13 @@ function _querySelectorDeep(selector, findMany) {
6666
if (!findMany) {
6767
return lightElement;
6868
} else {
69-
return document.querySelectorAll(selector);
69+
return root.querySelectorAll(selector);
7070
}
7171
}
7272

7373
}
7474

75-
function findMatchingElement(splitSelector, possibleElementsIndex) {
75+
function findMatchingElement(splitSelector, possibleElementsIndex, root) {
7676
return (element) => {
7777
let position = possibleElementsIndex;
7878
let parent = element;
@@ -86,7 +86,7 @@ function findMatchingElement(splitSelector, possibleElementsIndex) {
8686
if (foundMatch) {
8787
position--;
8888
}
89-
parent = findParentOrHost(parent);
89+
parent = findParentOrHost(parent, root);
9090
}
9191
return foundElement;
9292
};
@@ -112,9 +112,9 @@ function splitByCharacterUnlessQuoted(selector, character) {
112112
}
113113

114114

115-
function findParentOrHost(element) {
115+
function findParentOrHost(element, root) {
116116
const parentNode = element.parentNode;
117-
return (parentNode && parentNode.host && parentNode.nodeType === 11) ? parentNode.host : parentNode === document ? null : parentNode;
117+
return (parentNode && parentNode.host && parentNode.nodeType === 11) ? parentNode.host : parentNode === root ? null : parentNode;
118118
}
119119

120120
/**
@@ -124,7 +124,7 @@ function findParentOrHost(element) {
124124
* @author ebidel@ (Eric Bidelman)
125125
* License Apache-2.0
126126
*/
127-
function collectAllElementsDeep(selector = null) {
127+
function collectAllElementsDeep(selector = null, root) {
128128
const allElements = [];
129129

130130
const findAllElements = function(nodes) {
@@ -137,7 +137,7 @@ function collectAllElementsDeep(selector = null) {
137137
}
138138
};
139139

140-
findAllElements(document.querySelectorAll('*'));
140+
findAllElements(root.querySelectorAll('*'));
141141

142142
return selector ? allElements.filter(el => el.matches(selector)) : allElements;
143143
}

test/TestComponent.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,23 @@ export class TestComponent extends HTMLElement {
2121
addNested(child) {
2222
this.shadowRoot.querySelector('p').appendChild(child);
2323
}
24+
25+
static get observedAttributes() {
26+
return ['child-class-name', 'child-text-content', 'internal-html'];
27+
}
28+
29+
attributeChangedCallback(name, oldValue, newValue) {
30+
switch (name) {
31+
case 'child-class-name':
32+
this.childClassName = newValue;
33+
break;
34+
case 'child-text-content':
35+
this.childTextContent = newValue;
36+
break;
37+
case 'internal-html':
38+
this.internalHTML = newValue;
39+
break;
40+
}
41+
}
2442
}
2543
customElements.define('test-component', TestComponent);

test/basic.spec.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,40 @@ describe("Basic Suite", function() {
242242
expect(testComponents[1].classList.contains('header-3')).toBeTruthy();
243243
});
244244

245+
it('can provide an alternative node', function() {
246+
const root = document.createElement('div');
247+
parent.appendChild(root);
248+
249+
createTestComponent(root, {
250+
childClassName: 'inner-content'
251+
});
252+
253+
createTestComponent(parent, {
254+
childClassName: 'inner-content'
255+
});
256+
const testComponent = querySelectorAllDeep('.inner-content', root);
257+
expect(testComponent.length).toEqual(1);
258+
259+
});
260+
261+
it('can query nodes in an iframe', function(done) {
262+
263+
const innerframe = `<p class='child'>Content</p>`;
264+
createTestComponent(parent, {
265+
internalHTML: `<iframe id="frame" srcdoc="${innerframe}"></iframe>`
266+
});
267+
setTimeout(() => {
268+
const iframe = querySelectorDeep('#frame');
269+
const testComponents = querySelectorAllDeep('.child', iframe.contentDocument);
270+
debugger;
271+
expect(testComponents.length).toEqual(1);
272+
expect(testComponents[0].textContent).toEqual("Content");
273+
done();
274+
}, 150);
275+
276+
277+
});
278+
245279

246280

247281
// describe(".perf", function() {

test/index.html

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,17 @@
2424
import ('../src/querySelectorDeep.js');
2525
const parent = document.createElement('div');
2626
document.body.appendChild(parent);
27-
const baseComponent = createTestComponent(parent, {
28-
childClassName: 'base',
29-
childTextContent: 'Base Component'
27+
const innerframe = `<p class='child'>Content</p>`;
28+
createTestComponent(parent, {
29+
internalHTML: `<iframe id="frame" srcdoc="${innerframe}"></iframe>`
3030
});
31-
const testComponent = createTestComponent(parent, {
32-
childClassName: 'header-1',
33-
internalHTML: '<div class="header-2">Content</div>'
34-
});
35-
const test2 = createTestComponent(testComponent, {
36-
childClassName: 'header-2'
37-
});
38-
test2.setAttribute('data-test', 'Hello, World')
39-
testComponent.classList.add('header-1');
40-
const testComponents = querySelectorAllDeep(`.header-1 [data-test="Hello, World"], .george`);
41-
window.querySelectorAllDeep = querySelectorAllDeep;
42-
window.querySelectorDeep = querySelectorDeep;
31+
setTimeout(() => {
32+
const iframe = querySelectorDeep('#frame');
33+
const testComponents = querySelectorAllDeep('.child', iframe.contentDocument);
34+
expect(testComponents.length).toEqual(1);
35+
expect(testComponents[0].textContent).toEqual("Content");
36+
done();
37+
}, 300);
4338
})();
4439
</script>
4540
</body>

test/nopolyfills.spec.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,19 @@ describe("No Polyfills Suite", function() {
3838
expect(querySelectorAllDeep('.testing').length).toEqual(2);
3939
});
4040

41+
it('can fallback to query selector when no support and no polyfills with alternative root', function() {
42+
const root = document.createElement('div');
43+
parent.appendChild(root);
44+
const element = document.createElement('a');
45+
const element2 = document.createElement('a');
46+
element.classList.add('testing');
47+
element2.classList.add('testing');
48+
root.appendChild(element);
49+
parent.appendChild(element2);
50+
51+
expect(querySelectorAllDeep('.testing', root).length).toEqual(1);
52+
});
53+
54+
4155

4256
});

0 commit comments

Comments
 (0)