定义后修改自定义元素类

时间:2017-12-14 03:11:20

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

是否可以在注册后修改自定义元素/ Web组件类?这不是我通常(或曾经)想要在生产代码中做的事情,但在为开发进行原型设计或实现代码修补工具时,它会很有用。到目前为止,这是我尝试过但未成功的事情:

document.body.appendChild(document.createElement('foo-bar')) // empty element

class FooBar extends HTMLElement {
  connectedCallback () {
    this.textContent = 'foo bar'
  }
}

customElements.define('foo-bar', FooBar) // element shows "foo bar"

FooBar.protoype.connectedCallback = function () {
  this.textContent = 'foo bar 2'
}

document.body.appendChild(document.createElement('foo-bar')) // element shows "foo bar", not "foo bar 2"

customElements.get('foo-bar').protoype.connectedCallback = function () {
  this.textContent = 'foo bar 2'
}

document.body.appendChild(document.createElement('foo-bar')) // element shows "foo bar", not "foo bar 2"

class FooBar2 extends HTMLElement {
  connectedCallback () {
    this.textContent = 'foo bar 2'
  }
}

customElements.define('foo-bar', FooBar2) // Exception thrown, 'foo-bar' already defined

总之,修改最初传递给CustomElementsRegistry的类没有任何效果。分配给注册表中的内容的原型没有任何效果。尝试将新类分配给注册表中的元素是不可能的,因为抛出了异常。所有这三种行为都与我对其他JavaScript API几乎所有方面的体验相悖。无论好坏,除非将其设置为不可配置,否则无法改变某些内容是非常不寻常的。但是,当我检查对象是否可配置时,运行时会说它们是!

有人知道在定义自定义元素定义之后修改/扩充自定义元素定义的方法吗?

4 个答案:

答案 0 :(得分:0)

我认为很难改变标签的行为,他们(浏览器开发人员)可能认为一个标签应该只定义一次。

我测试了修改阴影根,它工作正常,如果你想模板化自定义标签,阴影根将有所帮助。

let customTable = document.registerElement('custom-table', {
  prototype: Object.create(HTMLElement.prototype, {
    createdCallback: {
      value: function() {
        let shadowRoot = this.createShadowRoot();
        window.shadowRoot = shadowRoot
        let template = document.querySelector('#custom-table');
        shadowRoot.appendChild(template.content.cloneNode(true));
      }
    }
  })
});

let i = 0
setInterval(() => {
  shadowRoot.append(`${i++}s past `)
}, 1000)
table {
  height: 100px;
  width: 100px;
  background-color: black;
}
<template id="custom-table">
  <style>
    table {
    }
  </style>
  <table>
    <thead>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>P</td>
        <td>P</td>
        <td>A</td>
        <td>P</td>
      </tr>
    </tbody>
  </table>
</template>
<custom-table></custom-table>
<table>
</table>

使用MVVM是另一种选择,它们也可以模板化东西,但实时修改可能需要手动触发重新渲染

  • angular(typescript,need compile)
  • react(jsx,need compile)
  • angular 1.x
  • VUE

答案 1 :(得分:0)

一旦定义,就无法更改标签,好像在定义时就拉入了类。

我找到了一种方法,可以通过将包装器类作为构造函数来动态地在自定义元素后面重新加载代码,我现在可以在自定义元素上进行热重装,不再需要刷新应用程序-包装器将需要处理属性冒泡等。 / p>

基本上,您需要依靠以下内容

  1. 知道未定义哪些元素,理想的是在对DOM进行更改时调用,或者可以放置setInterval进行开发。 document.querySelectorAll(':not(:defined)')

  2. 内联定义自定义元素,因此您可以对所有自定义元素使用相同的构造函数。 customElements.define(elementName, class extends HTMLElement {...


document.querySelectorAll(':not(:defined)').forEach((ele, k) => {
        let elementName = ele.tagName.toLowerCase();
        if (!customElements.get(elementName)) {

            //defining custom element
            customElements.define(elementName, class extends HTMLElement {
                constructor() {
                    super();
                }

                disconnectedCallback() {
                    if (this._elementClass) {
                        this._elementClass.disconnectedCallback()
                    }
                }

                connectedCallback() {

                    let element = (this.tagName).toLowerCase();

                    //reload the class variable here eg: importScript/getScripts 
                    //dependent on whatever variable you want eg: cache id or 
                    //then make calls to the customElement class
                    this._elementClass = new NodeTest(this);
                    this._elementClass.connectedCallback(); 


...


var NodeTest = class {

    constructor(ele) {
        this.ele = ele;
    }

    disconnectedCallback() {

    }

    connectedCallback() {

        this.render();
    }

    render() {
        this.ele.innerHTML = '<h1>hheee</h1>';
    }
}

答案 2 :(得分:0)

我实现了一个非常粗糙 prototype,可以让您customElements.define()个CustomElements 多次。这样,您便可以灵活地覆盖自定义元素将来的所有用法。

为CustomElement

修改现有的 class 的缺点是,您将丢失实际CustomElement及其实际类之间的映射,这可能是原因,为什么不可能...

加载上述原型实现后,您可以执行以下操作:

// add <foo-bar> to the DOM
// define it once
customElements.define('foo-bar', class extends HTMLElement {
  constructor() { super() };
  connectedCallback() { this.textContent = 'foo bar'; }
}
// ...
// and define it again :)
customElements.define('foo-bar', class extends HTMLElement {
  constructor() { super() };
  connectedCallback() { this.textContent = 'foo bar 2'; }
}
// and add it <foo-bar> again to the DOM to get the new impl

答案 3 :(得分:0)

我需要这个来进行热重载,我已经找到了解决方案,但是它使用clojurescript。最后,代码被编译为javascript,因此值得一看:https://clojureverse.org/t/hot-reloading-custom-elements-web-components/5964