diff --git a/lib/core/utils/get-selector.js b/lib/core/utils/get-selector.js
index 261c7b6530..32fbbe3d62 100644
--- a/lib/core/utils/get-selector.js
+++ b/lib/core/utils/get-selector.js
@@ -1,4 +1,5 @@
const escapeSelector = axe.utils.escapeSelector;
+let isXHTML;
function isUncommonClassName (className) {
return ![
@@ -27,7 +28,7 @@ function getDistinctClassList (elm) {
}
const commonNodes = [
- 'div', 'span', 'p',
+ 'div', 'span', 'p',
'b', 'i', 'u', 'strong', 'em',
'h2', 'h3'
];
@@ -78,7 +79,6 @@ const createSelector = {
// Get uncommon node names
getUncommonElm (elm, { isCommonElm, isCustomElm, nodeName }) {
if (!isCommonElm && !isCustomElm) {
- nodeName = escapeSelector(nodeName);
// Add [type] if nodeName is an input element
if (nodeName === 'input' && elm.hasAttribute('type')) {
nodeName += '[type="' + elm.type + '"]';
@@ -129,7 +129,12 @@ const createSelector = {
* recognize the element by (IDs, aria roles, custom element names, etc.)
*/
function getElmFeatures (elm, featureCount) {
- const nodeName = elm.nodeName.toLowerCase();
+ if (typeof isXHTML === 'undefined') {
+ isXHTML = axe.utils.isXHTML(document);
+ }
+ const nodeName = escapeSelector(isXHTML?
+ elm.localName
+ :elm.nodeName.toLowerCase());
const classList = Array.from(elm.classList) || [];
// Collect some props we need to build the selector
const props = {
@@ -181,7 +186,7 @@ axe.utils.getSelector = function createUniqueSelector (elm, options = {}) {
let { isUnique = false } = options;
const idSelector = createSelector.getElmId(elm);
const {
- featureCount = 2,
+ featureCount = 2,
minDepth = 1,
toRoot = false,
childSelectors = []
diff --git a/lib/core/utils/is-xhtml.js b/lib/core/utils/is-xhtml.js
new file mode 100644
index 0000000000..9e764982ed
--- /dev/null
+++ b/lib/core/utils/is-xhtml.js
@@ -0,0 +1,16 @@
+
+/**
+ * Determines if a document node is XHTML
+ * @method isXHTML
+ * @memberof axe.utils
+ * @instance
+ * @param {Node} doc a document node
+ * @return {Boolean}
+ */
+axe.utils.isXHTML = function (doc) {
+ 'use strict';
+ if (!doc.createElement) {
+ return false;
+ }
+ return doc.createElement('A').localName === 'A';
+};
diff --git a/test/core/utils/get-selector.js b/test/core/utils/get-selector.js
index 4ef9e0b570..d4f1db3cfd 100644
--- a/test/core/utils/get-selector.js
+++ b/test/core/utils/get-selector.js
@@ -177,6 +177,20 @@ describe('axe.utils.getSelector', function () {
assert.equal(result[0], node);
});
+ it('should work on complex namespaced elements', function () {
+ fixture.innerHTML = '' +
+ 'x' +
+ '' +
+ 'x' +
+ '' +
+ '';
+ var node = fixture.querySelector('m\\:ci');
+ var sel = axe.utils.getSelector(node);
+ var result = document.querySelectorAll(sel);
+ assert.lengthOf(result, 1);
+ assert.equal(result[0], node);
+ });
+
it('shouldn\'t fail if the node\'s parentNode doesnt have children, somehow (Firefox bug)', function () {
var sel = axe.utils.getSelector({
nodeName: 'a',
diff --git a/test/core/utils/is-xhtml.js b/test/core/utils/is-xhtml.js
new file mode 100644
index 0000000000..abe78610aa
--- /dev/null
+++ b/test/core/utils/is-xhtml.js
@@ -0,0 +1,21 @@
+describe('axe.utils.isXHTML', function () {
+ 'use strict';
+
+ it('should be a function', function () {
+ assert.isFunction(axe.utils.isXHTML);
+ });
+
+ it('should return true on any document that is XHTML', function () {
+ var doc = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null);
+ assert.isTrue(axe.utils.isXHTML(doc));
+ });
+
+ it('should return false on any document that is HTML', function () {
+ var doc = document.implementation.createHTMLDocument('Monkeys');
+ assert.isFalse(axe.utils.isXHTML(doc));
+ });
+
+ it('should return false on any document that is HTML - fixture', function () {
+ assert.isFalse(axe.utils.isXHTML(document));
+ });
+});
diff --git a/test/integration/full/get-selector/get-selector.js b/test/integration/full/get-selector/get-selector.js
new file mode 100644
index 0000000000..ab9eb1ef04
--- /dev/null
+++ b/test/integration/full/get-selector/get-selector.js
@@ -0,0 +1,12 @@
+
+describe('axe.utils.getSelector', function () {
+ 'use strict';
+ it('should work on namespaced elements', function () {
+ var fixture = document.querySelector('#fixture');
+ var node = fixture.firstElementChild;
+ var sel = axe.utils.getSelector(node);
+ var result = document.querySelectorAll(sel);
+ assert.lengthOf(result, 1);
+ assert.equal(result[0], node);
+ });
+});
diff --git a/test/integration/full/get-selector/get-selector.xhtml b/test/integration/full/get-selector/get-selector.xhtml
new file mode 100644
index 0000000000..d9139cb2e3
--- /dev/null
+++ b/test/integration/full/get-selector/get-selector.xhtml
@@ -0,0 +1,25 @@
+
+
+ axe.utils.getSelector test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/integration/full/is-xhtml/is-xhtml.js b/test/integration/full/is-xhtml/is-xhtml.js
new file mode 100644
index 0000000000..1837bdc1ab
--- /dev/null
+++ b/test/integration/full/is-xhtml/is-xhtml.js
@@ -0,0 +1,9 @@
+
+describe('axe.utils.isXHTML', function () {
+ 'use strict';
+
+ it('should return true on any document that is XHTML', function () {
+ assert.isTrue(axe.utils.isXHTML(document));
+ });
+
+});
diff --git a/test/integration/full/is-xhtml/is-xhtml.xhtml b/test/integration/full/is-xhtml/is-xhtml.xhtml
new file mode 100644
index 0000000000..c0b6e4f70a
--- /dev/null
+++ b/test/integration/full/is-xhtml/is-xhtml.xhtml
@@ -0,0 +1,24 @@
+
+
+ axe.utils.isXHTML test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+