如何使用shadow DOM在动态加载的Web组件样式中加载@font-face

Seb*_*anR 7 html css

我有一个带有 Shadow DOM 的 HTML 5 WebComponent,它显示的内容必须根据组件中显示的内容类型加载样式。样式表列表是从服务器获取的。

我可以像这样加载样式表:

for (const style of styles) {
    const stylesheet = document.createElement('link');
    stylesheet.setAttribute('rel', 'stylesheet');
    stylesheet.setAttribute('href', style);
    stylesheet.setAttribute('type', 'text/css');
    this.root.appendChild(stylesheet);
}
Run Code Online (Sandbox Code Playgroud)

但是,样式表有时还包含@font-face未添加到组件中的规则。浏览器永远不会创建对规则中字体引用的请求@font-face。如何动态加载这些规则?

Seb*_*anR 6

事实证明,@font-face截至 2020 年 9 月,浏览器不支持在影子 DOM 中加载 CSS 规则。不过,Chrome 团队似乎正在努力解决这个问题。

我采取的方法是在动态创建的样式表中查找@font-face规则,稍微修改它们以使相对路径正常工作,然后<head>使用脚本将它们添加到页面中。如果将@font-face规则添加到 headShadow DOM,则会加载并应用字体。

这是代码:

for (const style of styles) { // styles is an array of urls
    const stylesheet = document.createElement('link');
    stylesheet.setAttribute('rel', 'stylesheet');
    stylesheet.setAttribute('href', style);
    stylesheet.setAttribute('type', 'text/css');
    stylesheet.onload = (event) => {
        for (
            let i = 0;
            i < event.currentTarget.sheet.cssRules.length;
            i++
        ) {
            if (event.currentTarget.sheet.cssRules[i].type == 5) { // type 5 is @font-face
                const split = style.split('/');
                const stylePath = split
                    .slice(0, split.length - 1)
                    .join('/');
                let cssText =
                    event.currentTarget.sheet.cssRules[i].cssText;
                cssText = cssText.replace(
                    // relative paths
                    /url\s*\(\s*[\'"]?(?!((\/)|((?:https?:)?\/\/)|(?:data\:?:)))([^\'"\)]+)[\'"]?\s*\)/g,
                    `url("${stylePath}/$4")`
                );

                const st = document.createElement('style');
                st.appendChild(document.createTextNode(cssText));
                document
                    .getElementsByTagName('head')[0]
                    .appendChild(st);
            }
        }
    };
    this.root.appendChild(stylesheet);
}
Run Code Online (Sandbox Code Playgroud)

它在 Edge 85(基于 Chromium)上运行良好。