【问题标题】:How will I access the components in the script inside a template?如何访问模板内脚本中的组件?
【发布时间】:2020-10-06 20:27:48
【问题描述】:

我想重用我的包含一些 javascript 代码的 html 组件,所以为了简化我举一个简单的例子:

index.html

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <my-component></my-component>
    <script src="index.js"></script>
  </body>
</html>

my-component.html

<template>
  <div id="something"></div>
  <script>
    // It doesn't work, this here is "window"
    document.getElementById("something").innerHTML = "Something"
  </script>
</template>

index.js

window.makeComponent = (function () {
    function fetchAndParse(url) {
    return fetch(url, {mode: "no-cors"})
        .then(res => res.text())
        .then(html => {
        const parser = new DOMParser()
        const document = parser.parseFromString(html, 'text/html')
        const head = document.head
        const template = head.querySelector('template')
        return template
        })
    }
    function defineComponent(name, template) {
    class UnityComponent extends HTMLElement {
        connectedCallback() {
        const shadow = this.attachShadow({mode: 'open'})
        shadow.appendChild(document.importNode(template.content, true))
        }
    }
    return customElements.define(name, UnityComponent)
    }
    function loadComponent (name, url) {
    fetchAndParse(url).then((template) => defineComponent(name, template))
    }

    return {loadComponent}
}())
makeComponent.loadComponent("my-component", "my-component.html")

我可以使用这段代码,但是它将脚本的所有变量复制到窗口:

<template>
  <div id="something"></div>
  <style onload="templFunc.apply(this.getRootNode())"></style>
  <script>
    function templFunc() {
    // It works
    let text = "Something"
    this.querySelector('#something').innerHTML = text
    // but...
    console.log(window.text) // => "Something"
    }
  </script>
</template>

没有意义,如果脚本在模板内至少应该可以访问模板内的元素,否则模板几乎不能用于javascript,所以,我无法理解的意图在模板里面使用脚本或者如何复用使用javascript的web组件,这样做有错吗?

那么,如何在不将所有脚本变量复制到窗口的情况下访问模板内脚本中的组件?

【问题讨论】:

  • 您可以将索引文件设为 php 并包含或需要其中的组件。
  • 我没有使用 php。

标签: javascript html web-component shadow-dom


【解决方案1】:

正如您发现&lt;script&gt;&lt;template&gt; 内运行在全局范围

如果您使用 Angular,请注意 Angular 从模板中删除所有 &lt;script&gt; 内容。

一种解决方法是添加一个 HTML 元素来触发代码元素范围内。

&lt;img src onerror="[CODE]"&gt; 是最有可能的候选人:

这可以调用一个全局函数,或者立即运行this.getRootNode().host

<template id=scriptContainer>
  <script>
    console.log("script runs in Global scope!!");

    function GlobalFunction(scope, marker) {
      scope = scope.getRootNode().host || scope;
      console.log('run', marker, 'scope:', scope);
      scope.elementMethod && scope.elementMethod();
    }
  </script>

  <img src onerror="(()=>{
    this.onerror = null;// prevent endless loop if function generates an error
    GlobalFunction(this,'fromIMGonerror');
  })()">

</template>

<my-element id=ONE></my-element>
<my-element id=TWO></my-element>

<script>
  console.log('START SCRIPT');
  customElements.define('my-element',
    class extends HTMLElement {
      connectedCallback() {
        console.log('connectedCallback', this.id);
        this.attachShadow({ mode: 'open' })
            .append(scriptContainer.content.cloneNode(true));
      }
    });

</script>

更详细的游乐场,包括注入 SCRIPT,位于:https://jsfiddle.net/CustomElementsExamples/g134yp7v/

【讨论】:

    【解决方案2】:

    这是解决方案,

    我的组件

    <template>
      <div id="something"></div>
      <script>
        makeComponent.getComponent("my-component", "something").innerHTML = "Something"
      </script>
    </template>
    

    index.js

    window.makeComponent = (function () {
      function fetchAndParse(url) {
        return fetch(url, { mode: "no-cors" })
          .then((res) => res.text())
          .then((html) => {
            const parser = new DOMParser();
            const document = parser.parseFromString(html, "text/html");
            const head = document.head;
            const template = head.querySelector("template");
            return template;
          });
      }
      function defineComponent(name, template) {
        class UnityComponent extends HTMLElement {
          connectedCallback() {
            const shadow = this.attachShadow({ mode: "open" });
            this.setAttribute("id", name);
            shadow.appendChild(document.importNode(template.content, true));
          }
        }
        return customElements.define(name, UnityComponent);
      }
      function getComponent(host, query) {
        return document.getElementById(host).shadowRoot.querySelector(query);
      }
      function loadComponent(name, url) {
        fetchAndParse(url).then((template) => defineComponent(name, template));
      }
    
      return { getComponent, loadComponent };
    })();
    makeComponent.loadComponent("my-component", "my-component.html");
    
    

    但是我认为这不是更好的方法,也许我需要在这里使用事件,并将影子范围传递给模板中脚本标记中调用的侦听器,但我不知道如何传递事件的范围还没有。

    向上:

    有事件:

    我的组件

    <template>
      <div id="something"></div>
      <script>
         document.addEventListener("custom-event", (e) => {
             console.log(e.detail.target.shadowRoot.getElementById("date-year"));
         })
      </script>
    </template>
    

    index.js

    window.makeComponent = (function () {
      function fetchAndParse(url) {
        return fetch(url, { mode: "no-cors" })
          .then((res) => res.text())
          .then((html) => {
            const parser = new DOMParser();
            const document = parser.parseFromString(html, "text/html");
            const head = document.head;
            const template = head.querySelector("template");
            return template;
          });
      }
      function defineComponent(name, template) {
        class UnityComponent extends HTMLElement {
          connectedCallback() {
            const shadow = this.attachShadow({ mode: "open" });
            shadow.appendChild(document.importNode(template.content, true));
            const event = new CustomEvent("custom-event", {'detail': {
                target: this
            }});
            document.dispatchEvent(event);
          }
        }
        return customElements.define(name, UnityComponent);
      }
      function loadComponent(name, url) {
        fetchAndParse(url).then((template) => defineComponent(name, template));
      }
    
      return { loadComponent };
    })();
    makeComponent.loadComponent("my-component", "my-component.html");
    
    

    但是,我什至更喜欢第一种解决方案。但是如果你需要嵌套组件,第一个不起作用,你需要第二个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-17
      • 2013-06-19
      • 2012-04-13
      • 1970-01-01
      • 1970-01-01
      • 2015-05-31
      • 2018-09-23
      • 1970-01-01
      相关资源
      最近更新 更多