diff --git a/src/dom.js b/src/dom.js index 7fd24f4868c5..27936f1cfea8 100644 --- a/src/dom.js +++ b/src/dom.js @@ -374,6 +374,21 @@ export function childElementByTag(parent, tagName) { } +/** + * A wrapper around native querySelectorAll to use :scope when possible. + * @param {!Element} parent + * @param {string} selector + * @returns {!Array} + */ +export function scopedQuerySelectorAll(parent, selector) { + if (scopeSelectorSupported) { + const scopedSelectors = selector.split(',').map(sel => `:scope ${sel}`); + return toArray(parent.querySelectorAll(scopedSelectors.join(','))); + } + return toArray(parent.querySelectorAll(selector)); +} + + /** * Returns element data-param- attributes as url parameters key-value pairs. * e.g. data-param-some-attr=value -> {someAttr: value}. diff --git a/test/functional/test-dom.js b/test/functional/test-dom.js index bc9fdc29f899..3a6b4bff364e 100644 --- a/test/functional/test-dom.js +++ b/test/functional/test-dom.js @@ -291,6 +291,32 @@ describe('DOM', () => { expect(dom.lastChildElementByAttr(parent, 'on-child')).to.be.null; }); + function testScopedQuerySelectorAll() { + const parent = document.createElement('parent'); + const element1 = document.createElement('element1'); + parent.appendChild(element1); + const element2 = document.createElement('element2'); + parent.appendChild(element2); + const element3 = document.createElement('element3'); + element1.appendChild(element3); + + const element1Nested = document.createElement('element1'); + element2.appendChild(element1Nested); + + expect(dom.scopedQuerySelectorAll(parent, 'element1, element3').length) + .to.equal(3); + expect(dom.scopedQuerySelectorAll(parent, 'element2').length).to.equal(1); + expect(dom.scopedQuerySelectorAll(parent, 'element4').length).to.equal(0); + } + + it('scopedQuerySelectorAll should return all matches', + testScopedQuerySelectorAll); + + it('scopedQuerySelectorAll should return all matches (polyfill)', () => { + dom.setScopeSelectorSupportedForTesting(false); + testScopedQuerySelectorAll(); + }); + describe('contains', () => { let connectedElement; let connectedChild;