【问题标题】:Elements declared in HTML after Custom Element do not render自定义元素后在 HTML 中声明的元素不呈现
【发布时间】:2019-04-14 02:41:00
【问题描述】:

真正的大编辑: 您好,这是第 3 号编辑。这是最重要的,因为它改变了这篇文章的基本问题。 简而言之,我尝试将h1s 放在单个自定义元素之前和之后。好吧...只有我在 before 渲染之前放置的内容。所以,我猜问题是 在我的自定义元素之后没有呈现任何元素。 我不会出于记录目的而删除下面的旧观察结果,因为可能有人碰巧遇到了这个问题并理解错了,就像我一样。


我正在 Chrome 上尝试使用 Custom Elements v1,但事情不太对劲。

有问题的自定义元素——我称之为range-slider——只是一堆1 label、1个input range和1个input number放在一起并设置样式,加上numberrange绑定到彼此。

RangeSlider.js:

class RangeSlider extends HTMLElement {
    constructor() {
        super();
    }
    connectedCallback() {
        this.initShadowDom();
        this.flowInputToElem("field", "slider");
        this.flowInputToElem("slider", "field");
    }

    initShadowDom() {
        const shadowRoot = this.attachShadow({mode: "open"});
        shadowRoot.innerHTML = this.template;
    }
    flowInputToElem(inputId, elemId) {
        const shadowRoot = this.shadowRoot;
        const input = shadowRoot.getElementById(inputId);
        const elem = shadowRoot.getElementById(elemId);

        input.addEventListener("input", e => {
            const min = parseFloat(elem.min);
            const max = parseFloat(elem.max);
            const newVal = parseFloat(input.value);

            elem.value = newVal > max
                ? max
                : newVal < min
                    ? min
                    : newVal;
        });
    }

    get label() {
        return this.getAttribute("label") || "Range slider";
    }
    get min() {
        return this.getAttribute("min") || 0;
    }
    get max() {
        return this.getAttribute("max");
    }
    get value() {
        return this.getAttribute("value") || 0;
    }

    get template() {
        return `
            <style>
                @import url('style/appearance.css');
                @import url('style/color.css');
                @import url('style/size.css');
                @import url('style/position.css');
                @import url('components/rangeSlider/style/slider.css');
            </style>
            <form class="w-max arial">
                <label for="field">${this.label}:</label>
                <div class="flx flx-middle">
                    <input id="slider" type="range" min="${this.min}" max="${this.max}" value="${this.value}" class="slider 
                        w-big">
                    <input id="field" type="number" min="0" max="${Infinity}" value="${this.value}" class="mrg-none 
                        spin-none brd-none w-small bg-transp txt-center">
                </div>
            </form>
        `;
    }
}

window.customElements.define('range-slider', RangeSlider);

据我了解,range-slider 似乎一切正常。事实上,它有效;嗯,有点。如果我在 index.html 上放置了超过 1 个range-slider,则只会呈现第一个。

index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Pegada de carbono</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" type="text/css" href="style/position.css">
    <link rel="stylesheet" type="text/css" href="style/color.css">
    <link rel="stylesheet" type="text/css" href="style/size.css">

    <script src="components/rangeSlider/RangeSlider.js"></script>
</head>
<body class="pos-abs h-fill w-fill mrg-none pdg-none bg-white">
    <range-slider label="example 1" min="0" max="100" value="25"></range-slider>
    <range-slider label="example 2" min="0" max="100" value="45"></range-slider>
    <range-slider label="example 3" min="0" max="100" value="65"></range-slider>
</body>
</html>

铬:

这是为什么呢?

编辑: 奇怪的是,当我通过 JavaScript 附加元素时,它会按预期呈现。

const rangeSlider = document.createElement("range-slider");
rangeSlider.label = "example 4";
rangeSlider.value = "65";
rangeSlider.max = "1000";
rangeSlider.min = "0";

document.body.appendChild(rangeSlider);

编辑 2: 好的,我做了更多的测试,发生了更奇怪的事情: 1. 如果我放置 1 个原生元素,则放置 1 个 range-slider(反之亦然), 一切正常; 2.如果我把range-slider,原生组件然后另一个range-slider,只有第一个range-slider呈现; (这意味着即使是原生组件也不好) 3. 如果我尝试在另一个自定义元素中添加range-sliders,外部自定义元素是好的,range-slider 也是 1,但所有其他 range-sliders 都不是。

