如何从 javascript class 渲染方法添加点击事件,该方法调用相同的 class 上的方法?

How can I add a click event from a javascript class render method that calls a method on the same class?

我有一个 JS class:

class MyClass {
    constructor() {
    }

    handleClick() {
        alert('clicked');
    }

    render() {
        return `<input type="radio" onClick="${this.handleClick} /><label>My Label</label>"`;
    }
}

window.addEventListener('load', init);

function init() {
    const myClassInstance = new MyClass();
    const el = document.getElementsByClassName('myelement')[0];
    el.insertAdjacentHTML('beforeend', myClassInstance.render());
}

这会在带有 class myelement 的元素中呈现单选输入,但是当我单击单选按钮时,我会看到:

Uncaught SyntaxError: Unexpected token {

错误是指 handleClick 方法。

我的语法有问题吗?如果我使用 onClick="${this.handleClick()}",我看不到错误,但会立即调用该函数,而不是在单击时调用。我做错了什么?

您最好不要使用标记,因为您从 onxyz-属性式事件处理程序调用的函数必须是全局的。

相反,创建元素然后为其分配函数:

class MyClass {
    constructor() {
    }

    handleClick(e) {
        alert('clicked');
    }

    render() {
        var input = document.createElement("input");
        input.type = "radio";
        input.addEventListener("click", e => this.handleClick(e));
        return input;
    }
}

window.addEventListener('load', init);

function init() {
    const myClassInstance = new MyClass();
    const el = document.getElementsByClassName('myelement')[0];
    el.appendChild(myClassInstance.render());
}
<div class="myelement"></div>

注意箭头函数的使用,因此当我们调用 this.handleClickthis 是正确的。显然,您不需要它只用于 alert,但我假设您会想要使用 MyClass 实例的属性。 (要访问发生点击的元素,请使用 e.currentTarget,因为它不会是 this。)


如果您强烈希望使用标记来执行此操作,一个选项是:

  • 具有 class 的消费者应该调用的 initEvents 方法或类似方法(就像他们应该调用 render
  • 在标记中使用唯一 ID
  • 使用该 ID 查找元素并连接事件

或者更好:让 render 接受要添加的元素,并在添加后进行事件连接。

但使用您现有的 render 方法:

const MyClass = (() => {
  let nextId = 1;
  class MyClass {
      constructor() {
          this.id = `__MyClass__${nextId++}`;
      }

      handleClick(e) {
          alert('clicked');
      }

      render() {
          return `<input type="radio" id="${this.id}" />`;
      }
      
      initEvents() {
          const e = document.getElementById(this.id);
          if (!e) {
              throw new Error("initEvents must be called after appending the markup from render to the DOM");
          }
          e.addEventListener("click", e => this.handleClick(e));
      }
  }
  return MyClass;
})();

window.addEventListener('load', init);

function init() {
    const myClassInstance = new MyClass();
    const el = document.getElementsByClassName('myelement')[0];
    el.insertAdjacentHTML('beforeend', myClassInstance.render());
    myClassInstance.initEvents();
}
<div class="myelement"></div>

如果你想让这个 onclick 工作,那么你需要将全局函数分配给 onclick 否则你不能这样做我做了快速修复这只是一个临时解决方案你可以选择这个或@T.J 答案看起来也不错。

class MyClass {
    constructor() {}

    handleClick() {
        alert('clicked');
    }

    render() {
        const globalId = "h" + Math.floor((Math.random() * 100) + 12345);
        window[globalId] = this.handleClick;
        return `<input type="radio" onClick=window['${globalId}']() />`;
    }
}

window.addEventListener('load', init);

function init() {
    const myClassInstance = new MyClass();
    const el = document.getElementsByClassName('myelement')[0];
    el.insertAdjacentHTML('beforeend', myClassInstance.render());
}
<div class="myelement"></div>