wicg-inert
Advanced tools
Comparing version 1.1.6 to 2.0.0
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(factory()); | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(factory()); | ||
}(this, (function () { 'use strict'; | ||
/** | ||
* Determine if a DOM element matches a CSS selector | ||
* | ||
* @param {Element} elem | ||
* @param {String} selector | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
function matches(elem, selector) { | ||
// Vendor-specific implementations of `Element.prototype.matches()`. | ||
var proto = window.Element.prototype; | ||
var nativeMatches = proto.matches || | ||
proto.mozMatchesSelector || | ||
proto.msMatchesSelector || | ||
proto.oMatchesSelector || | ||
proto.webkitMatchesSelector; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
if (!elem || elem.nodeType !== 1) { | ||
return false; | ||
} | ||
/** | ||
* This work is licensed under the W3C Software and Document License | ||
* (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). | ||
*/ | ||
var parentElem = elem.parentNode; | ||
// use native 'matches' | ||
if (nativeMatches) { | ||
return nativeMatches.call(elem, selector); | ||
} | ||
// native support for `matches` is missing and a fallback is required | ||
var nodes = parentElem.querySelectorAll(selector); | ||
var len = nodes.length; | ||
for (var i = 0; i < len; i++) { | ||
if (nodes[i] === elem) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
/** | ||
* Expose `matches` | ||
*/ | ||
var index = matches; | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
}; | ||
var createClass = function () { | ||
function defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
return function (Constructor, protoProps, staticProps) { | ||
if (protoProps) defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
}; | ||
}(); | ||
/** | ||
* This work is licensed under the W3C Software and Document License | ||
* (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). | ||
*/ | ||
(function (document) { | ||
// Convenience function for converting NodeLists. | ||
@@ -115,3 +46,3 @@ /** @type {function(number,number):Array} */ | ||
function InertRoot(rootElement, inertManager) { | ||
classCallCheck(this, InertRoot); | ||
_classCallCheck(this, InertRoot); | ||
@@ -154,3 +85,3 @@ /** @type {InertManager} */ | ||
createClass(InertRoot, [{ | ||
_createClass(InertRoot, [{ | ||
key: 'destructor', | ||
@@ -170,27 +101,6 @@ value: function destructor() { | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
this._managedNodes.forEach(function (inertNode) { | ||
this._unmanageNode(inertNode.node); | ||
}, this); | ||
try { | ||
for (var _iterator = this._managedNodes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var inertNode = _step.value; | ||
this._unmanageNode(inertNode.node); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
this._managedNodes = null; | ||
@@ -220,3 +130,4 @@ | ||
var activeElement = document.activeElement; | ||
if (!contains(document.body, startNode)) { | ||
if (!document.body.contains(startNode)) { | ||
// startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement. | ||
@@ -236,4 +147,10 @@ var node = startNode; | ||
} | ||
if (contains(startNode, activeElement)) { | ||
if (startNode.contains(activeElement)) { | ||
activeElement.blur(); | ||
// In IE11, if an element is already focused, and then set to tabindex=-1 | ||
// calling blur() will not actually move the focus. | ||
// To work around this we call focus() on the body instead. | ||
if (activeElement === document.activeElement) { | ||
document.body.focus(); | ||
} | ||
} | ||
@@ -259,3 +176,3 @@ } | ||
if (index(node, _focusableElementsString) || node.hasAttribute('tabindex')) { | ||
if (node.matches(_focusableElementsString) || node.hasAttribute('tabindex')) { | ||
this._manageNode(node); | ||
@@ -323,26 +240,5 @@ } | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
try { | ||
for (var _iterator2 = inertSubroot.managedNodes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var savedInertNode = _step2.value; | ||
this._manageNode(savedInertNode.node); | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
inertSubroot.managedNodes.forEach(function (savedInertNode) { | ||
this._manageNode(savedInertNode.node); | ||
}, this); | ||
} | ||
@@ -359,120 +255,35 @@ | ||
value: function _onMutation(records, self) { | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
records.forEach(function (record) { | ||
var target = record.target; | ||
if (record.type === 'childList') { | ||
// Manage added nodes | ||
slice.call(record.addedNodes).forEach(function (node) { | ||
this._makeSubtreeUnfocusable(node); | ||
}, this); | ||
try { | ||
for (var _iterator3 = records[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var record = _step3.value; | ||
var target = record.target; | ||
if (record.type === 'childList') { | ||
// Manage added nodes | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
try { | ||
for (var _iterator4 = slice.call(record.addedNodes)[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var node = _step4.value; | ||
this._makeSubtreeUnfocusable(node); | ||
// Un-manage removed nodes | ||
slice.call(record.removedNodes).forEach(function (node) { | ||
this._unmanageSubtree(node); | ||
}, this); | ||
} else if (record.type === 'attributes') { | ||
if (record.attributeName === 'tabindex') { | ||
// Re-initialise inert node if tabindex changes | ||
this._manageNode(target); | ||
} else if (target !== this._rootElement && record.attributeName === 'inert' && target.hasAttribute('inert')) { | ||
// If a new inert root is added, adopt its managed nodes and make sure it knows about the | ||
// already managed nodes from this inert subroot. | ||
this._adoptInertRoot(target); | ||
var inertSubroot = this._inertManager.getInertRoot(target); | ||
this._managedNodes.forEach(function (managedNode) { | ||
if (target.contains(managedNode.node)) { | ||
inertSubroot._manageNode(managedNode.node); | ||
} | ||
// Un-manage removed nodes | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
} | ||
var _iteratorNormalCompletion5 = true; | ||
var _didIteratorError5 = false; | ||
var _iteratorError5 = undefined; | ||
try { | ||
for (var _iterator5 = slice.call(record.removedNodes)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
var _node = _step5.value; | ||
this._unmanageSubtree(_node); | ||
} | ||
} catch (err) { | ||
_didIteratorError5 = true; | ||
_iteratorError5 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion5 && _iterator5.return) { | ||
_iterator5.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError5) { | ||
throw _iteratorError5; | ||
} | ||
} | ||
} | ||
} else if (record.type === 'attributes') { | ||
if (record.attributeName === 'tabindex') { | ||
// Re-initialise inert node if tabindex changes | ||
this._manageNode(target); | ||
} else if (target !== this._rootElement && record.attributeName === 'inert' && target.hasAttribute('inert')) { | ||
// If a new inert root is added, adopt its managed nodes and make sure it knows about the | ||
// already managed nodes from this inert subroot. | ||
this._adoptInertRoot(target); | ||
var inertSubroot = this._inertManager.getInertRoot(target); | ||
var _iteratorNormalCompletion6 = true; | ||
var _didIteratorError6 = false; | ||
var _iteratorError6 = undefined; | ||
try { | ||
for (var _iterator6 = this._managedNodes[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { | ||
var managedNode = _step6.value; | ||
if (contains(target, managedNode.node)) { | ||
inertSubroot._manageNode(managedNode.node); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError6 = true; | ||
_iteratorError6 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion6 && _iterator6.return) { | ||
_iterator6.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError6) { | ||
throw _iteratorError6; | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
}, this); | ||
} | ||
}, { | ||
key: 'managedNodes', | ||
get: function get$$1() { | ||
get: function get() { | ||
return new Set(this._managedNodes); | ||
@@ -485,3 +296,3 @@ } | ||
key: 'hasSavedAriaHidden', | ||
get: function get$$1() { | ||
get: function get() { | ||
return '_savedAriaHidden' in this; | ||
@@ -494,3 +305,3 @@ } | ||
key: 'savedAriaHidden', | ||
set: function set$$1(ariaHidden) { | ||
set: function set(ariaHidden) { | ||
this._savedAriaHidden = ariaHidden; | ||
@@ -501,6 +312,7 @@ } | ||
, | ||
get: function get$$1() { | ||
get: function get() { | ||
return this._savedAriaHidden; | ||
} | ||
}]); | ||
return InertRoot; | ||
@@ -531,3 +343,3 @@ }(); | ||
function InertNode(node, inertRoot) { | ||
classCallCheck(this, InertNode); | ||
_classCallCheck(this, InertNode); | ||
@@ -559,3 +371,3 @@ /** @type {Node} */ | ||
createClass(InertNode, [{ | ||
_createClass(InertNode, [{ | ||
key: 'destructor', | ||
@@ -610,3 +422,3 @@ value: function destructor() { | ||
var node = this.node; | ||
if (index(node, _focusableElementsString)) { | ||
if (node.matches(_focusableElementsString)) { | ||
if (node.tabIndex === -1 && this.hasSavedTabIndex) { | ||
@@ -660,3 +472,3 @@ return; | ||
key: 'destroyed', | ||
get: function get$$1() { | ||
get: function get() { | ||
return this._destroyed; | ||
@@ -666,3 +478,3 @@ } | ||
key: 'hasSavedTabIndex', | ||
get: function get$$1() { | ||
get: function get() { | ||
return '_savedTabIndex' in this; | ||
@@ -675,3 +487,3 @@ } | ||
key: 'node', | ||
get: function get$$1() { | ||
get: function get() { | ||
this._throwIfDestroyed(); | ||
@@ -685,3 +497,3 @@ return this._node; | ||
key: 'savedTabIndex', | ||
set: function set$$1(tabIndex) { | ||
set: function set(tabIndex) { | ||
this._throwIfDestroyed(); | ||
@@ -693,3 +505,3 @@ this._savedTabIndex = tabIndex; | ||
, | ||
get: function get$$1() { | ||
get: function get() { | ||
this._throwIfDestroyed(); | ||
@@ -699,2 +511,3 @@ return this._savedTabIndex; | ||
}]); | ||
return InertNode; | ||
@@ -719,3 +532,3 @@ }(); | ||
function InertManager(document) { | ||
classCallCheck(this, InertManager); | ||
_classCallCheck(this, InertManager); | ||
@@ -765,3 +578,3 @@ if (!document) { | ||
createClass(InertManager, [{ | ||
_createClass(InertManager, [{ | ||
key: 'setInert', | ||
@@ -780,3 +593,3 @@ value: function setInert(root, inert) { | ||
// Ensure inert styles are added there. | ||
if (!contains(this._document.body, root)) { | ||
if (!this._document.body.contains(root)) { | ||
var parent = root.parentNode; | ||
@@ -877,29 +690,7 @@ while (parent) { | ||
var inertElements = slice.call(this._document.querySelectorAll('[inert]')); | ||
var _iteratorNormalCompletion7 = true; | ||
var _didIteratorError7 = false; | ||
var _iteratorError7 = undefined; | ||
inertElements.forEach(function (inertElement) { | ||
this.setInert(inertElement, true); | ||
}, this); | ||
try { | ||
for (var _iterator7 = inertElements[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { | ||
var inertElement = _step7.value; | ||
this.setInert(inertElement, true); | ||
} | ||
// Comment this out to use programmatic API only. | ||
} catch (err) { | ||
_didIteratorError7 = true; | ||
_iteratorError7 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion7 && _iterator7.return) { | ||
_iterator7.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError7) { | ||
throw _iteratorError7; | ||
} | ||
} | ||
} | ||
// Comment this out to use programmatic API only. | ||
this._observer.observe(this._document.body, { attributes: true, subtree: true, childList: true }); | ||
@@ -917,94 +708,31 @@ } | ||
value: function _watchForInert(records, self) { | ||
var _iteratorNormalCompletion8 = true; | ||
var _didIteratorError8 = false; | ||
var _iteratorError8 = undefined; | ||
try { | ||
for (var _iterator8 = records[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { | ||
var record = _step8.value; | ||
switch (record.type) { | ||
case 'childList': | ||
var _iteratorNormalCompletion9 = true; | ||
var _didIteratorError9 = false; | ||
var _iteratorError9 = undefined; | ||
try { | ||
for (var _iterator9 = slice.call(record.addedNodes)[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { | ||
var node = _step9.value; | ||
if (node.nodeType !== Node.ELEMENT_NODE) { | ||
continue; | ||
} | ||
var inertElements = slice.call(node.querySelectorAll('[inert]')); | ||
if (index(node, '[inert]')) { | ||
inertElements.unshift(node); | ||
} | ||
var _iteratorNormalCompletion10 = true; | ||
var _didIteratorError10 = false; | ||
var _iteratorError10 = undefined; | ||
try { | ||
for (var _iterator10 = inertElements[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) { | ||
var inertElement = _step10.value; | ||
this.setInert(inertElement, true); | ||
} | ||
} catch (err) { | ||
_didIteratorError10 = true; | ||
_iteratorError10 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion10 && _iterator10.return) { | ||
_iterator10.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError10) { | ||
throw _iteratorError10; | ||
} | ||
} | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError9 = true; | ||
_iteratorError9 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion9 && _iterator9.return) { | ||
_iterator9.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError9) { | ||
throw _iteratorError9; | ||
} | ||
} | ||
records.forEach(function (record) { | ||
switch (record.type) { | ||
case 'childList': | ||
slice.call(record.addedNodes).forEach(function (node) { | ||
if (node.nodeType !== Node.ELEMENT_NODE) { | ||
return; | ||
} | ||
break; | ||
case 'attributes': | ||
if (record.attributeName !== 'inert') { | ||
continue; | ||
var inertElements = slice.call(node.querySelectorAll('[inert]')); | ||
if (node.matches('[inert]')) { | ||
inertElements.unshift(node); | ||
} | ||
var target = record.target; | ||
var inert = target.hasAttribute('inert'); | ||
this.setInert(target, inert); | ||
break; | ||
} | ||
inertElements.forEach(function (inertElement) { | ||
this.setInert(inertElement, true); | ||
}, this); | ||
}, this); | ||
break; | ||
case 'attributes': | ||
if (record.attributeName !== 'inert') { | ||
return; | ||
} | ||
var target = record.target; | ||
var inert = target.hasAttribute('inert'); | ||
this.setInert(target, inert); | ||
break; | ||
} | ||
} catch (err) { | ||
_didIteratorError8 = true; | ||
_iteratorError8 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion8 && _iterator8.return) { | ||
_iterator8.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError8) { | ||
throw _iteratorError8; | ||
} | ||
} | ||
} | ||
}, this); | ||
} | ||
}]); | ||
return InertManager; | ||
@@ -1089,16 +817,2 @@ }(); | ||
/** | ||
* `Node#contains()` polyfill. | ||
* | ||
* See: http://compatibility.shwups-cms.ch/en/polyfills/?&id=1 | ||
* | ||
* @param {Node} node | ||
* @param {Node} other | ||
* @return {Boolean} | ||
* @public | ||
*/ | ||
function contains(node, other) { | ||
return other && (node === other || !!(node.compareDocumentPosition(other) & 16)); | ||
} | ||
var inertManager = new InertManager(document); | ||
@@ -1108,11 +822,10 @@ | ||
enumerable: true, | ||
get: function get$$1() { | ||
get: function get() { | ||
return this.hasAttribute('inert'); | ||
}, | ||
set: function set$$1(inert) { | ||
set: function set(inert) { | ||
inertManager.setInert(this, inert); | ||
} | ||
}); | ||
})(document); | ||
}))); |
@@ -1,1 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t():"function"==typeof define&&define.amd?define(t):t()}(0,function(){"use strict";function e(e,t){var n=window.Element.prototype,r=n.matches||n.mozMatchesSelector||n.msMatchesSelector||n.oMatchesSelector||n.webkitMatchesSelector;if(!e||1!==e.nodeType)return!1;var i=e.parentNode;if(r)return r.call(e,t);for(var o=i.querySelectorAll(t),a=o.length,s=0;s<a;s++)if(o[s]===e)return!0;return!1}var t=e,n=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();!function(e){function i(e,t,n){if(e.nodeType==Node.ELEMENT_NODE){var r=e;t&&t(r);var o=r.shadowRoot||r.webkitShadowRoot;if(o)return void i(o,t,o);if("content"==r.localName){for(var a=r,s=a.getDistributedNodes?a.getDistributedNodes():[],d=0;d<s.length;d++)i(s[d],t,n);return}if("slot"==r.localName){for(var u=r,l=u.assignedNodes?u.assignedNodes({flatten:!0}):[],h=0;h<l.length;h++)i(l[h],t,n);return}}for(var f=e.firstChild;null!=f;)i(f,t,n),f=f.nextSibling}function o(t){if(!t.querySelector("style#inert-style")){var n=e.createElement("style");n.setAttribute("id","inert-style"),n.textContent="\n[inert] {\n pointer-events: none;\n cursor: default;\n}\n\n[inert], [inert] * {\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n}\n",t.appendChild(n)}}function a(e,t){return t&&(e===t||!!(16&e.compareDocumentPosition(t)))}var s=Array.prototype.slice,d=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]"].join(","),u=function(){function o(e,t){n(this,o),this._inertManager=t,this._rootElement=e,this._managedNodes=new Set([]),this._rootElement.hasAttribute("aria-hidden")&&(this._savedAriaHidden=this._rootElement.getAttribute("aria-hidden")),this._rootElement.setAttribute("aria-hidden","true"),this._makeSubtreeUnfocusable(this._rootElement),this._observer=new MutationObserver(this._onMutation.bind(this)),this._observer.observe(this._rootElement,{attributes:!0,childList:!0,subtree:!0})}return r(o,[{key:"destructor",value:function(){this._observer.disconnect(),this._observer=null,this._rootElement&&(this.hasSavedAriaHidden?this._rootElement.setAttribute("aria-hidden",this.savedAriaHidden):this._rootElement.removeAttribute("aria-hidden")),this._rootElement=null;var e=!0,t=!1,n=void 0;try{for(var r,i=this._managedNodes[Symbol.iterator]();!(e=(r=i.next()).done);e=!0){var o=r.value;this._unmanageNode(o.node)}}catch(e){t=!0,n=e}finally{try{!e&&i.return&&i.return()}finally{if(t)throw n}}this._managedNodes=null,this._inertManager=null}},{key:"_makeSubtreeUnfocusable",value:function(t){var n=this;i(t,function(e){return n._visitNode(e)});var r=e.activeElement;if(!a(e.body,t)){for(var o=t,s=void 0;o;){if(o.nodeType===Node.DOCUMENT_FRAGMENT_NODE){s=o;break}o=o.parentNode}s&&(r=s.activeElement)}a(t,r)&&r.blur()}},{key:"_visitNode",value:function(e){e.nodeType===Node.ELEMENT_NODE&&(e!==this._rootElement&&e.hasAttribute("inert")&&this._adoptInertRoot(e),(t(e,d)||e.hasAttribute("tabindex"))&&this._manageNode(e))}},{key:"_manageNode",value:function(e){var t=this._inertManager.register(e,this);this._managedNodes.add(t)}},{key:"_unmanageNode",value:function(e){var t=this._inertManager.deregister(e,this);t&&this._managedNodes.delete(t)}},{key:"_unmanageSubtree",value:function(e){var t=this;i(e,function(e){return t._unmanageNode(e)})}},{key:"_adoptInertRoot",value:function(e){var t=this._inertManager.getInertRoot(e);t||(this._inertManager.setInert(e,!0),t=this._inertManager.getInertRoot(e));var n=!0,r=!1,i=void 0;try{for(var o,a=t.managedNodes[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;this._manageNode(s.node)}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}}},{key:"_onMutation",value:function(e,t){var n=!0,r=!1,i=void 0;try{for(var o,d=e[Symbol.iterator]();!(n=(o=d.next()).done);n=!0){var u=o.value,l=u.target;if("childList"===u.type){var h=!0,f=!1,c=void 0;try{for(var v,y=s.call(u.addedNodes)[Symbol.iterator]();!(h=(v=y.next()).done);h=!0){var b=v.value;this._makeSubtreeUnfocusable(b)}}catch(e){f=!0,c=e}finally{try{!h&&y.return&&y.return()}finally{if(f)throw c}}var _=!0,m=!1,g=void 0;try{for(var N,w=s.call(u.removedNodes)[Symbol.iterator]();!(_=(N=w.next()).done);_=!0){var E=N.value;this._unmanageSubtree(E)}}catch(e){m=!0,g=e}finally{try{!_&&w.return&&w.return()}finally{if(m)throw g}}}else if("attributes"===u.type)if("tabindex"===u.attributeName)this._manageNode(l);else if(l!==this._rootElement&&"inert"===u.attributeName&&l.hasAttribute("inert")){this._adoptInertRoot(l);var I=this._inertManager.getInertRoot(l),p=!0,k=!1,x=void 0;try{for(var S,A=this._managedNodes[Symbol.iterator]();!(p=(S=A.next()).done);p=!0){var M=S.value;a(l,M.node)&&I._manageNode(M.node)}}catch(e){k=!0,x=e}finally{try{!p&&A.return&&A.return()}finally{if(k)throw x}}}}}catch(e){r=!0,i=e}finally{try{!n&&d.return&&d.return()}finally{if(r)throw i}}}},{key:"managedNodes",get:function(){return new Set(this._managedNodes)}},{key:"hasSavedAriaHidden",get:function(){return"_savedAriaHidden"in this}},{key:"savedAriaHidden",set:function(e){this._savedAriaHidden=e},get:function(){return this._savedAriaHidden}}]),o}(),l=function(){function e(t,r){n(this,e),this._node=t,this._overrodeFocusMethod=!1,this._inertRoots=new Set([r]),this._destroyed=!1,this.ensureUntabbable()}return r(e,[{key:"destructor",value:function(){this._throwIfDestroyed(),this._node&&(this.hasSavedTabIndex?this._node.setAttribute("tabindex",this.savedTabIndex):this._node.removeAttribute("tabindex"),this._overrodeFocusMethod&&delete this._node.focus),this._node=null,this._inertRoots=null,this._destroyed=!0}},{key:"_throwIfDestroyed",value:function(){if(this.destroyed)throw new Error("Trying to access destroyed InertNode")}},{key:"ensureUntabbable",value:function(){var e=this.node;if(t(e,d)){if(-1===e.tabIndex&&this.hasSavedTabIndex)return;e.hasAttribute("tabindex")&&(this._savedTabIndex=e.tabIndex),e.setAttribute("tabindex","-1"),e.nodeType===Node.ELEMENT_NODE&&(e.focus=function(){},this._overrodeFocusMethod=!0)}else e.hasAttribute("tabindex")&&(this._savedTabIndex=e.tabIndex,e.removeAttribute("tabindex"))}},{key:"addInertRoot",value:function(e){this._throwIfDestroyed(),this._inertRoots.add(e)}},{key:"removeInertRoot",value:function(e){this._throwIfDestroyed(),this._inertRoots.delete(e),0===this._inertRoots.size&&this.destructor()}},{key:"destroyed",get:function(){return this._destroyed}},{key:"hasSavedTabIndex",get:function(){return"_savedTabIndex"in this}},{key:"node",get:function(){return this._throwIfDestroyed(),this._node}},{key:"savedTabIndex",set:function(e){this._throwIfDestroyed(),this._savedTabIndex=e},get:function(){return this._throwIfDestroyed(),this._savedTabIndex}}]),e}(),h=function(){function e(t){if(n(this,e),!t)throw new Error("Missing required argument; InertManager needs to wrap a document.");this._document=t,this._managedNodes=new Map,this._inertRoots=new Map,this._observer=new MutationObserver(this._watchForInert.bind(this)),o(t.head||t.body||t.documentElement),"loading"===t.readyState?t.addEventListener("DOMContentLoaded",this._onDocumentLoaded.bind(this)):this._onDocumentLoaded()}return r(e,[{key:"setInert",value:function(e,t){if(t){if(this._inertRoots.has(e))return;var n=new u(e,this);if(e.setAttribute("inert",""),this._inertRoots.set(e,n),!a(this._document.body,e))for(var r=e.parentNode;r;)11===r.nodeType&&o(r),r=r.parentNode}else{if(!this._inertRoots.has(e))return;this._inertRoots.get(e).destructor(),this._inertRoots.delete(e),e.removeAttribute("inert")}}},{key:"getInertRoot",value:function(e){return this._inertRoots.get(e)}},{key:"register",value:function(e,t){var n=this._managedNodes.get(e);return void 0!==n?(n.addInertRoot(t),n.ensureUntabbable()):n=new l(e,t),this._managedNodes.set(e,n),n}},{key:"deregister",value:function(e,t){var n=this._managedNodes.get(e);return n?(n.removeInertRoot(t),n.destroyed&&this._managedNodes.delete(e),n):null}},{key:"_onDocumentLoaded",value:function(){var e=s.call(this._document.querySelectorAll("[inert]")),t=!0,n=!1,r=void 0;try{for(var i,o=e[Symbol.iterator]();!(t=(i=o.next()).done);t=!0){var a=i.value;this.setInert(a,!0)}}catch(e){n=!0,r=e}finally{try{!t&&o.return&&o.return()}finally{if(n)throw r}}this._observer.observe(this._document.body,{attributes:!0,subtree:!0,childList:!0})}},{key:"_watchForInert",value:function(e,n){var r=!0,i=!1,o=void 0;try{for(var a,d=e[Symbol.iterator]();!(r=(a=d.next()).done);r=!0){var u=a.value;switch(u.type){case"childList":var l=!0,h=!1,f=void 0;try{for(var c,v=s.call(u.addedNodes)[Symbol.iterator]();!(l=(c=v.next()).done);l=!0){var y=c.value;if(y.nodeType===Node.ELEMENT_NODE){var b=s.call(y.querySelectorAll("[inert]"));t(y,"[inert]")&&b.unshift(y);var _=!0,m=!1,g=void 0;try{for(var N,w=b[Symbol.iterator]();!(_=(N=w.next()).done);_=!0){var E=N.value;this.setInert(E,!0)}}catch(e){m=!0,g=e}finally{try{!_&&w.return&&w.return()}finally{if(m)throw g}}}}}catch(e){h=!0,f=e}finally{try{!l&&v.return&&v.return()}finally{if(h)throw f}}break;case"attributes":if("inert"!==u.attributeName)continue;var I=u.target,p=I.hasAttribute("inert");this.setInert(I,p)}}}catch(e){i=!0,o=e}finally{try{!r&&d.return&&d.return()}finally{if(i)throw o}}}}]),e}(),f=new h(e);Object.defineProperty(Element.prototype,"inert",{enumerable:!0,get:function(){return this.hasAttribute("inert")},set:function(e){f.setInert(this,e)}})}(document)}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t():"function"==typeof define&&define.amd?define(t):t()}(0,function(){"use strict";var e=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var n=Array.prototype.slice,i=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]"].join(","),o=function(){function o(e,n){t(this,o),this._inertManager=n,this._rootElement=e,this._managedNodes=new Set([]),this._rootElement.hasAttribute("aria-hidden")&&(this._savedAriaHidden=this._rootElement.getAttribute("aria-hidden")),this._rootElement.setAttribute("aria-hidden","true"),this._makeSubtreeUnfocusable(this._rootElement),this._observer=new MutationObserver(this._onMutation.bind(this)),this._observer.observe(this._rootElement,{attributes:!0,childList:!0,subtree:!0})}return e(o,[{key:"destructor",value:function(){this._observer.disconnect(),this._observer=null,this._rootElement&&(this.hasSavedAriaHidden?this._rootElement.setAttribute("aria-hidden",this.savedAriaHidden):this._rootElement.removeAttribute("aria-hidden")),this._rootElement=null,this._managedNodes.forEach(function(e){this._unmanageNode(e.node)},this),this._managedNodes=null,this._inertManager=null}},{key:"_makeSubtreeUnfocusable",value:function(e){var t=this;s(e,function(e){return t._visitNode(e)});var n=document.activeElement;if(!document.body.contains(e)){for(var i=e,o=void 0;i;){if(i.nodeType===Node.DOCUMENT_FRAGMENT_NODE){o=i;break}i=i.parentNode}o&&(n=o.activeElement)}e.contains(n)&&(n.blur(),n===document.activeElement&&document.body.focus())}},{key:"_visitNode",value:function(e){e.nodeType===Node.ELEMENT_NODE&&(e!==this._rootElement&&e.hasAttribute("inert")&&this._adoptInertRoot(e),(e.matches(i)||e.hasAttribute("tabindex"))&&this._manageNode(e))}},{key:"_manageNode",value:function(e){var t=this._inertManager.register(e,this);this._managedNodes.add(t)}},{key:"_unmanageNode",value:function(e){var t=this._inertManager.deregister(e,this);t&&this._managedNodes.delete(t)}},{key:"_unmanageSubtree",value:function(e){var t=this;s(e,function(e){return t._unmanageNode(e)})}},{key:"_adoptInertRoot",value:function(e){var t=this._inertManager.getInertRoot(e);t||(this._inertManager.setInert(e,!0),t=this._inertManager.getInertRoot(e)),t.managedNodes.forEach(function(e){this._manageNode(e.node)},this)}},{key:"_onMutation",value:function(e,t){e.forEach(function(e){var t=e.target;if("childList"===e.type)n.call(e.addedNodes).forEach(function(e){this._makeSubtreeUnfocusable(e)},this),n.call(e.removedNodes).forEach(function(e){this._unmanageSubtree(e)},this);else if("attributes"===e.type)if("tabindex"===e.attributeName)this._manageNode(t);else if(t!==this._rootElement&&"inert"===e.attributeName&&t.hasAttribute("inert")){this._adoptInertRoot(t);var i=this._inertManager.getInertRoot(t);this._managedNodes.forEach(function(e){t.contains(e.node)&&i._manageNode(e.node)})}},this)}},{key:"managedNodes",get:function(){return new Set(this._managedNodes)}},{key:"hasSavedAriaHidden",get:function(){return"_savedAriaHidden"in this}},{key:"savedAriaHidden",set:function(e){this._savedAriaHidden=e},get:function(){return this._savedAriaHidden}}]),o}(),r=function(){function n(e,i){t(this,n),this._node=e,this._overrodeFocusMethod=!1,this._inertRoots=new Set([i]),this._destroyed=!1,this.ensureUntabbable()}return e(n,[{key:"destructor",value:function(){this._throwIfDestroyed(),this._node&&(this.hasSavedTabIndex?this._node.setAttribute("tabindex",this.savedTabIndex):this._node.removeAttribute("tabindex"),this._overrodeFocusMethod&&delete this._node.focus),this._node=null,this._inertRoots=null,this._destroyed=!0}},{key:"_throwIfDestroyed",value:function(){if(this.destroyed)throw new Error("Trying to access destroyed InertNode")}},{key:"ensureUntabbable",value:function(){var e=this.node;if(e.matches(i)){if(-1===e.tabIndex&&this.hasSavedTabIndex)return;e.hasAttribute("tabindex")&&(this._savedTabIndex=e.tabIndex),e.setAttribute("tabindex","-1"),e.nodeType===Node.ELEMENT_NODE&&(e.focus=function(){},this._overrodeFocusMethod=!0)}else e.hasAttribute("tabindex")&&(this._savedTabIndex=e.tabIndex,e.removeAttribute("tabindex"))}},{key:"addInertRoot",value:function(e){this._throwIfDestroyed(),this._inertRoots.add(e)}},{key:"removeInertRoot",value:function(e){this._throwIfDestroyed(),this._inertRoots.delete(e),0===this._inertRoots.size&&this.destructor()}},{key:"destroyed",get:function(){return this._destroyed}},{key:"hasSavedTabIndex",get:function(){return"_savedTabIndex"in this}},{key:"node",get:function(){return this._throwIfDestroyed(),this._node}},{key:"savedTabIndex",set:function(e){this._throwIfDestroyed(),this._savedTabIndex=e},get:function(){return this._throwIfDestroyed(),this._savedTabIndex}}]),n}();function s(e,t,n){if(e.nodeType==Node.ELEMENT_NODE){var i=e;t&&t(i);var o=i.shadowRoot||i.webkitShadowRoot;if(o)return void s(o,t,o);if("content"==i.localName){for(var r=i,a=r.getDistributedNodes?r.getDistributedNodes():[],d=0;d<a.length;d++)s(a[d],t,n);return}if("slot"==i.localName){for(var u=i,h=u.assignedNodes?u.assignedNodes({flatten:!0}):[],c=0;c<h.length;c++)s(h[c],t,n);return}}for(var l=e.firstChild;null!=l;)s(l,t,n),l=l.nextSibling}function a(e){if(!e.querySelector("style#inert-style")){var t=document.createElement("style");t.setAttribute("id","inert-style"),t.textContent="\n[inert] {\n pointer-events: none;\n cursor: default;\n}\n\n[inert], [inert] * {\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n}\n",e.appendChild(t)}}var d=new(function(){function i(e){if(t(this,i),!e)throw new Error("Missing required argument; InertManager needs to wrap a document.");this._document=e,this._managedNodes=new Map,this._inertRoots=new Map,this._observer=new MutationObserver(this._watchForInert.bind(this)),a(e.head||e.body||e.documentElement),"loading"===e.readyState?e.addEventListener("DOMContentLoaded",this._onDocumentLoaded.bind(this)):this._onDocumentLoaded()}return e(i,[{key:"setInert",value:function(e,t){if(t){if(this._inertRoots.has(e))return;var n=new o(e,this);if(e.setAttribute("inert",""),this._inertRoots.set(e,n),!this._document.body.contains(e))for(var i=e.parentNode;i;)11===i.nodeType&&a(i),i=i.parentNode}else{if(!this._inertRoots.has(e))return;this._inertRoots.get(e).destructor(),this._inertRoots.delete(e),e.removeAttribute("inert")}}},{key:"getInertRoot",value:function(e){return this._inertRoots.get(e)}},{key:"register",value:function(e,t){var n=this._managedNodes.get(e);return void 0!==n?(n.addInertRoot(t),n.ensureUntabbable()):n=new r(e,t),this._managedNodes.set(e,n),n}},{key:"deregister",value:function(e,t){var n=this._managedNodes.get(e);return n?(n.removeInertRoot(t),n.destroyed&&this._managedNodes.delete(e),n):null}},{key:"_onDocumentLoaded",value:function(){n.call(this._document.querySelectorAll("[inert]")).forEach(function(e){this.setInert(e,!0)},this),this._observer.observe(this._document.body,{attributes:!0,subtree:!0,childList:!0})}},{key:"_watchForInert",value:function(e,t){e.forEach(function(e){switch(e.type){case"childList":n.call(e.addedNodes).forEach(function(e){if(e.nodeType===Node.ELEMENT_NODE){var t=n.call(e.querySelectorAll("[inert]"));e.matches("[inert]")&&t.unshift(e),t.forEach(function(e){this.setInert(e,!0)},this)}},this);break;case"attributes":if("inert"!==e.attributeName)return;var t=e.target,i=t.hasAttribute("inert");this.setInert(t,i)}},this)}}]),i}())(document);Object.defineProperty(Element.prototype,"inert",{enumerable:!0,get:function(){return this.hasAttribute("inert")},set:function(e){d.setInert(this,e)}})}); | ||
//# sourceMappingURL=inert.min.js.map |
{ | ||
"name": "wicg-inert", | ||
"version": "1.1.6", | ||
"version": "2.0.0", | ||
"description": "A polyfill for the proposed inert API", | ||
"main": "dist/inert.js", | ||
"scripts": { | ||
"lint": "eslint src/*.js", | ||
"test": "npm run lint && npm run build && easy-sauce", | ||
"build": "rollem", | ||
"dev": "rollem --watch" | ||
"build": "rollup -c", | ||
"prepublishOnly": "SAUCE=true npm run test", | ||
"test": "npm run build && karma start" | ||
}, | ||
@@ -25,3 +24,4 @@ "repository": { | ||
"Jesse Beach", | ||
"Brian Kardell" | ||
"Brian Kardell", | ||
"Valdrin Koshi" | ||
], | ||
@@ -32,48 +32,27 @@ "bugs": { | ||
"homepage": "https://github.com/WICG/inert#readme", | ||
"dependencies": { | ||
"dom-matches": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"babel-plugin-external-helpers": "^6.22.0", | ||
"babel-polyfill": "^6.13.0", | ||
"babel-preset-es2015": "^6.13.2", | ||
"chai": "^4.0.2", | ||
"del": "^3.0.0", | ||
"easy-sauce": "^0.4.1", | ||
"eslint": "^4.0.0", | ||
"eslint-config-google": "^0.8.0", | ||
"mocha": "^3.1.2", | ||
"rollem": "^1.11.0", | ||
"rollup": "^0.43.0", | ||
"rollup-plugin-babel": "^2.7.1", | ||
"rollup-plugin-commonjs": "^8.0.2", | ||
"rollup-plugin-node-resolve": "^3.0.0", | ||
"rollup-plugin-uglify": "^2.0.1", | ||
"rollup-watch": "^4.0.0", | ||
"whatwg-fetch": "^2.0.3" | ||
}, | ||
"easySauce": { | ||
"username": "robdodson_inert", | ||
"key": "a844aee9-d3ec-4566-94e3-dba3d0c30248", | ||
"testPath": "/test/", | ||
"port": "8080", | ||
"service": "sauce-connect", | ||
"platforms": [ | ||
[ | ||
"Windows 10", | ||
"chrome", | ||
"latest" | ||
], | ||
[ | ||
"Linux", | ||
"firefox", | ||
"latest" | ||
], | ||
[ | ||
"OS X 10.11", | ||
"safari", | ||
"10" | ||
] | ||
] | ||
"babel-core": "^6.26.3", | ||
"babel-preset-env": "^1.6.1", | ||
"chai": "^4.1.2", | ||
"eslint": "^4.16.0", | ||
"eslint-config-google": "^0.9.1", | ||
"eslint-plugin-es5": "^1.3.1", | ||
"husky": "^0.14.3", | ||
"karma": "^2.0.2", | ||
"karma-chai": "^0.1.0", | ||
"karma-chrome-launcher": "^2.2.0", | ||
"karma-firefox-launcher": "^1.1.0", | ||
"karma-fixture": "^0.2.6", | ||
"karma-html2js-preprocessor": "^1.1.0", | ||
"karma-mocha": "^1.3.0", | ||
"karma-polyfill": "^1.0.0", | ||
"karma-sauce-launcher": "^1.2.0", | ||
"karma-sourcemap-loader": "^0.3.7", | ||
"karma-spec-reporter": "0.0.32", | ||
"lint-staged": "^7.0.0", | ||
"mocha": "^5.0.0", | ||
"rollup": "^0.59.0", | ||
"rollup-plugin-babel": "^3.0.4", | ||
"rollup-plugin-uglify": "^3.0.0" | ||
} | ||
} |
294
README.md
@@ -1,221 +0,85 @@ | ||
# `inert` attribute | ||
[![Build Status](https://travis-ci.org/WICG/inert.svg?branch=master)](https://travis-ci.org/WICG/inert) | ||
[![Dependencies Status](https://david-dm.org/WICG/inert/status.svg)](https://david-dm.org/WICG/inert) | ||
[![DevDependencies Status](https://david-dm.org/WICG/inert/dev-status.svg)](https://david-dm.org/WICG/inert?type=dev) | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/WICG/inert.svg)](https://greenkeeper.io/) | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
**Table of Contents** | ||
The `inert` attribute/property allows web authors to mark parts of the DOM tree | ||
as [inert](https://html.spec.whatwg.org/multipage/interaction.html#inert): | ||
- [tl;dr](#tldr) | ||
- [Background](#background) | ||
- [Spec](#spec) | ||
- [Spec gaps](#spec-gaps) | ||
- [The case for `inert` as a primitive](#the-case-for-inert-as-a-primitive) | ||
- [Use cases](#use-cases) | ||
- [Wouldn't this be better as...](#wouldnt-this-be-better-as) | ||
- [Notes on the polyfill](#notes-on-the-polyfill) | ||
- [Install](#install) | ||
- [Legacy Browser Support](#legacy-browser-support) | ||
> When a node is inert, then the user agent must act as if the node was absent | ||
> for the purposes of targeting user interaction events, may ignore the node for | ||
> the purposes of text search user interfaces (commonly known as "find in | ||
> page"), and may prevent the user from selecting text in that node. | ||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
Furthermore, a node which is **inert** should also be hidden from assistive | ||
technology. | ||
## tl;dr | ||
# Details | ||
The `inert` attribute would allow web authors to mark parts of the DOM tree as [inert](https://html.spec.whatwg.org/multipage/interaction.html#inert): | ||
- Read the [Explainer](explainer.md). | ||
- Read the [Spec](https://html.spec.whatwg.org/multipage/interaction.html#inert). | ||
- Try the [Demo](https://wicg.github.io/inert/demo/). | ||
- [Give feedback!](https://github.com/WICG/inert/issues) | ||
> When a node is inert, then the user agent must act as if the node was absent for the purposes of targeting user interaction events, may ignore the node for the purposes of text search user interfaces (commonly known as "find in page"), and may prevent the user from selecting text in that node. | ||
# Polyfill | ||
Furthermore, a node which is **inert** should also be hidden from assistive technology. | ||
## Installation | ||
Try out the [polyfill](#notes-on-the-polyfill), or look at the [demo page](https://wicg.github.io/inert/index.html). | ||
`npm install --save wicg-inert` | ||
## Background | ||
_We recommend only using versions of the polyfill that have been published to npm, rather than | ||
cloning the repo and using the source directly. This helps ensure the version you're using is stable | ||
and thoroughly tested._ | ||
The `inert` attribute was [originally specced](https://github.com/whatwg/html/commit/2fb24fcf) as part of the `<dialog>` element specification. | ||
`<dialog>` required the concept of `inert` to be defined in order to describe the blocking behaviour of dialogs, | ||
and the `inert` attribute was introduced ["so you could do `<dialog>` without `<dialog>`"](https://www.w3.org/Bugs/Public/show_bug.cgi?id=24983#c1). | ||
_If you do want to build from source, make sure you clone the latest tag!_ | ||
The attribute was later [removed](https://github.com/whatwg/html/commit/5ddfc78b1f82e86cc202d72ccc752a0e15f1e4ad) as it was argued that its only use case was subsumed by `<dialog>`. However, later discussion on the [original bug](https://www.w3.org/Bugs/Public/show_bug.cgi?id=24983) proposed several use cases which could not be handled, or only handled poorly, using `<dialog>`. | ||
## Usage | ||
## Spec | ||
### 1. Add the script to your page | ||
The spec for the `inert` attribute, | ||
with [the existing definition of "inert" already specified](https://html.spec.whatwg.org/multipage/interaction.html#inert), | ||
is extremely straightforward: | ||
```html | ||
... | ||
<script src="/node_modules/wicg-inert/dist/inert.min.js"></script> | ||
</body> | ||
</html> | ||
``` | ||
> <h4>The <dfn title="attr-inert"><code>inert</code></dfn> attribute</h4> | ||
### 2. Toggle `inert` on an element | ||
> <p>The <code title="attr-inert">inert</code> attribute is a | ||
> <span>boolean attribute</span> that indicates, by its presence, that | ||
> the element is to be made <span>inert</span>.</p> | ||
```js | ||
const el = document.querySelector('#my-element'); | ||
el.inert = true; // alternatively, el.setAttribute('inert', ''); | ||
``` | ||
> <div class="impl"> | ||
### Legacy Browser Support | ||
> <p>When an element has an <code title="attr-inert">inert</code> | ||
> attribute, the user agent must mark that element as | ||
> <span>inert</span>.</p> | ||
If you want to use `inert` with an older browser you'll need to include | ||
additional polyfills for | ||
[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), | ||
[Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set), | ||
[Element.prototype.matches](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches), | ||
and [Node.prototype.contains](https://developer.mozilla.org/en-US/docs/Web/API/Node/contains). | ||
> </div> | ||
In accordance with the W3C's new [polyfill | ||
guidance](https://www.w3.org/2001/tag/doc/polyfills/#don-t-serve-unnecessary-polyfills), | ||
the `inert` polyfill does not bundle other polyfills. | ||
> <p class="note">By default, there is no visual indication of a | ||
> subtree being inert. Authors are encouraged to clearly mark what | ||
> parts of their document are active and which are inert, to avoid | ||
> user confusion. In particular, it is worth remembering that not all | ||
> users can see all parts of a page at once; for example, users of | ||
> screen readers, users on small devices or with magnifiers, and even | ||
> users just using particularly small windows might not be able to see | ||
> the active part of a page and may get frustrated if inert sections | ||
> are not obviously inert. For individual controls, the <code | ||
> title="attr-input-disabled">disabled</code> attribute is probably | ||
> more appropriate.</p> | ||
You can use a service like [Polyfill.io](https://polyfill.io/v2/docs/examples) | ||
to download only the polyfills needed by the current browser. Just add the | ||
following line to the start of your page: | ||
### Spec gaps | ||
```html | ||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Map,Set,Element.prototype.matches,Node.prototype.contains"></script> | ||
``` | ||
- The spec does not explicitly state what effect `inert` has on the subtree of the element marked as `inert`, | ||
however it is implied by the note that `inert` causes the entire subtree of the element with the `inert` attribute to be made [_inert_](https://html.spec.whatwg.org/multipage/interaction.html#inert). | ||
The polyfill makes the assumption that the entire subtree becomes _inert_. | ||
- Furthermore, the spec is unclear as to whether the attribute applies into [shadow trees](https://dom.spec.whatwg.org/#concept-shadow-tree). | ||
Consistency with CSS attributes and with inheriting attributes like [`aria-hidden`](https://www.w3.org/TR/wai-aria/states_and_properties#aria-hidden) and [`lang`](http://w3c.github.io/html/dom.html#the-lang-and-xmllang-attributes) imply that it should. | ||
The polyfill assumes that it does so. | ||
- The [existing description of _inert_](https://html.spec.whatwg.org/multipage/interaction.html#inert) is not specific about where pointer events which would have been targeted to an element in an inert subtree should go. | ||
(See also: discussion on the [WHATWG pull request](https://github.com/whatwg/html/pull/1474).) | ||
Does the event: | ||
### Performance and gotchas | ||
1. go to the next non-inert element in the hit test stack? | ||
(The inert element is "transparent" for pointer events.) | ||
2. go to the next non-inert parent element? | ||
3. simply not fire? | ||
The polyfill attempts to provide a reasonable fidelity polyfill for the `inert` | ||
**attribute**, however please note: | ||
Consistency with `pointer-events` would suggest (ii). The polyfill uses `pointer-events: none` and so models its behaviour. | ||
- The spec is also not explicit about whether the attribute should be [reflected](http://w3c.github.io/html/infrastructure.html#reflection). The polyfill assumes that it is. | ||
- The spec does not explicitly state that inert content should be [hidden from assistive technology](https://www.w3.org/WAI/PF/aria-implementation/#exclude_elements2). | ||
However, typically, the HTML spec does not provide this type of information. The polyfill makes _inert_ content hidden from assistive technology (via `aria-hidden`). | ||
- The spec does not make explicit that there is no way to "un-inert" a subtree of an inert subtree. | ||
- It relies on mutation observers to detect the addition of the `inert` | ||
attribute, and to detect dynamically added content within inert subtrees. | ||
Testing for _inert_-ness in any way immediately after either type of mutation | ||
will therefore give inconsistent results; please allow the current task to end | ||
before relying on mutation-related changes to take effect, for example via | ||
`setTimeout(fn, 0)` or `Promise.resolve()`. | ||
## The case for `inert` as a primitive | ||
Developers find themselves in situations where they'd like to be able to mark a part of the page "un-tabbable". | ||
Rob Dodson lays out one such example in his article ["Building better accessibility primitives"](https://robdodson.me/building-better-accessibility-primitives/#problem2disablingtabindex): | ||
> One problem: to [achieve a performance optimisation for animation] we must leave the drawer in the DOM at all times. | ||
> Meaning its focusable children are just sitting there offscreen, | ||
> and as the user is tabbing through the page eventually their focus will just disappear into the drawer and they won't know where it went. | ||
> I see this on responsive websites all the time. | ||
> This is just one example but I've also run into the need to disable tabindex when I'm animating between elements with opacity: 0, | ||
> or temporarily disabling large lists of custom controls, | ||
> and as others have pointed out, | ||
> you'd hit if you tried to build something like coverflow where you can see a preview of the next element but can't actually interact with it yet. | ||
`inert` would also allow slightly more straightforward polyfilling of both `<dialog>` | ||
and the proposed, more primitive | ||
[`blockingElements`](https://github.com/whatwg/html/issues/897) | ||
API. | ||
See | ||
[Polymer Labs' `blockingElements` polyfill](https://github.com/PolymerLabs/blockingElements/blob/master/demo/index.html), | ||
based on this polyfill, | ||
for an example of how `inert` may be used for this purpose. | ||
Currently, since there is no way to express the "inertness" concept, | ||
polyfilling these APIs requires both focus event trapping | ||
to avoid focus cycling out of the dialog/blocking element | ||
(and thus as a side effect may prevent focus from walking out of the page at all) | ||
and a tree-walk | ||
(usually neglected by developers) | ||
to set `aria-hidden` on all sibling elements of the dialog or blocking element. | ||
On the implementer side, | ||
the vast majority of work involved in implementing `inert` is a necessary pre-cursor to both `<dialog>` and `blockingElements` implementations, | ||
so by implementing `inert` first, | ||
implementers may get useful functionality into the hands of developers sooner while still laying the groundwork for one or both of these more complex APIs. | ||
### Use cases | ||
- **Temporarily offscreen/hidden content** | ||
As discussed in the [article](https://robdodson.me/building-better-accessibility-primitives/#problem2disablingtabindex), | ||
there are a range of circumstances in which case it's desirable to add content to the DOM to be rendered but remain offscreen. | ||
In these cases, without `inert`, authors are forced to choose between | ||
an accessible experience for keyboard and assistive technology users, | ||
or the factors (such as performance) which make offscreen rendering desirable - | ||
or, performing all the contortions necessary to keep the offscreen content functionally "inert". | ||
These cases include: | ||
+ rendering content, such as a menu, offscreen, before having it animate on-screen; | ||
+ similarly, for content like a menu which may be repeatedly shown to the user, | ||
avoiding re-rendering this content each time; | ||
+ a carousel or other type of content cycler (such as a "tweet cycler") | ||
which visually hides non-current items by placing them at a lower z-index than the active item, | ||
or by setting their `opacity` to zero, | ||
and animates transitions between items; | ||
+ "infinitely scrolling" UI which re-uses and/or pre-renders nodes. | ||
- **On-screen but non-interactive content** | ||
Occasionally, UI designs require that certain content be visible or partially visible, | ||
but clearly non-interactive. | ||
Typically, this content is made non-interactive for pointer device users | ||
either via a semi-transparent overlay which provides a visual cue as well as intercepting pointer events, | ||
or via using `pointer-events: none`. | ||
In these cases developers are once again required to perform contortions in order to ensure that this content is not an accessibility issue. | ||
These cases include: | ||
+ Any of the use cases for [`blockingElement[s]`](https://github.com/whatwg/html/issues/897): | ||
* a modal dialog; | ||
* a focus-trapping menu; | ||
* a [side nav](https://material.google.com/patterns/navigation-drawer.html). | ||
+ A slide show or "cover flow" style carousel may have non-active items partially visible, | ||
as a preview - | ||
they may be transformed or partially obscured to indicate that they are non-interactive. | ||
+ Form content which is not currently relevant, | ||
e.g. fading out and disabling the "Shipping Address" fields when the "Same as billing address" checkbox has been checked. | ||
+ Disabling the entire UI while in an inconsistent state, | ||
such as showing a throbber/loading bar during unexpectedly slow loading. | ||
## Wouldn't this be better as... | ||
- A **CSS property**? | ||
`inert` encompasses the behaviour of at least two other things which are CSS properties - | ||
`pointer-events: none` and `user-select: none`, plus another attribute, `aria-hidden`. | ||
These behaviours, along with the currently near-impossible to achieve behaviour of preventing tabbing/programmatic focus, are very frequently applied together | ||
(or if one, such as `aria-hidden`, is omitted, it is more often through lack of awareness than deliberate). | ||
There is scope for a more primitive CSS property to "explain" the ability of `inert` to prevent focus, however that could easily coexist with the `inert` attribute. | ||
- [`blockingElements`](https://github.com/whatwg/html/issues/897)? | ||
`blockingElements` (or, potentially, a single `blockingElement`) represents roughly the opposite use case to `inert`: | ||
a per-document, single element which blocks the document, analogous to the [blocking behaviour of a modal dialog](https://html.spec.whatwg.org/multipage/interaction.html#blocked-by-a-modal-dialog). | ||
It's not always the case that we will want a single subtree to be non-inert. Ideally, we would have both concepts available; | ||
however, `inert` allows reasonable approximation of `blockingElements` whereas the reverse is not true. | ||
- To approximate a `blockingElement` using `inert`, it's most straightforward to insert a non-_inert_ element as a sibling element to the main page content, and then use `inert` to mark the main page content as _inert_. | ||
More generally, all siblings of the desired "blocking" element, plus all siblings of all of its ancestors, could be marked _inert_. | ||
- A **programmatic API**? | ||
Something like `document.makeInert(el)`. | ||
This would require waiting for script execution before parts of the page became inert, which can take some time. | ||
## Notes on the polyfill | ||
The `dist` directory contains a transpiled, UMD build of `inert.js` generated from the ES6 source. | ||
There is also an [HTML Import](https://developer.mozilla.org/en-US/docs/Web/Web_Components/HTML_Imports) | ||
available in the root of the project at `inert.html`. | ||
The polyfill attempts to provide a reasonable fidelity polyfill for the `inert` attribute, however please note: | ||
- It relies on mutation observers to detect the addition of the `inert` attribute, and to detect dynamically added content within inert subtrees. | ||
Testing for _inert_-ness in any way immediately after either type of mutation will therefore give inconsistent results; | ||
please allow the current task to end before relying on mutation-related changes to take effect, for example via `Promise.resolve()`. | ||
Example: | ||
@@ -226,30 +90,24 @@ ```js | ||
inertContainer.appendChild(newButton); | ||
// Wait for the next microtask to allow mutation observers to react to the DOM change | ||
// Wait for the next microtask to allow mutation observers to react to the | ||
// DOM change | ||
Promise.resolve().then(() => { | ||
expect(isUnfocusable(newButton)).to.equal(true); | ||
expect(isUnfocusable(newButton)).to.equal(true); | ||
}); | ||
``` | ||
- Using the `inert` property, however, is synchronous. | ||
- Using the `inert` **property**, however, is synchronous. | ||
- It will be very expensive performance-wise compared to a native `inert` implementation, because it requires a lot of tree-walking on any relevant mutation | ||
(applying `inert`, or adding descendant content into `inert` subtrees). | ||
- To mitigate this, avoid these types of mutations as much as possible when using this polyfill. | ||
- The polyfill will be expensive, performance-wise, compared to a native `inert` | ||
implementation, because it requires a fair amount of tree-walking. To mitigate | ||
this, we recommend not using `inert` during performance sensitive actions | ||
(like during animations or scrolling). Instead, wait till these events are | ||
complete, or consider using | ||
[requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback) | ||
to set `inert`. | ||
### Install | ||
Using npm: | ||
``` | ||
npm install --save wicg-inert | ||
``` | ||
Using Bower: | ||
``` | ||
bower install --save WICG/inert | ||
``` | ||
# Testing | ||
### Legacy Browser Support | ||
If you want to use `inert` with an older browser or JavaScript runtime like PhantomJS you'll need to include a polyfill for Map and Set, such as [babel-polyfill](https://babeljs.io/docs/usage/polyfill/). | ||
[As suggested on StackOverflow](http://stackoverflow.com/a/40388592/712889), try adding the following to the document in your build process: | ||
```html | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.min.js"></script> | ||
``` | ||
Tests are written using ES5 syntax. This is to avoid needing to transpile them | ||
for older browsers. There are a few modern features they rely upon, e.g. | ||
`Array.from` and `Promises`. These are polyfilled for the tests using | ||
[Polyfill.io](http://polyfill.io/). For a list of polyfilled features, check out | ||
the `polyfill` section in `karma.conf.js`. |
@@ -6,5 +6,2 @@ /** | ||
import matches from 'dom-matches'; | ||
(function(document) { | ||
// Convenience function for converting NodeLists. | ||
@@ -95,5 +92,5 @@ /** @type {function(number,number):Array} */ | ||
for (let inertNode of this._managedNodes) { | ||
this._managedNodes.forEach(function(inertNode) { | ||
this._unmanageNode(inertNode.node); | ||
} | ||
}, this); | ||
@@ -134,3 +131,4 @@ this._managedNodes = null; | ||
let activeElement = document.activeElement; | ||
if (!contains(document.body, startNode)) { | ||
if (!document.body.contains(startNode)) { | ||
// startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement. | ||
@@ -150,4 +148,10 @@ let node = startNode; | ||
} | ||
if (contains(startNode, activeElement)) { | ||
if (startNode.contains(activeElement)) { | ||
activeElement.blur(); | ||
// In IE11, if an element is already focused, and then set to tabindex=-1 | ||
// calling blur() will not actually move the focus. | ||
// To work around this we call focus() on the body instead. | ||
if (activeElement === document.activeElement) { | ||
document.body.focus(); | ||
} | ||
} | ||
@@ -170,3 +174,3 @@ } | ||
if (matches(node, _focusableElementsString) || node.hasAttribute('tabindex')) { | ||
if (node.matches(_focusableElementsString) || node.hasAttribute('tabindex')) { | ||
this._manageNode(node); | ||
@@ -218,5 +222,5 @@ } | ||
for (let savedInertNode of inertSubroot.managedNodes) { | ||
inertSubroot.managedNodes.forEach(function(savedInertNode) { | ||
this._manageNode(savedInertNode.node); | ||
} | ||
}, this); | ||
} | ||
@@ -230,14 +234,14 @@ | ||
_onMutation(records, self) { | ||
for (let record of records) { | ||
records.forEach(function(record) { | ||
const target = record.target; | ||
if (record.type === 'childList') { | ||
// Manage added nodes | ||
for (let node of slice.call(record.addedNodes)) { | ||
slice.call(record.addedNodes).forEach(function(node) { | ||
this._makeSubtreeUnfocusable(node); | ||
} | ||
}, this); | ||
// Un-manage removed nodes | ||
for (let node of slice.call(record.removedNodes)) { | ||
slice.call(record.removedNodes).forEach(function(node) { | ||
this._unmanageSubtree(node); | ||
} | ||
}, this); | ||
} else if (record.type === 'attributes') { | ||
@@ -254,10 +258,10 @@ if (record.attributeName === 'tabindex') { | ||
const inertSubroot = this._inertManager.getInertRoot(target); | ||
for (let managedNode of this._managedNodes) { | ||
if (contains(target, managedNode.node)) { | ||
this._managedNodes.forEach(function(managedNode) { | ||
if (target.contains(managedNode.node)) { | ||
inertSubroot._manageNode(managedNode.node); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
}, this); | ||
} | ||
@@ -373,3 +377,3 @@ } | ||
const node = this.node; | ||
if (matches(node, _focusableElementsString)) { | ||
if (node.matches(_focusableElementsString)) { | ||
if (node.tabIndex === -1 && this.hasSavedTabIndex) { | ||
@@ -484,3 +488,3 @@ return; | ||
// Ensure inert styles are added there. | ||
if (!contains(this._document.body, root)) { | ||
if (!this._document.body.contains(root)) { | ||
let parent = root.parentNode; | ||
@@ -567,5 +571,5 @@ while (parent) { | ||
const inertElements = slice.call(this._document.querySelectorAll('[inert]')); | ||
for (let inertElement of inertElements) { | ||
inertElements.forEach(function(inertElement) { | ||
this.setInert(inertElement, true); | ||
} | ||
}, this); | ||
@@ -582,21 +586,21 @@ // Comment this out to use programmatic API only. | ||
_watchForInert(records, self) { | ||
for (let record of records) { | ||
records.forEach(function(record) { | ||
switch (record.type) { | ||
case 'childList': | ||
for (let node of slice.call(record.addedNodes)) { | ||
slice.call(record.addedNodes).forEach(function(node) { | ||
if (node.nodeType !== Node.ELEMENT_NODE) { | ||
continue; | ||
return; | ||
} | ||
const inertElements = slice.call(node.querySelectorAll('[inert]')); | ||
if (matches(node, '[inert]')) { | ||
if (node.matches('[inert]')) { | ||
inertElements.unshift(node); | ||
} | ||
for (let inertElement of inertElements) { | ||
inertElements.forEach(function(inertElement) { | ||
this.setInert(inertElement, true); | ||
} | ||
} | ||
}, this); | ||
}, this); | ||
break; | ||
case 'attributes': | ||
if (record.attributeName !== 'inert') { | ||
continue; | ||
return; | ||
} | ||
@@ -608,3 +612,3 @@ const target = record.target; | ||
} | ||
} | ||
}, this); | ||
} | ||
@@ -700,16 +704,2 @@ } | ||
/** | ||
* `Node#contains()` polyfill. | ||
* | ||
* See: http://compatibility.shwups-cms.ch/en/polyfills/?&id=1 | ||
* | ||
* @param {Node} node | ||
* @param {Node} other | ||
* @return {Boolean} | ||
* @public | ||
*/ | ||
function contains(node, other) { | ||
return other && (node === other || !!(node.compareDocumentPosition(other) & 16) ); | ||
} | ||
const inertManager = new InertManager(document); | ||
@@ -726,2 +716,1 @@ | ||
}); | ||
})(document); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
134732
0
29
1901
23
113
2
- Removeddom-matches@^2.0.0
- Removeddom-matches@2.0.0(transitive)