Skip to content

Commit

Permalink
Fix getElementsByTagName to handle colon and more
Browse files Browse the repository at this point in the history
Instead of relying on CSS selectors, this now implements the
matching as described by dom.spec.whatwg.org

Fixes Polymer/polymer#468
  • Loading branch information
arv committed Apr 9, 2014
1 parent a86a1b9 commit 19076d5
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 21 deletions.
82 changes: 61 additions & 21 deletions src/querySelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
(function(scope) {
'use strict';

var HTMLCollection = scope.wrappers.HTMLCollection;
var NodeList = scope.wrappers.NodeList;

function findOne(node, selector) {
var m, el = node.firstElementChild;
while (el) {
Expand All @@ -18,15 +21,46 @@
return null;
}

function findAll(node, selector, results) {
function matchesSelector(el, selector) {
return el.matches(selector);
}

var XHTML_NS = 'http://www.w3.org/1999/xhtml';

function matchesTagName(el, localName, localNameLowerCase) {
if (localName === '*')
return true;

var ln = el.localName;
return ln === localName ||
ln === localNameLowerCase && el.namespaceURI === XHTML_NS;
}

function matchesEveryThing() {
return true;
}

function matchesLocalName(el, localName) {
return el.localName === localName;
}

function matchesNameSpace(el, ns) {
return el.namespaceURI === ns;
}

function matchesLocalNameNS(el, ns, localName) {
return el.namespaceURI === ns && el.localName === localName;
}

function findElements(node, result, p, arg0, arg1) {
var el = node.firstElementChild;
while (el) {
if (el.matches(selector))
results[results.length++] = el;
findAll(el, selector, results);
if (p(el, arg0, arg1))
result[result.length++] = el;
findElements(el, result, p, arg0, arg1);
el = el.nextElementSibling;
}
return results;
return result;
}

// find and findAll will only match Simple Selectors,
Expand All @@ -38,32 +72,38 @@
return findOne(this, selector);
},
querySelectorAll: function(selector) {
return findAll(this, selector, new NodeList())
return findElements(this, new NodeList(), matchesSelector, selector);
}
};

var GetElementsByInterface = {
getElementsByTagName: function(tagName) {
// TODO(arv): Check tagName?
return this.querySelectorAll(tagName);
getElementsByTagName: function(localName) {
return findElements(this, new HTMLCollection(),
matchesTagName,
localName,
localName.toLowerCase());
},

getElementsByClassName: function(className) {
// TODO(arv): Check className?
return this.querySelectorAll('.' + className);
},
getElementsByTagNameNS: function(ns, tagName) {
if (ns === '*')
return this.getElementsByTagName(tagName);

// TODO(arv): Check tagName?
var result = new NodeList;
var els = this.getElementsByTagName(tagName);
for (var i = 0, j = 0; i < els.length; i++) {
if (els[i].namespaceURI === ns)
result[j++] = els[i];

getElementsByTagNameNS: function(ns, localName) {
var result = new HTMLCollection();

if (ns === '') {
ns = null;
} else if (ns === '*') {
if (localName === '*')
return findElements(this, result, matchesEveryThing);
return findElements(this, result, matchesLocalName, localName);
}
result.length = j;
return result;

if (localName === '*')
return findElements(this, result, matchesNameSpace, ns);

return findElements(this, result, matchesLocalNameNS, ns, localName);
}
};

Expand Down
30 changes: 30 additions & 0 deletions test/js/Document.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ htmlSuite('Document', function() {
var nsTwo = 'http://two.com';
var aOne = div.appendChild(document.createElementNS(nsOne, 'a'));
var aTwo = div.appendChild(document.createElementNS(nsTwo, 'a'));
var aNull = div.appendChild(document.createElementNS(null, 'a'));
var bOne = div.appendChild(document.createElementNS(nsOne, 'b'));
var bTwo = div.appendChild(document.createElementNS(nsTwo, 'b'));
var bNull = div.appendChild(document.createElementNS(null, 'b'));

var all = div.getElementsByTagNameNS(nsOne, 'a');
assert.equal(all.length, 1);
Expand All @@ -102,10 +106,36 @@ htmlSuite('Document', function() {
assert.equal(all.length, 1);
assert.equal(all[0], aTwo);

var all = div.getElementsByTagNameNS(null, 'a');
assert.equal(all.length, 1);
assert.equal(all[0], aNull);

var all = div.getElementsByTagNameNS('', 'a');
assert.equal(all.length, 1);
assert.equal(all[0], aNull);

var all = div.getElementsByTagNameNS('*', 'a');
assert.equal(all.length, 3);
assert.equal(all[0], aOne);
assert.equal(all[1], aTwo);
assert.equal(all[2], aNull);

var all = div.getElementsByTagNameNS(nsOne, '*');
assert.equal(all.length, 2);
assert.equal(all[0], aOne);
assert.equal(all[1], bOne);

var all = div.getElementsByTagNameNS('*', '*');
assert.equal(all.length, 6);
assert.equal(all[0], aOne);
assert.equal(all[1], aTwo);
assert.equal(all[2], aNull);
assert.equal(all[3], bOne);
assert.equal(all[4], bTwo);
assert.equal(all[5], bNull);

var all = div.getElementsByTagNameNS('*', 'A');
assert.equal(all.length, 0);
});

test('querySelectorAll', function() {
Expand Down
43 changes: 43 additions & 0 deletions test/js/Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,49 @@ suite('Element', function() {
assert.equal(as[1], a4);
});

test('getElementsByTagName with colon', function() {
var div = document.createElement('div');
div.innerHTML = '<a:b:c>0</a:b:c><a:b:c>1</a:b:c>';
var a0 = div.firstChild;
var a1 = div.lastChild;

var as = div.getElementsByTagName('a:b:c');
assert.equal(as.length, 2);
assert.equal(as[0], a0);
assert.equal(as.item(0), a0);
assert.equal(as[1], a1);
assert.equal(as.item(1), a1);
});

test('getElementsByTagName with namespace', function() {
var div = document.createElement('div');
div.innerHTML = '<a>0</a><a>1</a>';
var a0 = div.firstChild;
var a1 = div.lastChild;
var a2 = document.createElementNS('NS', 'a');
var a3 = document.createElementNS('NS', 'A');
div.appendChild(a2);
div.appendChild(a3);

var as = div.getElementsByTagName('a');
assert.equal(as.length, 3);
assert.equal(as[0], a0);
assert.equal(as.item(0), a0);
assert.equal(as[1], a1);
assert.equal(as.item(1), a1);
assert.equal(as[2], a2);
assert.equal(as.item(2), a2);

var as = div.getElementsByTagName('A');
assert.equal(as.length, 3);
assert.equal(as[0], a0);
assert.equal(as.item(0), a0);
assert.equal(as[1], a1);
assert.equal(as.item(1), a1);
assert.equal(as[2], a3);
assert.equal(as.item(2), a3);
});

test('getElementsByClassName', function() {
var div = document.createElement('div');
div.innerHTML = '<span class=a>0</span><span class=a>1</span>';
Expand Down

0 comments on commit 19076d5

Please sign in to comment.