【问题讨论】:

  • 更新了帖子,提供了更多细节。
  • 更新了帖子以解决新问题。简而言之,我认为我在渲染 N 元自定义元素时遇到了问题。经过一些测试,我似乎无法在自定义元素之后呈现 HTML 声明的 any 元素。

标签: javascript html google-chrome shadow-dom custom-element


【解决方案1】:

我拿了你的代码并稍微修改了一下,然后在这个页面上以 sn-p 模式运行,它运行良好。

    class OtherEl  extends HTMLElement {
      constructor() {
        super();

        this.attachShadow({mode: "open"}).innerHTML = '<div style="background-color: yellow;padding:4px;"><range-slider label="inner slider" min="0" max="100" value="5"></range-slider></div>';
      }
    }

    window.customElements.define('other-el', OtherEl);

    class RangeSlider extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({mode: "open"});
      }

      connectedCallback() {
        if (this.shadowRoot.childElementCount === 0) {
          this.shadowRoot.innerHTML = this.template;
          this.flowInputToElem("field", "slider");
          this.flowInputToElem("slider", "field");
        }
      }

      flowInputToElem(inputId, elemId) {
        const shadowRoot = this.shadowRoot;
        const input = shadowRoot.getElementById(inputId);
        const elem = shadowRoot.getElementById(elemId);

        input.addEventListener("input", e => {
          const min = elem.min;
          const max = elem.max;
          const newVal = input.value;
          elem.value = newVal > max ? max : newVal < min ? min : newVal;
        });
      }

      get label() {
        return this.getAttribute("label") || "Range slider";
      }
      get min() {
        return parseFloat(this.getAttribute("min") || '0');
      }
      get max() {
        return parseFloat(this.getAttribute("max") || '100');
      }
      get value() {
        return parseFloat(this.getAttribute("value") || '0');
      }

      get template() {
        return `
          <style>
            @import url('style/appearance.css');
            @import url('style/color.css');
            @import url('style/size.css');
            @import url('style/position.css');
            @import url('components/rangeSlider/style/slider.css');
          </style>
          <form class="w-max arial">
            <label for="field">${this.label}:</label>
            <div class="flx flx-middle">
              <input id="slider" type="range" min="${this.min}" max="${this.max}" value="${this.value}" class="slider w-big">
              <input id="field" type="number" min="0" max="${Infinity}" value="${this.value}" class="mrg-none spin-none brd-none w-small bg-transp txt-center">
              </div>
          </form>
        `;
      }
    }

    window.customElements.define('range-slider', RangeSlider);
  <h1>Before</h1>
  <range-slider label="example 1" min="0" max="100" value="25"></range-slider>
  <h2>After 1</h2>
  <range-slider label="example 2" min="0" max="100" value="45"></range-slider>
  <h2>After 2</h2>
  <range-slider label="example 3" min="0" max="100" value="65"></range-slider>
  <h2>After 3</h2>
  <other-el></other-el>
  <h2>After OtherEl</h2>

然后我在我的服务器上运行它并看到了你所看到的。只显示了一个元素。

但我在开发工具中发现了这个错误:

由于我没有您的 CSS 文件,我的 CSS 没有加载,这导致 Web 组件代码中的某些内容崩溃。

当我在您的模板中注释掉 &lt;style&gt; 标记时:

  get template() {
    return `
      <!-- <style>
        @import url('style/appearance.css');
        @import url('style/color.css');
        @import url('style/size.css');
        @import url('style/position.css');
        @import url('components/rangeSlider/style/slider.css');
      </style> -->
      <form class="w-max arial">
        <label for="field">${this.label}:</label>
        <div class="flx flx-middle">
          <input id="slider" type="range" min="${this.min}" max="${this.max}" value="${this.value}" class="slider w-big">
          <input id="field" type="number" min="0" max="${Infinity}" value="${this.value}" class="mrg-none spin-none brd-none w-small bg-transp txt-center">
          </div>
      </form>
    `;
  }
}

然后所有 5 个都出现了,包括在我的另一个元素中的那个。

【讨论】:

    猜你喜欢
    • 2021-12-09
    • 2020-05-22
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 2016-04-02
    相关资源
    最近更新 更多