【问题标题】:How to add JavaScript to Custom Elements?如何将 JavaScript 添加到自定义元素?
【发布时间】:2016-01-03 19:20:18
【问题描述】:

我有以下代码,它创建了一个用 Shadow DOM 封装的自定义元素:

'use strict'
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {

    var root = this.createShadowRoot();
    var divEl = document.createElement('div');
    divEl.setAttribute("id", "container");
    divEl.innerHTML =
        "<input id='input' type='text'>"
      + "<br>"
      + "Result: <span id='result'></span>"
      + "<br><button onclick='performTask()'>Run</button>";
    root.appendChild(divEl);

};
document.registerElement('custom-ele', {
    prototype: proto
});

这个想法是,当单击“运行”时,将从输入元素中获取输入并进行处理(在 performTask() 中),然后将输出放入“#result”中。我的两个问题是:

  1. 如何从 Shadow DOM 的输入字段中获取值?
  2. 如何将输出放入#result?

This 以前的堆栈溢出帖子看起来可以回答我的问题,但是所有建议的链接都不再有效,所以我想知道是否有人可以指出我正确的方向:)

附:我宁愿不使用模板,因为并非所有浏览器都支持 HTML 导入,我希望我的所有自定义元素代码都包含在一个文件中。

【问题讨论】:

  • 我建议在创建或事件委托后附加事件。
  • 这里面的javascript在哪里?

标签: javascript web-component shadow-dom custom-element


【解决方案1】:

原来你可以向影子根本身添加函数,然后你可以在影子根上调用 this.parentNode.fn() 直接让孩子访问 shadowRoot...

proto.createdCallback = function() {

    let root = this.createShadowRoot();

    root.innerHTML = "<input id='input' type='text'>"
        + "<br>"
        + "Result: <span id='result'></span>"
        + "<br><button onclick='this.parentNode.process()'>Run</button>";

    this.shadowRoot.process = function() {
        let spanEle = this.querySelector('span');
        let inputEle = this.querySelector('input');
        spanEle.textContent = performAlgorithm(inputEle.value.split(','));
    };
};
document.registerElement('custom-ele', { prototype: proto });

(感谢 MarcG 让我初步了解)

【讨论】:

  • 我认为这是一种相当非正统的做事方式。您正在将方法添加到 shadowRoot 对象,而不是将它们添加到它们所属的自定义元素中。做什么的?还有,怎么给按钮添加事件监听,performAlgorithm函数在哪里定义?
  • performAlgorithm 在同一个文件中,我将 process() 添加到 shadow root 的原因是我可以使用标记而不是将整个东西与 javascript 混合在一起,这非常乏味。在这种情况下没有添加事件侦听器,它是通过 onclick 属性“this.parentNode.process()”调用的空)
  • @Ashley,出于安全原因,您应该避免使用内联脚本(onclick 表达式)。相反,您可以直接在createdCallback(或attachedCallback)中定义它:this.shadowRoot.querySelector( "button" ).onclick = function () { this.parentNode.process() }
  • 出于安全原因?哈哈。 @Ashley:我很高兴能帮上忙。再见。
【解决方案2】:

关闭

您可以在 Shadow DOM root 上使用方法 querySelector 来获取内部元素:

'use strict'
var proto = Object.create( HTMLElement.prototype )
proto.createdCallback = function ()
{
    //HTML ROOT
    var root = this.createShadowRoot()
    root.innerHTML = "<input id='input' type='text'>"
        + "<br>"
        + "Result: <span id='result'></span>"
        + "<br><button>Run</button>"

    //UI
    var buttonEle = root.querySelector( "button" )
    var inputEle = root.querySelector( "input" )
    var spanEle = root.querySelector( "#result" )
    buttonEle.onclick = function ()
    {
        var input = inputEle.value
        // do some processing...
        spanEle.textContent = input
    } 
}
document.registerElement( 'custom-ele', { prototype: proto } )

注意:您可以在同一页面中使用 template 而不使用 HTML 导入。看下面的sn-p:

<html>

<body>
  <custom-ele></custom-ele>

  <template id="custelem">
    <input id='input' type='text'>
    <br>Result:
    <span id='result'></span>
    <br>
    <button>Run</button>
  </template>

  <script>
    var proto = Object.create(HTMLElement.prototype)
    proto.createdCallback = function() {
      //HTML ROOT
      var root = this.createShadowRoot()
      root.innerHTML = custelem.innerHTML

      //UI
      var buttonEle = root.querySelector("button")
      var inputEle = root.querySelector("input")
      var spanEle = root.querySelector("#result")
      buttonEle.onclick = function() {
        var input = inputEle.value
          // do some processing...
        spanEle.textContent = input
      }
    }
    document.registerElement('custom-ele', {
      prototype: proto
    })
  </script>


</body>

</html>

没有关闭

如果你不想使用闭包,你可以在你的自定义元素上声明一个名为 handleEvent 的方法,并添加一个事件监听器来重定向它:

proto.createdCallback = function ()
{
    //HTML ROOT
    var root = this.createShadowRoot()
    root.innerHTML = custelem.innerHTML

    //EVENT
    var buttonEle = root.querySelector( "button" )
    buttonEle.addEventListener( "click", this )
}

proto.handleEvent = function ( ev )
{
    var inputEle = this.shadowRoot.querySelector( "input" )
    var spanEle = this.shadowRoot.querySelector( "#result" )
    // do some processing...
    spanEle.textContent = inputEle.value
}

【讨论】:

  • 嗯,不完全是。我的解决方案会让 createdCallback 被垃圾回收,而你的不会。
  • 感谢您的回复。在您的代码 sn-p 中,buttonEle.onclick 引用了 inputEle,这是 createdCallback 范围的一部分……这不会像我的解决方案一样阻止 createdCallback 被垃圾收集吗?至于模板,我希望我所有的自定义元素代码都被限制在一个文件中,这样我所要做的就是导入一个文件,然后能够以最小的努力使用新元素。
  • @CodeMedic 是的,我的第一个示例是使用闭包上下文,因为我认为这是最好和最快的方法。我将发布第二个不使用闭包上下文的示例。 Snippet 也被修改为合并 javascript 和 html 代码。
  • @MarcG。你错了。您的bind 调用将生成一个比简单闭包大得多的本地上下文。 Bind 依赖于闭包本身,因此从 GC 的角度来看,它无济于事,抱歉……当我发布时,您的答案也不正确且不完整。实际上,它仍然是错误的、不精确的,而且……比我的慢几十倍。准确吗?!
  • @Supersharp:首先你从我的回答中复制了querySelector。然后现在你更新了你的答案来复制我声明的方法proto.process,你称之为process.handleEvent。但是你甚至没有调用你的proto.handleEvent 函数。您能否通过至少复制我的所有答案来更正您的答案?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-14
  • 2016-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多