FontAwesome svg在阴影区



我试图在web组件中使用字体awsomejs/svg库,但图标不会显示。这可能吗?

我正试图在现有的webforms项目中实现一个有角度的组件,而不需要css和脚本"流血",还有其他关于如何做到这一点的建议吗?iframe不是一个选项。

<html>
<head>
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js" defer>
</script>
<script>
customElements.define('my-holder', class extends HTMLElement {
constructor() {
super();
console.log("constructor");
let shadowRoot = this.attachShadow({
mode: 'open'
});
const t = document.querySelector('#holder');
const instance = t.content.cloneNode(true);
shadowRoot.appendChild(instance);
}
connectedCallback() {
console.log("callback");
}
});
</script>
</head>
<body>
<div id="outside">
light dom
<div class="fa-4x">
<span class="fa-layers fa-fw" style="background:MistyRose">
<i class="fas fa-circle" style="color:Tomato"></i>
<i class="fa-inverse fas fa-times" data-fa-transform="shrink-6"></i>
</span>
</div>
</div>
<template id="holder">
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js" defer></script>
dark shadow dom
<div class="fa-4x">
<span class="fa-layers fa-fw" style="background:MistyRose">
<i class="fas fa-circle" style="color:Tomato"></i>
<i class="fa-inverse fas fa-times" data-fa-transform="shrink-6"></i>
</span>
</div>
</template>
<div id="inside">
<my-holder></my-holder>
</div>
</body>
</html>

许多旧的skool库都使用document.来访问主DOM
因此他们无法对shadowDOM中的内容执行任何操作

