自定义元素类中扩展HTMLElement onclick事件无效的原因探究
嘿,我来帮你搞清楚这个自定义元素里onclick死活不生效的问题!核心原因其实是你混淆了HTMLElement的事件处理器属性和类的原型方法这两个概念,咱们一步步拆解:
首先得明确:浏览器触发点击事件时,只会找元素实例自身的onclick属性(而且得是个函数),不会去原型链上扒同名方法。咱们逐个看你的代码:
Case 1:类里定义的onclick()方法
onclick() { console.log('mySuperElement'); // 完全没反应 }
这个方法其实是挂在MyElement的原型上的,属于类的普通方法,浏览器的事件系统根本不会把它当成点击事件的处理器来调用。它和connectedCallback不一样——connectedCallback是自定义元素的生命周期钩子,是浏览器专门识别并自动调用的特殊方法,而onclick只是个普通属性,不是钩子。
Case 2 & Case 3:在constructor/connectedCallback里赋值this.onclick
// Case3 constructor() { super(); this.onclick = () => console.log('mySuperElement'); } // Case2 connectedCallback() { // ...其他代码 this.onclick = () => console.log('myElement'); }
理论上这种写法是有效的,但你说没生效,大概率是因为你点击的其实是内部的div,而不是my-element本身!你给my-element设置了display:block,但它的高度完全被内部50px的div撑开了,所以整个my-element的尺寸和div一模一样——你点击的每一处都是div的区域。如果div没有阻止事件冒泡,点击div本来应该触发父元素的onclick,但可能你测试时的Chrome 79有特殊行为,或者之前测试Case4的残留影响?
不过不管怎样,这种直接赋值onclick的方式本身就不太推荐,容易被覆盖,不如用更可靠的addEventListener。
addEventListener绑定事件 这是自定义元素中绑定事件的标准做法,不仅不会和原型方法混淆,还能绑定多个监听器,也不用担心this指向的问题:
class MyElement extends HTMLElement { constructor() { super(); // 绑定点击事件,箭头函数确保this指向当前元素实例 this.addEventListener('click', () => this.handleClick()); } connectedCallback() { this.style.display = 'block'; // 给my-element加个padding,这样就能看到点击空白区域的效果了 this.style.padding = '20px'; let div = document.createElement('div'); div.style.display = 'block'; div.style.height = '50px'; div.style.width = '50px'; div.style.backgroundColor = 'green'; this.appendChild(div); } // 自定义点击处理方法,名字随便取,不用非得叫onclick handleClick() { console.log('mySuperElement被点击啦!'); } } customElements.define('my-element', MyElement);
如果你非要用onclick属性赋值,那记得给my-element加个padding(像上面代码那样),这样你点击padding区域就能看到效果了——因为那部分是my-element自身的区域,不是div的。
你提到connectedCallback能正常工作,那是因为它是浏览器规定的自定义元素生命周期钩子,浏览器会在元素挂载到DOM时自动调用它。但onclick不是钩子,它只是个普通的事件处理器属性,你必须明确地给它赋值函数,或者用addEventListener绑定,浏览器才会在点击时触发它。
内容的提问来源于stack exchange,提问作者Kadgiko

