Skip to content

Commit

Permalink
observe shadow root child nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
valdrinkoshi committed Aug 6, 2016
1 parent 9cf168e commit 3c0dba7
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 2 deletions.
37 changes: 35 additions & 2 deletions inert.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,44 @@ class InertRoot {
// Make all focusable elements in the subtree unfocusable and add them to _managedNodes
this._makeSubtreeUnfocusable(this._rootElement);

const boundOnMutation = this._onMutation.bind(this);
// Watch for:
// - any additions in the subtree: make them unfocusable too
// - any removals from the subtree: remove them from this inert root's managed nodes
// - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable element,
// make that node a managed node.
this._observer = new MutationObserver(this._onMutation.bind(this));
this._observer = new MutationObserver(boundOnMutation);
this._observer.observe(this._rootElement, { attributes: true, childList: true, subtree: true });

// Watch for:
// - any additions in the shadowRoot subtree: make them unfocusable too
// - any removals from the shadowRoot subtree: remove them from this inert root's managed nodes
const rootObserver = new MutationObserver(boundOnMutation);
const shadowRoot = this._rootElement.shadowRoot || this._rootElement.webkitShadowRoot;
if (shadowRoot) {
rootObserver.observe(shadowRoot, { childList: true, subtree: true });
} else {
// Might create a shadowRoot in the future, either by attachShadow
// (ShadowDOM v1) or by createShadowRoot (ShadowDOM v0), so we patch both
// of them.
const attachShadowFn = this._rootElement.attachShadow;
if (attachShadowFn) {
this._rootElement.attachShadow = function patchedAttachShadow() {
var shadowRoot = attachShadowFn.apply(this, arguments);
rootObserver.observe(shadowRoot, { childList: true, subtree: true });
return shadowRoot;
};
}
const createShadowRootFn = this._rootElement.createShadowRoot;
if (createShadowRootFn) {
this._rootElement.createShadowRoot = function patchedCreateShadowRoot() {
var shadowRoot = createShadowRootFn.apply(this, arguments);
rootObserver.observe(shadowRoot, { childList: true, subtree: true });
return shadowRoot;
};
}
}
this._rootObserver = rootObserver;
}

/**
Expand All @@ -86,6 +117,9 @@ class InertRoot {
this._observer.disconnect();
delete this._observer;

this._rootObserver.disconnect();
delete this._rootObserver;

if (this._rootElement)
this._rootElement.removeAttribute('aria-hidden');
delete this._rootElement;
Expand Down Expand Up @@ -599,7 +633,6 @@ function addInertStyle(node) {
node.appendChild(style);
}


let inertManager = new InertManager(document);
Object.defineProperty(Element.prototype, 'inert', {
enumerable: true,
Expand Down
59 changes: 59 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,34 @@ describe('Basic', function() {
expect(isUnfocusable(shadowButton)).to.equal(true);
});

it('should apply to elements added to shadow trees later in time', function(done) {
host.inert = true;
const shadowButton = document.createElement('button');
shadowButton.textContent = 'Shadow button';
host.shadowRoot.appendChild(shadowButton);
// Give time to mutation observers.
setTimeout(function() {
expect(isUnfocusable(shadowButton)).to.equal(true);
done();
});
});

it('should apply to elements that create shadow tree later in time', function(done) {
fixture.removeChild(host);
host = document.createElement('div');
fixture.appendChild(host);
host.inert = true;
const shadowRoot = host.createShadowRoot();
const shadowButton = document.createElement('button');
shadowButton.textContent = 'Shadow button';
shadowRoot.appendChild(shadowButton);
// Give time to mutation observers.
setTimeout(function() {
expect(isUnfocusable(shadowButton)).to.equal(true);
done();
});
});

it('should apply inert styles inside shadow trees', function() {
const shadowButton = document.createElement('button');
shadowButton.textContent = 'Shadow button';
Expand All @@ -165,6 +193,7 @@ describe('Basic', function() {
fixture.inert = true;
expect(isUnfocusable(distributedButton)).to.equal(true);
});

});

describe('ShadowDOM v1', function() {
Expand Down Expand Up @@ -196,6 +225,36 @@ describe('Basic', function() {
expect(isUnfocusable(shadowButton)).to.equal(true);
});

it('should apply to elements added to shadow trees later in time', function(done) {
host.inert = true;
const shadowButton = document.createElement('button');
shadowButton.textContent = 'Shadow button';
host.shadowRoot.appendChild(shadowButton);
// Give time to mutation observers.
setTimeout(function() {
expect(isUnfocusable(shadowButton)).to.equal(true);
done();
});
});

it('should apply to elements that create shadow tree later in time', function(done) {
fixture.removeChild(host);
host = document.createElement('div');
fixture.appendChild(host);
host.inert = true;
const shadowRoot = host.attachShadow({
mode: 'open'
});
const shadowButton = document.createElement('button');
shadowButton.textContent = 'Shadow button';
shadowRoot.appendChild(shadowButton);
// Give time to mutation observers.
setTimeout(function() {
expect(isUnfocusable(shadowButton)).to.equal(true);
done();
});
});

it('should apply inert styles inside shadow trees', function() {
const shadowButton = document.createElement('button');
shadowButton.textContent = 'Shadow button';
Expand Down

0 comments on commit 3c0dba7

Please sign in to comment.