这意味着您的目标是不让脚本流血是不可能的
必须在主DOM中加载Font Awesome(脚本和样式(。

如果你不想在shadowDOM之外流血样式,你必须遵守规则:

  • Font真棒图标定义必须保留在主DOM 中

  • lightDOMshadowDOM的(主DOM("原始">分时段内容

  • lightDOM由主DOM(如果元素本身在另一个shadowDOM中,则由其shadowDOM容器(进行样式设置

  • 开槽lightDOM保留在lightDOM中,仅反射到其<slot></slot>

  • 您不想在每个lightDOM中重复FontAwesome图标定义
    (您还不如完全不使用自定义元素(

<span class="fa-4x fa-layers fa-fw">
<i class="fas fa-circle"></i>
<i class="fa-inverse fas fa-times" data-fa-transform="shrink-6"></i>
</span>    
  • 自定义元素可以访问整个DOM

解决方案:

编写的自定义元素

  • 创建自己的lightDOM
  • 它是时隙<slot></slot>(反射!未移动!(
  • 从属性获取配置
<awesome-icon background="lightcoral" color="red"></awesome-icon>
<awesome-icon background="lightgreen" color="green"></awesome-icon>
<awesome-icon></awesome-icon>

JSFidle:https://jsfiddle.net/CustomElementsExamples/1pmvasnj/

<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js" defer></script>
<script>
customElements.define('awesome-icon', class extends HTMLElement {
constructor() {
super().attachShadow({mode: 'open'})
.append(document.getElementById(this.nodeName).content.cloneNode(true));
}
connectedCallback() {
let setProperty =
(prop, value)=>this.shadowRoot.host.style.setProperty('--' + prop, value);
setProperty('fa-background', this.getAttribute('background'));
setProperty('fa-color', this.getAttribute('color'));
// move icon HTML back to lightDOM so FontAwesome can style it
this.innerHTML = this.shadowRoot.querySelector('#ICON').innerHTML;
}
});
</script>
<template id="AWESOME-ICON">
<style>
::slotted(*) {
/* lightDOM SPAN has higher Specificity, only way out is using !important */
background: var(--fa-background,grey) !important;
color: var(--fa-color,darkgrey) !important;
}
</style>
<template id="ICON">
<span class="fa-4x fa-layers fa-fw">
<i class="fas fa-circle"></i>
<i class="fa-inverse fas fa-times" data-fa-transform="shrink-6"></i>
</span>    
</template>
<slot><!--lightDOM REFLECTED here--></slot>
</template>
<awesome-icon><!-- lightDOM CREATED here --></awesome-icon>
<awesome-icon background="lightcoral" color="red"></awesome-icon>
<awesome-icon background="lightgreen" color="green"></awesome-icon>
<style>
span{
background:lightblue; /* !important inside shadowDOM overrules these settings */
color:red;
}
</style>

NOT使用shadowDOM和SLOT

并且依赖于作用域的CSS属性,使代码更简单:

<script>
customElements.define('awesome-icon', class extends HTMLElement {
connectedCallback() {
this.append(document.getElementById(this.nodeName).content.cloneNode(true));
this.style.setProperty('--fa-background', this.getAttribute('background') );
this.style.setProperty('--fa-color'     , this.getAttribute('color')      );
}
});
</script>
<template id="AWESOME-ICON">
<span class="fa-4x fa-layers fa-fw">
<i class="fas fa-circle"></i>
<i class="fa-inverse fas fa-times" data-fa-transform="shrink-6"></i>
</span>
</template>
<awesome-icon background="lightcoral" color="red"></awesome-icon>
<awesome-icon background="lightgreen" color="green"></awesome-icon>
<awesome-icon></awesome-icon>
<style>
span {
background: var(--fa-background);
color:      var(--fa-color     );
}
</style>

我找到了一种在shadow-dom中渲染SVG字体的绝妙图标的方法。

从文档来看,@fortawesome/fontawesome-svg-core包提供了更多的控制

这是我用它创建的一个例子https://codesandbox.io/s/romantic-panka-40xqm

基本思想是我们可以使用icon(faCamera).html[0]获得SVG HTML,并可以在shadow-dom-root中使用它

import { icon } from "@fortawesome/fontawesome-svg-core";
import { faCamera } from "@fortawesome/free-solid-svg-icons";
icon(faCamera).html[0]

Font Awesome有一种内置的方法,可以解析/搜索给定元素中的图标标签,以生成SVG。以下内容应该适用于您的示例,或者非常接近您想要的内容:

FontAwesome.dom.i2svg({
node: document.querySelector('my-holder').shadowRoot
})

或者,如果你想让它在一个更动态的web组件中自动热加载任何字体很棒的图标:

connectedCallback() {
if (this.isConnected) {
FontAwesome.dom.i2svg({
node: this.shadowRoot
})
FontAwesome.dom.watch({
autoReplaceSvgRoot: this,
observeMutationsRoot: this.shadowRoot
})
/** Other web component init on connected code here */
}
}

FontAwesome是由字体真棒脚本设置的全局变量的名称。

i2svg()在没有参数的情况下重新解析整个灯光dom(注意,这是不必要的,因为字体真棒默认情况下已经在观察dom的变化(。通过一个参数,可以传递一个具有指向某个元素(光或阴影(的node属性的js对象,它将根据需要生成SVG。

对于那些想在SPA/编译的js前端中实现这一点的人,请查看FA文档,以获得更直接的导入解决方案。

您是否考虑过使用SVG精灵?经过几天查找导入库的替代解决方案,并弄清楚如何使用shadow-dom,我发现SVG精灵是最简单的解决方案。方法是使用svguse方法。请参阅字体很棒的文档。

我最终在项目的dist文件夹中引入了sprite文件(在本例中,我引入了引导程序图标,但任何其他图标集都是一样的(。dist/icons/bootstrap-icons.svg

app.js


const template = document.createElement('template');
template.innerHTML = `
<template>
<style>
.icon {
width: 3rem;
height: 3rem;
stroke: currentColor;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}
</style>
<svg class="icon">
<use href="icons/bootstrap-icons.svg#box-arrow-right"/>
</svg> 
</template>`;
customElements.define('my-svg-icon', class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
});

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Awesome SVG Icons</title>
</head>
<body>
<my-svg-icon></my-svg-icon>
<script type="module" src="app.js"></script>
</body>
</html>

最新更新