首页 > 解决方案 > 如何访问模板内脚本中的组件?

问题描述

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

索引.html

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

我的组件.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 组件,这样做有错吗?

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

标签: javascripthtmlweb-componentshadow-dom

解决方案


正如您在全局范围<script>内的<template>运行中发现的那样

如果您使用 Angular,请注意 Angular会直接从模板中删除所有<script>内容。

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

<img src onerror="[CODE]">是最有可能的候选人:

然后可以调用全局函数,或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/


推荐阅读