[6a3a178] | 1 | function cssHasPseudo(document) {
|
---|
| 2 | const observedItems = []; // document.createAttribute() doesn't support `:` in the name. innerHTML does
|
---|
| 3 |
|
---|
| 4 | const attributeElement = document.createElement('x'); // walk all stylesheets to collect observed css rules
|
---|
| 5 |
|
---|
| 6 | [].forEach.call(document.styleSheets, walkStyleSheet);
|
---|
| 7 | transformObservedItems(); // observe DOM modifications that affect selectors
|
---|
| 8 |
|
---|
| 9 | const mutationObserver = new MutationObserver(mutationsList => {
|
---|
| 10 | mutationsList.forEach(mutation => {
|
---|
| 11 | [].forEach.call(mutation.addedNodes || [], node => {
|
---|
| 12 | // walk stylesheets to collect observed css rules
|
---|
| 13 | if (node.nodeType === 1 && node.sheet) {
|
---|
| 14 | walkStyleSheet(node.sheet);
|
---|
| 15 | }
|
---|
| 16 | }); // transform observed css rules
|
---|
| 17 |
|
---|
| 18 | cleanupObservedCssRules();
|
---|
| 19 | transformObservedItems();
|
---|
| 20 | });
|
---|
| 21 | });
|
---|
| 22 | mutationObserver.observe(document, {
|
---|
| 23 | childList: true,
|
---|
| 24 | subtree: true
|
---|
| 25 | }); // observe DOM events that affect pseudo-selectors
|
---|
| 26 |
|
---|
| 27 | document.addEventListener('focus', transformObservedItems, true);
|
---|
| 28 | document.addEventListener('blur', transformObservedItems, true);
|
---|
| 29 | document.addEventListener('input', transformObservedItems); // transform observed css rules
|
---|
| 30 |
|
---|
| 31 | function transformObservedItems() {
|
---|
| 32 | requestAnimationFrame(() => {
|
---|
| 33 | observedItems.forEach(item => {
|
---|
| 34 | const nodes = [];
|
---|
| 35 | [].forEach.call(document.querySelectorAll(item.scopeSelector), element => {
|
---|
| 36 | const nthChild = [].indexOf.call(element.parentNode.children, element) + 1;
|
---|
| 37 | const relativeSelectors = item.relativeSelectors.map(relativeSelector => item.scopeSelector + ':nth-child(' + nthChild + ') ' + relativeSelector).join(); // find any relative :has element from the :scope element
|
---|
| 38 |
|
---|
| 39 | const relativeElement = element.parentNode.querySelector(relativeSelectors);
|
---|
| 40 | const shouldElementMatch = item.isNot ? !relativeElement : relativeElement;
|
---|
| 41 |
|
---|
| 42 | if (shouldElementMatch) {
|
---|
| 43 | // memorize the node
|
---|
| 44 | nodes.push(element); // set an attribute with an irregular attribute name
|
---|
| 45 | // document.createAttribute() doesn't support special characters
|
---|
| 46 |
|
---|
| 47 | attributeElement.innerHTML = '<x ' + item.attributeName + '>';
|
---|
| 48 | element.setAttributeNode(attributeElement.children[0].attributes[0].cloneNode()); // trigger a style refresh in IE and Edge
|
---|
| 49 |
|
---|
| 50 | document.documentElement.style.zoom = 1;
|
---|
| 51 | document.documentElement.style.zoom = null;
|
---|
| 52 | }
|
---|
| 53 | }); // remove the encoded attribute from all nodes that no longer match them
|
---|
| 54 |
|
---|
| 55 | item.nodes.forEach(node => {
|
---|
| 56 | if (nodes.indexOf(node) === -1) {
|
---|
| 57 | node.removeAttribute(item.attributeName); // trigger a style refresh in IE and Edge
|
---|
| 58 |
|
---|
| 59 | document.documentElement.style.zoom = 1;
|
---|
| 60 | document.documentElement.style.zoom = null;
|
---|
| 61 | }
|
---|
| 62 | }); // update the
|
---|
| 63 |
|
---|
| 64 | item.nodes = nodes;
|
---|
| 65 | });
|
---|
| 66 | });
|
---|
| 67 | } // remove any observed cssrules that no longer apply
|
---|
| 68 |
|
---|
| 69 |
|
---|
| 70 | function cleanupObservedCssRules() {
|
---|
| 71 | [].push.apply(observedItems, observedItems.splice(0).filter(item => item.rule.parentStyleSheet && item.rule.parentStyleSheet.ownerNode && document.documentElement.contains(item.rule.parentStyleSheet.ownerNode)));
|
---|
| 72 | } // walk a stylesheet to collect observed css rules
|
---|
| 73 |
|
---|
| 74 |
|
---|
| 75 | function walkStyleSheet(styleSheet) {
|
---|
| 76 | try {
|
---|
| 77 | // walk a css rule to collect observed css rules
|
---|
| 78 | [].forEach.call(styleSheet.cssRules || [], rule => {
|
---|
| 79 | if (rule.selectorText) {
|
---|
| 80 | // decode the selector text in all browsers to:
|
---|
| 81 | // [1] = :scope, [2] = :not(:has), [3] = :has relative, [4] = :scope relative
|
---|
| 82 | const selectors = decodeURIComponent(rule.selectorText.replace(/\\(.)/g, '$1')).match(/^(.*?)\[:(not-)?has\((.+?)\)\](.*?)$/);
|
---|
| 83 |
|
---|
| 84 | if (selectors) {
|
---|
| 85 | const attributeName = ':' + (selectors[2] ? 'not-' : '') + 'has(' + // encode a :has() pseudo selector as an attribute name
|
---|
| 86 | encodeURIComponent(selectors[3]).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',') + ')';
|
---|
| 87 | observedItems.push({
|
---|
| 88 | rule,
|
---|
| 89 | scopeSelector: selectors[1],
|
---|
| 90 | isNot: selectors[2],
|
---|
| 91 | relativeSelectors: selectors[3].split(/\s*,\s*/),
|
---|
| 92 | attributeName,
|
---|
| 93 | nodes: []
|
---|
| 94 | });
|
---|
| 95 | }
|
---|
| 96 | } else {
|
---|
| 97 | walkStyleSheet(rule);
|
---|
| 98 | }
|
---|
| 99 | });
|
---|
| 100 | } catch (error) {
|
---|
| 101 | /* do nothing and continue */
|
---|
| 102 | }
|
---|
| 103 | }
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | export default cssHasPseudo;
|
---|
| 107 | //# sourceMappingURL=index.mjs.map
|
---|