diff --git a/src/scopedQuerySelectorShim.js b/src/scopedQuerySelectorShim.js index 357e15c..5083407 100644 --- a/src/scopedQuerySelectorShim.js +++ b/src/scopedQuerySelectorShim.js @@ -13,8 +13,9 @@ container.querySelectorAll(':scope *'); } catch (e) { - // Match usage of scope - var scopeRE = /^\s*:scope/gi; + var rxTest = /(?:^|,)\s*:scope\s+/, + rxStart = /^\s*:scope\s+/i, + rxOthers = /,\s*:scope\s+/gi; // Overrides function overrideNodeMethod(prototype, methodName) { @@ -23,13 +24,12 @@ // Override the method prototype[methodName] = function(query) { - var nodeList, - gaveId = false, - gaveContainer = false; + var nodeList, parentNode, frag, idSelector, + gaveId = false, + gaveContainer = false, + parentIsFragment = false; - if (query.match(scopeRE)) { - // Remove :scope - query = query.replace(scopeRE, ''); + if (rxTest.test(query)) { if (!this.parentNode) { // Add to temporary container @@ -37,6 +37,12 @@ gaveContainer = true; } + if (this.parentNode instanceof DocumentFragment) { + frag = this.parentNode; + while (frag.firstChild) container.appendChild(frag.firstChild); + parentIsFragment = true; + } + parentNode = this.parentNode; if (!this.id) { @@ -45,8 +51,12 @@ gaveId = true; } + // replace :scope with ID selector + idSelector = '#' + this.id + ' '; + query = query.replace(rxStart, idSelector).replace(rxOthers, ', ' + idSelector); + // Find elements against parent node - nodeList = oldMethod.call(parentNode, '#'+this.id+' '+query); + nodeList = oldMethod.call(parentNode, query); // Reset the ID if (gaveId) { @@ -54,7 +64,9 @@ } // Remove from temporary container - if (gaveContainer) { + if (parentIsFragment) { + while (container.firstChild) frag.appendChild(container.firstChild); + } else if (gaveContainer) { container.removeChild(this); } @@ -71,4 +83,4 @@ overrideNodeMethod(HTMLElement.prototype, 'querySelector'); overrideNodeMethod(HTMLElement.prototype, 'querySelectorAll'); } -}()); +}()); \ No newline at end of file diff --git a/test/testShim.js b/test/testShim.js index e632355..87102c0 100644 --- a/test/testShim.js +++ b/test/testShim.js @@ -14,21 +14,44 @@ describe('scopedQuerySelectorShim', function() { return node; } + function makeNodeAndAddToFragment(html) { + var frag, node; + frag = document.createDocumentFragment(); + node = makeNode(html); + frag.appendChild(node); + return node; + } + function testChildNode(node) { - expect(node.innerHTML).to.equal('Child'); + expect(node.innerHTML).toBe('Child'); } function testChildNodeList(nodeList) { - expect(nodeList.length).to.equal(1); + expect(nodeList.length).toBe(1); testChildNode(nodeList[0]); } + + function testComplexNodeList(nodeList) { + testGrandChildNode(nodeList[0]); + testGrandChildNode1(nodeList[1]); + testGrandChildNode2(nodeList[2]); + testGrandChildNode3(nodeList[3]); + } function testGrandChildNode(node) { - expect(node.innerHTML).to.equal('Grandchild 1'); + expect(node.innerHTML).toBe('Grandchild 1'); } function testGrandChildNode1(node) { - expect(node.innerHTML).to.equal('Grandchild 2'); + expect(node.innerHTML).toBe('Grandchild 2'); + } + + function testGrandChildNode2(node) { + expect(node.innerHTML).toBe('Grandchild 3'); + } + + function testGrandChildNode3(node) { + expect(node.innerHTML).toBe('Grandchild 4'); } function testGrandChildNodeList(nodeList) { @@ -45,7 +68,7 @@ describe('scopedQuerySelectorShim', function() {
\
Grandchild
\
\ - '; +
'; var listHTML = '
\ \
'; + var complexHTML = '
\ + \ +
    \ +
  1. Grandchild 3
  2. \ +
  3. Grandchild 4
  4. \ +
\ +
'; + describe('when nodes are in the DOM', function() { it('should find child nodes', function() { testChildNode(makeNodeAndAddToDOM(childHTML).querySelector(':scope > header')); @@ -64,6 +98,11 @@ describe('scopedQuerySelectorShim', function() { testGrandChildNode(makeNodeAndAddToDOM(listHTML).querySelector(':scope > ul > li')); testGrandChildNodeList(makeNodeAndAddToDOM(listHTML).querySelectorAll(':scope > ul > li')); }); + + it('should handle complex queries', function() { + testGrandChildNode(makeNodeAndAddToDOM(complexHTML).querySelector(':scope > ul > li, :scope > ol > li')); + testComplexNodeList(makeNodeAndAddToDOM(complexHTML).querySelectorAll(':scope > ul > li, :scope > ol > li')); + }); }); describe('when nodes are not in the DOM', function() { @@ -76,30 +115,52 @@ describe('scopedQuerySelectorShim', function() { testGrandChildNode(makeNode(listHTML).querySelector(':scope > ul > li')); testGrandChildNodeList(makeNode(listHTML).querySelectorAll(':scope > ul > li')); }); + + it('should handle complex queries', function() { + testGrandChildNode(makeNode(complexHTML).querySelector(':scope > ul > li, :scope > ol > li')); + testComplexNodeList(makeNode(complexHTML).querySelectorAll(':scope > ul > li, :scope > ol > li')); + }); + }); + + describe('when nodes are in a document fragment', function() { + it('should find child nodes', function() { + testChildNode(makeNodeAndAddToFragment(childHTML).querySelector(':scope > header')); + testChildNodeList(makeNodeAndAddToFragment(childHTML).querySelectorAll(':scope > header')); + }); + + it('should find grandchild nodes', function() { + testGrandChildNode(makeNodeAndAddToFragment(listHTML).querySelector(':scope > ul > li')); + testGrandChildNodeList(makeNodeAndAddToFragment(listHTML).querySelectorAll(':scope > ul > li')); + }); + + it('should handle complex queries', function() { + testGrandChildNode(makeNodeAndAddToFragment(complexHTML).querySelector(':scope > ul > li, :scope > ol > li')); + testComplexNodeList(makeNodeAndAddToFragment(complexHTML).querySelectorAll(':scope > ul > li, :scope > ol > li')); + }); }); describe('when temporary containers and IDs are used', function() { it('should not leave nodes in temporary container', function() { var node = makeNode(childHTML); - expect(node.parentNode).to.be.null; + expect(node.parentNode).toBe(null); node.querySelectorAll(':scope > header'); - expect(node.parentNode).to.be.null; + expect(node.parentNode).toBe(null); }); it('should not leave temporary IDs', function() { var node = makeNode(childHTML); - expect(node.id).to.equal(''); + expect(node.id).toBe(''); node.querySelectorAll(':scope > header'); - expect(node.id).to.equal(''); + expect(node.id).toBe(''); }); }); describe('when nodes have IDs', function() { it('should not overwrite existing IDs', function() { var node = makeNode(idHTML); - expect(node.id).to.equal('myDiv'); + expect(node.id).toBe('myDiv'); node.querySelectorAll(':scope > header'); - expect(node.id).to.equal('myDiv'); + expect(node.id).toBe('myDiv'); }); }); });