addEventListener 在自定义元素中不起作用

addEventListener not working in custom element

我正在为我的网站制作一个大富翁游戏,我遇到了一个问题,我无法为自定义元素中的 "i" 元素添加事件侦听器

这是我的自定义元素:

class Popup extends HTMLElement {
  constructor() {
    super();
    var that = this;

    var shadow = this.attachShadow({mode: 'open'});
    var wrapper = document.createElement('div');
    wrapper.setAttribute("class","popup-wrapper");
    var popup = document.createElement('div');
    popup.setAttribute('class','popup');

    let exitButton = document.createElement('i');
    exitButton.className = "fas fa-times fa-lg exit";

    exitButton.addEventListener("click", function () {
      console.log('a');
    });

    // styles
    var style = document.createElement('style');
    style.textContent = `
      @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css");
      @import url("https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css");

      .popup-wrapper {
        position: fixed;
        top: 0; left: 0; right: 0; bottom: 0;
        background-color: rgba(0, 0, 0, .6);
        z-index: 9999;
        visibility: visible;
      }

      .popup {
        position: fixed;
        top: 50%; left: 50%;
        transform: translate(-50%, -50%);
        border: 1px solid #d6d6d6;
        background-color: #fff;
        border-radius: .25rem;
        padding: 1rem 2rem;
      }

      /* .exit {
        position: absolute;
        top: .5rem; right: .5rem;
        cursor: pointer;
      } */
    `;


    popup.appendChild(exitButton);

    wrapper.appendChild(popup);

    shadow.appendChild(style);
    shadow.appendChild(wrapper);

    this.isOpen = false;
    this.popup_wrapper = wrapper;
    this.popup = popup;
    this.exitButton = exitButton;
  }

  close() {
    console.log('a');
    this.remove();
  }
}

class TextPopup extends Popup {
  constructor() {
    super();

  }

  show() {
    this.exitButton.addEventListener("click", function () {
      console.log('a');
    });

    this.heading = this.getAttribute("heading");
    this.text = this.getAttribute("text");

    this.popup.innerHTML += `
      <h1>${this.heading}</h1>
      <p>${this.text}</p>
    `;
  }
}

customElements.define('text-popup', TextPopup);

我曾尝试将其放置在多个位置,但仍然无法正常工作

我还有另一个自定义元素,其中 addEventListener 起作用:

class Copy extends HTMLElement {
  constructor() {
    super();

    if (!this.hasAttribute('text')) return console.error("Text is not specified");

    this.text = this.getAttribute('text');
    this.style.cursor = "pointer";
    var that = this;
    this.addEvents(["click", "touchend"], () => {
      that.copy(that.text)
    });
  }

  copy(text) {
    navigator.clipboard.writeText(text).then(function() {
      // successfull
    }, function(err) {
      // unsuccessfull
    });
  }
}

customElements.define('copy-button', Copy);

PS:添加事件原型:

Element.prototype.addEvents = Document.prototype.addEvents = Window.prototype.addEvents = function (events, callback) {
  for (var i = 0; i < events.length; i++) this.addEventListener(events[i], callback);
};

我使用的是 chrome

的最新版本

感谢 @Jared Smith@Ken yo,我有了这个解决方案:

我发现有path,这是一个包含所有点击路径的数组

class TextPopup extends Popup {
  constructor() {
    super();
  }

  connectedCallback() {
    document.body.addEventListener("click", function (e) {
      if (e.path[0].classList.contains("exit")) {
        console.log("exitButton was clicked!");
      }
    });
  }

  show() {
    this.heading = this.getAttribute("heading");
    this.text = this.getAttribute("text");

    this.popup.innerHTML += `
      <h1>${this.heading}</h1>
      <p>${this.text}</p>
    `;
  }
}

一些建议:

  • ShadowDOM 中的

  • FontAwesome 和 Bootstrap CSS 需要在主文档中加载 CSS

  • 对于 shadowDOM 中的单击,您不必使用事件 Listener(用于添加多个侦听器的技术),设置 onclick 事件 处理程序 会做

  • 使用 <TEMPLATE> 而不是使用 JS 创建 HTML 更不容易出错(而且更简单,代码更少,并且..)

  • 使用 dispatchEvent 发出您自己的 CustomEvent,使您免于处理 path[0] 全局事件监听器中的数据

注意:CSS 在此 SO 片段中加载会导致点击后 3 秒的延迟...仅在 Whosebug

<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css">

<fa-button icon="fa-bars"></fa-button>
<div id=buttonbar>
  <fa-button icon="fa-address-card"></fa-button>
  <fa-button icon="fa-camera"></fa-button>
  <fa-button icon="fa-window-close"></fa-button>
</div>

<style>
  body {
    font-size: 48px; /* font settings cascade into shadowDOM */
  }
  #buttonbar {
    --buttonbackground: "grey"; /* CSS properties cascade into shadowDOM */
    --buttoncolor: black;
    float: right;
  }
</style>

<template id="FA-BUTTON">
  <style>
    @import url("//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css");
    :host {
      display: inline-block;
    }
    i {
      background: var(--buttonbackground, yellow);
      color: var(--buttoncolor, red);
    }
  </style>
  <i class="fas"></i>
</template>

<script>
  customElements.define('fa-button', class extends HTMLElement {
    constructor() {
      super() // returns 'this'
        .attachShadow({ mode: 'open' }) // returns AND sets 'this.shadowRoot'
        .append(document.getElementById(this.nodeName).content.cloneNode(true));
    }
    connectedCallback() {
      this.shadowRoot.querySelector("i").classList.add(this.getAttribute("icon"));
      this.onclick = () =>
        this.dispatchEvent(new CustomEvent("buttonclick", {
          detail: {
            clicked: this.getAttribute("icon")
          },
          bubbles: true,
          composed: true //escape shadowDOM
        }));
    }
  });

  document.body.addEventListener("buttonclick", (evt) => {
    console.log(evt.detail, evt.composedPath()); // full path into shadowDOM
  });

</script>