Vanilla Web Component 自定义事件属性和特性

Vanilla Web Component custom event attributes and properties

在没有任何框架的情况下使用Web Components,实现自定义事件的正确方法是什么?例如,假设我有一个自定义元素 x-pop-out,它有一个自定义事件 pop 我希望以下所有内容都有效:

<x-pop-out onpop="someGlobal.doSomething()"/>

var el = document.getElementsByTagName('x-pop-out')[0];
el.onpop = ()=> someGlobal.doSomething();
//or
el.addEventListener('pop', ()=> someGlobal.doSomething());

最后一个我得到了怎么做,但我需要自定义实现属性和每个 getter / setter 吗?另外,eval() 是从属性执行字符串的适当方法吗?

事件侦听器解决方案(第三个)是最简单的,因为您不必定义任何特殊的东西来捕捉事件。

事件处理程序解决方案需要eval()(第一个,来自属性)或显式调用函数(第二个)。

如果您不能使用 eval,您可以改为解析属性字符串。

customElements.define( 'x-pop-out', class extends HTMLElement {
    connectedCallback() {
        this.innerHTML = `<button id="Btn">pop</button>`

        this.querySelector( 'button' ).onclick = () => {
            this.dispatchEvent( new CustomEvent( 'pop' ) )
            if ( this.onpop )
                this.onpop()
            else
                eval( this.getAttribute( 'onpop' ) )
        }            
    }
} )

XPO.addEventListener( 'pop', () => console.info( 'pop' ) )
<x-pop-out id=XPO onpop="console.log( 'onpop attribute' )"></x-pop-out>
<hr>
<button onclick="XPO.onpop = () => console.log( 'onpop override' )">redefine onpop</button>

基于 SuperSharp 。他的建议仅适用于控制台日志,实际上不适用于事件。我使用的解决方案是覆盖 dispatchEvent 并使其也创建自定义 HTML 事件属性。

按照'on'+event.type的使用标准。我们使用 with 在一个新函数中屏蔽该属性,并检查该属性是否是一个函数,并在需要时传入事件。

dispatchEvent(event) {
  super.dispatchEvent(event);
  const eventFire = this['on' + event.type];
  if ( eventFire ) {
    eventFire(event);
  } else {
  const func = new Function('e',
    'with(document) {'
       + 'with(this) {'
         + 'let attr = ' + this.getAttribute('on' + event.type) +';'
         + 'if(typeof attr === \'function\') { attr(e)};
       + }'
     + '}'
   );
   func.call(this, event);
   } 
}

示例:

class UserCard extends HTMLElement {

  constructor() {
    // If you define a constructor, always call super() first as it is required by the CE spec.

    super(); //

  }


  dispatchEvent(event) {
    super.dispatchEvent(event);

    const eventFire = this['on' + event.type];
    if (eventFire) {
      eventFire(event);
    } else {
      const func = new Function('e',

        'with(document) {' +
        'with(this) {' +
        'let attr = ' + this.getAttribute('on' + event.type) + ';' +
        'if(typeof attr === \'function\') { attr(e)};' +
        '}' +
        '}');
      func.call(this, event);
    }

  }

  connectedCallback() {
    this.innerHTML = `<label>User Name</label> <input type="text" id="userName"/>
   <label>Password</label> <input type="password" id="passWord"/>
    <span id="login">login</span>`;
    this.test = this.querySelector("#login");
    this.test.addEventListener("click", (event) => {
      this.dispatchEvent(
        new CustomEvent('pop', {
          detail: {
            username: 'hardcodeduser',
            password: 'hardcodedpass'
          }
        })
      );
    })


  }

}

customElements.define('user-card', UserCard);

function onPop2(event) {
  debugger;
  console.log('from function');
}
<user-card id="XPO" onpop="onPop2"></user-card>
<hr>
<user-card id="XPO" onpop="console.log('direct Console')"></user-card>