Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
Merge pull request #492 from arv/qsa-deep
Browse files Browse the repository at this point in the history
Add support for /deep/ to querySelector and querySelectorAll
  • Loading branch information
arv committed Aug 14, 2014
2 parents 4702052 + 13e334d commit 57232dc
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 21 deletions.
48 changes: 32 additions & 16 deletions src/querySelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
var OriginalElement = window.Element;
var OriginalDocument = window.HTMLDocument || window.Document;

function filterNodeList(list, index, result) {
function filterNodeList(list, index, result, deep) {
var wrappedItem = null;
var root = null;
for (var i = 0, length = list.length; i < length; i++) {
wrappedItem = wrap(list[i]);
if (root = getTreeScope(wrappedItem).root) {
if (!deep && (root = getTreeScope(wrappedItem).root)) {
if (root instanceof scope.wrappers.ShadowRoot) {
continue;
}
Expand All @@ -42,6 +42,10 @@
return index;
}

function shimSelector(selector) {
return String(selector).replace(/\/deep\//g, ' ');
}

function findOne(node, selector) {
var m, el = node.firstElementChild;
while (el) {
Expand Down Expand Up @@ -98,7 +102,7 @@
// Structural Pseudo Classes are not guarenteed to be correct
// http://www.w3.org/TR/css3-selectors/#simple-selectors

function querySelectorAllFiltered (p, index, result, selector) {
function querySelectorAllFiltered(p, index, result, selector, deep) {
var target = unsafeUnwrap(this);
var list;
var root = getTreeScope(this).root;
Expand All @@ -116,11 +120,15 @@
return findElements(this, index, result, p, selector, null);
}

return filterNodeList(list, index, result);
return filterNodeList(list, index, result, deep);
}

var SelectorsInterface = {
querySelector: function(selector) {
var shimmed = shimSelector(selector);
var deep = shimmed !== selector;
selector = shimmed;

var target = unsafeUnwrap(this);
var wrappedItem;
var root = getTreeScope(this).root;
Expand All @@ -142,7 +150,7 @@
// When the original query returns nothing
// we return nothing (to be consistent with the other wrapped calls)
return wrappedItem;
} else if (root = getTreeScope(wrappedItem).root) {
} else if (!deep && (root = getTreeScope(wrappedItem).root)) {
if (root instanceof scope.wrappers.ShadowRoot) {
// When the original query returns an element in the ShadowDOM
// we must do a manual tree traversal
Expand All @@ -153,19 +161,25 @@
return wrappedItem;
},
querySelectorAll: function(selector) {
var shimmed = shimSelector(selector);
var deep = shimmed !== selector;
selector = shimmed;

var result = new NodeList();

result.length = querySelectorAllFiltered.call(this,
matchesSelector,
0,
result,
selector);
selector,
deep);

return result;
}
};

function getElementsByTagNameFiltered (p, index, result, localName, lowercase) {
function getElementsByTagNameFiltered(p, index, result, localName,
lowercase) {
var target = unsafeUnwrap(this);
var list;
var root = getTreeScope(this).root;
Expand All @@ -174,19 +188,21 @@
// going to be disconnected so we do a manual tree traversal
return findElements(this, index, result, p, localName, lowercase);
} else if (target instanceof OriginalElement) {
list = originalElementGetElementsByTagName.call(target, localName, lowercase);
list = originalElementGetElementsByTagName.call(target, localName,
lowercase);
} else if (target instanceof OriginalDocument) {
list = originalDocumentGetElementsByTagName.call(target, localName, lowercase);
list = originalDocumentGetElementsByTagName.call(target, localName,
lowercase);
} else {
// When we get a ShadowRoot the logical tree is going to be disconnected
// so we do a manual tree traversal
return findElements(this, index, result, p, localName, lowercase);
}

return filterNodeList(list, index, result);
return filterNodeList(list, index, result, false);
}

function getElementsByTagNameNSFiltered (p, index, result, ns, localName) {
function getElementsByTagNameNSFiltered(p, index, result, ns, localName) {
var target = unsafeUnwrap(this);
var list;
var root = getTreeScope(this).root;
Expand All @@ -204,17 +220,17 @@
return findElements(this, index, result, p, ns, localName);
}

return filterNodeList(list, index, result);
return filterNodeList(list, index, result, false);
}

var GetElementsByInterface = {
getElementsByTagName: function(localName) {
var result = new HTMLCollection();
var match = localName === '*' ? matchesEveryThing : matchesTagName;

result.length = getElementsByTagNameFiltered.call(this,
result.length = getElementsByTagNameFiltered.call(this,
match,
0,
0,
result,
localName,
localName.toLowerCase());
Expand All @@ -236,8 +252,8 @@
} else {
match = localName === '*' ? matchesNameSpace : matchesLocalNameNS;
}
result.length = getElementsByTagNameNSFiltered.call(this,

result.length = getElementsByTagNameNSFiltered.call(this,
match,
0,
result,
Expand Down
48 changes: 45 additions & 3 deletions test/js/Document.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ htmlSuite('Document', function() {
assert.equal(doc.head.parentNode, doc.documentElement);
});

skipTest('getElementsByTagName', function() {
test('getElementsByTagName', function() {
var elements = document.getElementsByTagName('body');
assert.isTrue(elements instanceof HTMLCollection);
assert.equal(elements.length, 1);
Expand All @@ -65,7 +65,9 @@ htmlSuite('Document', function() {
assert.isTrue(elements2[0] instanceof HTMLElement);
assert.equal(doc.body, elements2[0]);
assert.equal(doc.body, elements2.item(0));
});

skipTest('getElementsByTagName', function() {
div = document.body.appendChild(document.createElement('div'));
div.innerHTML = '<aa></aa><aa></aa>';
var aa1 = div.firstChild;
Expand Down Expand Up @@ -143,7 +145,7 @@ htmlSuite('Document', function() {
assert.equal(all.length, 0);
});

skipTest('querySelectorAll', function() {
test('querySelectorAll', function() {
var elements = document.querySelectorAll('body');
assert.isTrue(elements instanceof NodeList);
assert.equal(elements.length, 1);
Expand All @@ -157,7 +159,9 @@ htmlSuite('Document', function() {
assert.equal(elements2.length, 1);
assert.isTrue(elements2[0] instanceof HTMLElement);
assert.equal(doc.body, elements2[0]);
});

skipTest('querySelectorAll', function() {
div = document.body.appendChild(document.createElement('div'));
div.innerHTML = '<aa></aa><aa></aa>';
var aa1 = div.firstChild;
Expand All @@ -184,11 +188,49 @@ htmlSuite('Document', function() {
assert.equal(z.length, 0);
});

skipTest('querySelector', function() {
test('querySelector', function() {
var z = document.querySelector('z');
assert.equal(z, null);
});

test('querySelector deep', function() {
div = document.body.appendChild(document.createElement('div'));
div.innerHTML = '<aa></aa><aa></aa>';
var aa1 = div.firstChild;
var aa2 = div.lastChild;

var sr = div.createShadowRoot();
sr.innerHTML = '<bb></bb><content></content>';
var bb = sr.firstChild;

div.offsetHeight;

assert.equal(aa1, document.querySelector('div /deep/ aa'));
assert.equal(bb, document.querySelector('div /deep/ bb'));
});

test('querySelectorAll deep', function() {
div = document.body.appendChild(document.createElement('div'));
div.innerHTML = '<aa></aa><aa></aa>';
var aa1 = div.firstChild;
var aa2 = div.lastChild;

var sr = div.createShadowRoot();
sr.innerHTML = '<bb></bb><content></content>';
var bb = sr.firstChild;

div.offsetHeight;

var list = document.querySelectorAll('div /deep/ aa');
assert.equal(2, list.length);
assert.equal(aa1, list[0]);
assert.equal(aa2, list[1]);

list = document.querySelectorAll('div /deep/ bb');
assert.equal(1, list.length);
assert.equal(bb, list[0]);
});

test('addEventListener', function() {
var calls = 0;
var doc = wrap(document);
Expand Down
49 changes: 47 additions & 2 deletions test/js/Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ suite('Element', function() {

function skipTest () {}

skipTest('querySelector', function() {
test('querySelector', function() {
var div = document.createElement('div');
div.innerHTML = '<a><b></b></a>';
var b = div.firstChild.firstChild;
Expand All @@ -39,7 +39,7 @@ suite('Element', function() {
assert.equal(z, null);
});

skipTest('querySelectorAll', function() {
test('querySelectorAll', function() {
var div = document.createElement('div');
div.innerHTML = '<a>0</a><a>1</a>';
var a0 = div.firstChild;
Expand All @@ -51,6 +51,13 @@ suite('Element', function() {
assert.equal(as.item(0), a0);
assert.equal(as[1], a1);
assert.equal(as.item(1), a1);
});

skipTest('querySelectorAll', function() {
var div = document.createElement('div');
div.innerHTML = '<a>0</a><a>1</a>';
var a0 = div.firstChild;
var a1 = div.lastChild;

var sr = div.createShadowRoot();
sr.innerHTML = '<a>3</a><a>4</a>';
Expand All @@ -76,6 +83,44 @@ suite('Element', function() {
assert.equal(z.length, 0);
});

test('querySelector deep', function() {
var div = document.createElement('div');
div.innerHTML = '<aa></aa><aa></aa>';
var aa1 = div.firstChild;
var aa2 = div.lastChild;

var sr = div.createShadowRoot();
sr.innerHTML = '<bb></bb><content></content>';
var bb = sr.firstChild;

div.offsetHeight;

assert.equal(aa1, div.querySelector('div /deep/ aa'));
assert.equal(bb, div.querySelector('div /deep/ bb'));
});

test('querySelectorAll deep', function() {
var div = document.createElement('div');
div.innerHTML = '<aa></aa><aa></aa>';
var aa1 = div.firstChild;
var aa2 = div.lastChild;

var sr = div.createShadowRoot();
sr.innerHTML = '<bb></bb><content></content>';
var bb = sr.firstChild;

div.offsetHeight;

var list = div.querySelectorAll('div /deep/ aa');
assert.equal(2, list.length);
assert.equal(aa1, list[0]);
assert.equal(aa2, list[1]);

list = div.querySelectorAll('div /deep/ bb');
assert.equal(1, list.length);
assert.equal(bb, list[0]);
});

skipTest('getElementsByTagName', function() {
var div = document.createElement('div');
div.innerHTML = '<a>0</a><a>1</a>';
Expand Down

0 comments on commit 57232dc

Please sign in to comment.