Skip to content

Commit aac57c0

Browse files
committed
feat: Add dom.getComposedParent function
1 parent 99e8b73 commit aac57c0

File tree

4 files changed

+93
-6
lines changed

4 files changed

+93
-6
lines changed

lib/commons/dom/find-up.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,8 @@ dom.findUp = function (element, target) {
2222
return null;
2323
}
2424

25-
parent = (element.assignedSlot) ? element.assignedSlot : element.parentNode;
26-
if (parent.nodeType === 11) {
27-
parent = parent.host;
28-
}
2925
// recursively walk up the DOM, checking each parent node
26+
parent = dom.getComposedParent(element);
3027
while (parent && matches.indexOf(parent) === -1) {
3128
parent = (parent.assignedSlot) ? parent.assignedSlot : parent.parentNode;
3229
if (parent && parent.nodeType === 11) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*global dom */
2+
/**
3+
* Get an element's parent in the composed tree
4+
* @param DOMNode Element
5+
* @return DOMNode Parent element
6+
*/
7+
dom.getComposedParent = function getComposedParent (element) {
8+
if (element.assignedSlot) {
9+
return element.assignedSlot; // content of a shadow DOM slot
10+
} else if (element.parentNode) {
11+
var parentNode = element.parentNode;
12+
if (parentNode.nodeType === 1) {
13+
return parentNode; // Regular node
14+
} else if (parentNode.host) {
15+
return parentNode.host; // Shadow root
16+
}
17+
}
18+
return null; // Root node
19+
};

lib/commons/dom/is-visible.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ dom.isVisible = function (el, screenReader, recursed) {
6464
return false;
6565
}
6666

67-
parent = (el.assignedSlot) ? el.assignedSlot : el.parentNode;
68-
67+
parent = dom.getComposedParent(el);
6968
if (parent) {
7069
return dom.isVisible(parent, screenReader, true);
7170
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* global xit */
2+
describe('dom.getComposedParent', function () {
3+
'use strict';
4+
var getComposedParent = axe.commons.dom.getComposedParent;
5+
var fixture = document.getElementById('fixture');
6+
var shadowSupport = document.body && typeof document.body.attachShadow === 'function';
7+
8+
afterEach(function () {
9+
fixture.innerHTML = '';
10+
});
11+
12+
it('returns the parentNode normally', function () {
13+
fixture.innerHTML = '<div id="parent"><div id="target"></div></div>';
14+
15+
var actual = getComposedParent(document.getElementById('target'));
16+
assert.instanceOf(actual, Node);
17+
assert.equal(actual, document.getElementById('parent'));
18+
});
19+
20+
it('returns null from the documentElement', function () {
21+
assert.isNull(
22+
getComposedParent(document.documentElement)
23+
);
24+
});
25+
26+
(shadowSupport ? it : xit)('returns the slot node for slotted content', function () {
27+
fixture.innerHTML = '<div id="shadow"><div id="target"></div></div>';
28+
var shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' });
29+
shadowRoot.innerHTML = '<div id="grand-parent">' +
30+
'<slot id="parent"></slot>' +
31+
'</div>';
32+
33+
var actual = getComposedParent(fixture.querySelector('#target'));
34+
assert.instanceOf(actual, Node);
35+
assert.equal(actual, shadowRoot.querySelector('#parent'));
36+
});
37+
38+
(shadowSupport ? it : xit)('returns explicitly slotted nodes', function () {
39+
fixture.innerHTML = '<div id="shadow"><div id="target" slot="bar"></div></div>';
40+
var shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' });
41+
shadowRoot.innerHTML = '<div id="grand-parent">' +
42+
'<slot name="foo"></slot>' +
43+
'<slot id="parent" name="bar"></slot>' +
44+
'</div>';
45+
46+
var actual = getComposedParent(fixture.querySelector('#target'));
47+
assert.instanceOf(actual, Node);
48+
assert.equal(actual, shadowRoot.querySelector('#parent'));
49+
});
50+
51+
(shadowSupport ? it : xit)('returns elements within a shadow tree', function () {
52+
fixture.innerHTML = '<div id="shadow"> content </div>';
53+
var shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' });
54+
shadowRoot.innerHTML = '<div id="parent">' +
55+
'<slot id="target"></slot>' +
56+
'</div>';
57+
58+
var actual = getComposedParent(shadowRoot.querySelector('#target'));
59+
assert.instanceOf(actual, Node);
60+
assert.equal(actual, shadowRoot.querySelector('#parent'));
61+
});
62+
63+
(shadowSupport ? it : xit)('returns the host when it reaches the shadow root', function () {
64+
fixture.innerHTML = '<div id="parent"> content </div>';
65+
var shadowRoot = document.getElementById('parent').attachShadow({ mode: 'open' });
66+
shadowRoot.innerHTML = '<div id="target"> <slot></slot> </div>';
67+
68+
var actual = getComposedParent(shadowRoot.querySelector('#target'));
69+
assert.instanceOf(actual, Node);
70+
assert.equal(actual, fixture.querySelector('#parent'));
71+
});
72+
});

0 commit comments

Comments
 (0)