Created
July 17, 2024 14:08
-
-
Save barneycarroll/8f49070bab171034d82fadd8615d7c8c to your computer and use it in GitHub Desktop.
A CSS @scope polyfill, extracted from Jon Neals pen here: https://codepen.io/jonneal/pen/xxpqdpJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Return a unique selector for a specific element. */ | |
const getUniqueSelector = (/** @type {Element} */ element) => { | |
/** Unique selector for this element */ | |
let selector = '' | |
/** @type {Element} */ | |
let parent | |
while (parent = element.parentElement) { | |
/** @type {number} Nth-child order of the element. */ | |
const nthChild = Array.prototype.indexOf.call(parent.children, element) + 1 | |
selector = ` > :nth-child(${nthChild})${selector}` | |
element = parent | |
} | |
return ':root' + selector | |
} | |
let supportMap = new WeakMap() | |
let polyfillScope = (document) => { | |
for (let sheet of document.styleSheets) { | |
for (let index = 0; index < sheet.cssRules.length; ++index) { | |
let rule = sheet.cssRules[index] | |
if (rule.type === 12) { | |
let { conditionText } = rule | |
let match = conditionText.match(/^\(polyfill @scope \((.+) to\((.+)\)\)\)$/) | |
if (match) { | |
if (supportMap.has(rule)) continue | |
let [ , from, to ] = match | |
let mediaRule = sheet.cssRules[sheet.insertRule(`@media ${`@scope ${from} to(${to})`.replace(/[^\w]/g, '\\$&')},all` + rule.cssText.slice('@supports '.length + rule.conditionText.length), index++)] | |
supportMap.set(rule, mediaRule) | |
supportMap.set(mediaRule, rule) | |
let fromElements = Array.from(document.querySelectorAll(from)) | |
for (let innerRule of mediaRule.cssRules) { | |
if (innerRule.type === 1) { | |
let narrElements = fromElements.map( | |
from => Array.from( | |
from.querySelectorAll(':is(' + innerRule.selectorText + '):not(:scope :is(' + to + ') *)') | |
) | |
).flat() | |
let selectors = narrElements.map( | |
element => getUniqueSelector(element) | |
) | |
innerRule.selectorText += ':where(' + selectors + ')' | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
let observer = new MutationObserver(() => polyfillScope(document)); | |
observer.observe(document.documentElement, { childList: true, subtree: true }); | |
polyfillScope(document); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment