繁体   English   中英

阴影域内的 FontAwesome svg

[英]FontAwesome svg within shadow dom

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

我正在尝试在没有 css 和脚本“流血”的现有网络表单项目中实现 angular 组件,关于如何执行此操作的任何其他建议? 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>

许多 oldskool 库都使用document. 访问主 DOM。
所以他们不能对shadowDOM中的内容做任何事情

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

如果您不想在 shadowDOM 之外流血 styles,则必须遵守规则:

  • 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

解决方案:

编写一个自定义元素

  • 创建自己的 lightDOM
  • 这是开槽的<slot></slot>反射!没有移动!
  • 从属性中获取配置
    <awesome-icon background="lightcoral" color="red"></awesome-icon>
    <awesome-icon background="lightgreen" color="green"></awesome-icon>
    <awesome-icon></awesome-icon>

JSFidlle: 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 和 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 package 提供更多控制

这是我使用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
})

FontAwesome是由 font-awesome 脚本设置的全局变量的名称。

没有参数的i2svg()重新解析整个 light dom(注意,这是不必要的,因为 font awesome 已经默认监视 dom 的变化)。 使用参数,可以传递带有指向某个元素(光或阴影)的node属性的 js object,它会根据需要生成您的 SVG。

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

您是否考虑过使用 SVG 精灵? 在寻找导入库的替代解决方案并弄清楚它如何与影子 dom 一起工作几天后,我发现 SVG 精灵是最简单的解决方案。 完成的方法是使用svguse方法。 请参阅字体真棒文档

我最终在项目的 dist 文件夹中引入了 sprite 文件(在这种情况下,我引入了bootstrap icons ,但任何其他图标集都是相同的)。 dist/icons/bootstrap-icons.svg

应用程序.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));
    }
  });

索引.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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM