FontAwesome图标在Shadow DOM中的SVG形式

4

我想在Web组件中使用字体awesome js/svg库,但图标不会显示。这可能吗?

我正在尝试在现有的WebForms项目中实现Angular组件,但没有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>
4个回答

1
许多老派的库使用document.来访问主DOM。
因此,它们无法处理shadowDOM中的内容。

这意味着你的目标是不可能避免脚本泄漏。
Font-Awesome(脚本和样式)必须加载到主DOM中。

如果您不想在shadowDOM之外泄露样式,则必须遵守规则:

  • Font-Awesome图标定义必须保留在主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

解决方案:

编写一个自定义元素,它可以:

  • 创建自己的Light DOM
  • 该DOM被插槽 <slot></slot>反射!不是移动!
  • 从属性中获取配置
    <awesome-icon background="lightcoral" color="red"></awesome-icon>
    <awesome-icon background="lightgreen" color="green"></awesome-icon>
    <awesome-icon></awesome-icon>

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


JSFiddle: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>

使用shadowDOM和SLOTs

并依赖于作用域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>

0

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

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

或者,如果您想要更加高级一些,并使其自动热加载任何 Font Awesome 图标到一个更加动态的 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 是由 font-awesome 脚本设置的全局变量名称。

没有参数的 i2svg() 将重新解析整个 light dom(注意,这是不必要的,因为 font awesome 默认已经监视 dom 的更改)。使用参数,可以传递一个具有指向某个元素(light 或 shadow)的 node 属性的 js 对象,并按需生成您的 SVG。

对于那些想在 SPA/编译后的 js 前端中执行此操作的人,请查看 FA 文档,以获取更简单的导入解决方案。


0

我找到了一种在影子DOM中呈现SVG Font Awesome图标的方法。

根据文档@fortawesome/fontawesome-svg-core包提供了更多的控制。

这里有一个我使用https://codesandbox.io/s/romantic-panka-40xqm创建的示例。

基本思路是我们可以使用icon(faCamera).html[0]获取SVG HTML,并将其用于影子DOM根。

import { icon } from "@fortawesome/fontawesome-svg-core";
import { faCamera } from "@fortawesome/free-solid-svg-icons";

icon(faCamera).html[0]

0

你有没有考虑过使用SVG精灵图?经过几天寻找导入库的替代方案和弄清它如何与shadow dom一起工作,我发现SVG精灵图是最简单的解决方案。它的实现方式是使用svguse方法。请参见font awesome文档

最终我将精灵文件引入项目的dist文件夹中(在这种情况下我引入了bootstrap icons,但任何其他图标集都是相同的)。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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接