【问题标题】:HTML-based event listener for webcomponents/custom events (JS)用于 web 组件/自定义事件 (JS) 的基于 HTML 的事件侦听器
【发布时间】:2017-11-17 18:23:54
【问题描述】:

TL;DR; 是否可以在 HTML(不是 JS)中为自定义事件定义事件监听器?

基于此codepen,我正在尝试执行以下操作:

<my-checkreport
  onclick="myFunction()"
  oncheck="myFunction1)"
  check="myFunction()"
></my-checkreport>

myFunction 做了一些我可以在浏览器控制台中看到的console.log 的东西。当然,原生 onlick 有效,但对于下面定义的自定义事件,oncheckcheck 都不起作用(源代码取自上面链接的 codepen):

customElements.define('my-checkbox', class extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({mode: 'open'});
    this.checkEvent = new CustomEvent("check", {
      bubbles: true,
      cancelable: false,
    });
    shadowRoot.innerHTML = `
      <label>
        <input type="checkbox" id="my-checkbox"></input>
        Dummy Enabled
      </label>`;
    shadowRoot.querySelector('#my-checkbox').addEventListener('click', (e) => {
      console.log('checked', e.target.checked);
      this.dispatchEvent(this.checkEvent);
    });
  }
});

是否可以在 HTML 中为自定义事件附加事件侦听器?

【问题讨论】:

标签: javascript html web-component


【解决方案1】:

这是最接近模拟事件处理程序的 DOM 声明的方法。据我所知,这段代码完成了 DOM 为内置事件处理程序所做的一切。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>On-Event Test</title>
    <script>
    function onCheckHandler(event) {
      console.log('onCheckHandler: %O', event);
      event.stopPropagation();
      event.stopImmediatePropagation();
      //event.preventDefault();
    }

    function eventListener(event) {
      console.log('eventListener: %O', event);
    }
    (function() {
      var template = `<div>Click to Check</div>`;
      class MyEl extends HTMLElement {
        constructor() {
          super();

          var rootEl = this.attachShadow({mode: 'open'});
          rootEl.innerHTML = template;
          rootEl.firstChild.addEventListener('click', () => {
            var checkEvent = new CustomEvent("check", {bubbles:true,cancelable:true});
            if (this.dispatchEvent(checkEvent)) {
              // Do default operation here
              console.log('Performing default operation');
            }
          });
          this._onCheckFn = null;
        }

        static get observedAttributes() {
          return ['oncheck'];
        }

        attributeChangedCallback(attrName, oldVal, newVal) {
          if (attrName === 'oncheck' && oldVal !== newVal) {
            if (newVal === null) {
              this.oncheck = null;
            }
            else {
              this.oncheck = Function(`return function oncheck(event) {\n\t${newVal};\n};`)();
            }
          }
        }

        get oncheck() {
          return this._onCheckFn;
        }
        set oncheck(handler) {
          if (this._onCheckFn) {
            this.removeEventListener('check', this._onCheckFn);
            this._onCheckFn = null;
          }

          if (typeof handler === 'function') {
            this._onCheckFn = handler;
            this.addEventListener('check', this._onCheckFn);
          }
        }
      }

      // Define our web component
      customElements.define('my-el', MyEl);
    })();
    </script>
  </head>
  <body>
    <my-el oncheck="onCheckHandler(event)"></my-el>
  </body>
</html>

要使其正常工作,您需要两个步骤:

第一步:

组件代码必须支持属性更改。

当属性设置为字符串(要调用的函数)时,代码会创建一个临时函数,该函数尝试调用作为属性值提供的函数。然后将该函数传递到第二步。

当属性设置为其他任何值或属性被删除时,代码会将 null 传递到第二步。

第二步:

组件代码必须支持oncheck 属性。

无论何时更改此属性,它都需要通过调用 removeEventListener 来删除之前为此属性定义的任何事件处理程序。

如果属性的新值是一个函数,则调用addEventListener,将该函数作为处理程序。

这很有趣。起初,我认为基于 DOM 的 oncheck 处理程序永远是第一个。但是通过测试onclickaddEventHandler('click'),我发现事件处理程序是按收到的顺序添加的。如果在 DOM 中提供了onclick,则首先添加该事件侦听器。如果您随后调用 removeAttribute('onclick')setAttribute('onclick', 'handler(event)'),则该事件处理程序将移至列表末尾。

所以这段代码完全复制了我与click 事件相同的内容。

【讨论】:

    【解决方案2】:

    没有任何JS代码?不,但是您可以在元素的构造函数中定义一个属性来获取元素属性并对其进行评估。例如,

    constructor() {
        // Other code.
        const onCheckFunc = this.getAttribute('oncheck')
        this.addEventListener('check', () => eval(onCheckFunc))
        // Rest of your code.
    }
    

    编辑:正如@Intervalia 所述,您不应检查constructor 中元素的属性。您可以在connectedCallback 中执行此操作。但请记住:

    • 一般而言,工作应尽可能推迟到connectedCallback,尤其是涉及获取资源或渲染的工作。但是请注意,connectedCallback 可以多次调用,因此任何真正一次性的初始化工作都需要一个守卫来防止它运行两次。
    • 一般来说,constructor 应该用于设置初始状态和默认值,以及设置事件侦听器和可能的影子根。

    【讨论】:

    猜你喜欢
    • 2015-01-05
    • 1970-01-01
    • 2013-05-22
    • 2011-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 1970-01-01
    相关资源
    最近更新 更多