开发者问题收集

向可能不在页面上的元素添加事件监听器?

2021-04-17
719

向页面上可能未出现的元素添加事件侦听器的最佳做法是什么?

例如,假设我们有一个出现在文章页面上的评论表单,并附加了 submit 事件侦听器。

document.getElementById('commentform').addEventListener('submit', event => {
    /* do something */
})

此代码放置在站点模板的 script.js 文件中,该文件在每个页面上加载。但是,未出现评论表单的页面将导致错误:

Uncaught TypeError: Cannot read property 'addEventListener' of null...

我们如何防止发生此 TypeError?

2个回答

简单的方法是先检查元素是否存在...

const commentForm = document.getElementById('commentform');
if (commentForm) {
  commentForm.addEventListener('submit', event => {
    /* do something */
  })
}

在现代环境中,您可以使用可选链:

document.getElementById('commentform')?.addEventListener('submit', event => {
    /* do something */
})

但更好的方法是设置页面上的 commentform 与其相关脚本 完全连接 ,这样就不会出现没有另一个的脚本。通常,人们会使用一个框架来实现这一点,将 commentform 的 HTML 与其提交监听器集成在一起。这是我推荐的专业方法。如果没有这个,您也可以将 commentform 的脚本放在 commentform 旁边:

<form id="commentform">
  ...
</form>
<script>
document.getElementById('commentform').addEventListener('submit', event => {
    /* do something */
})
</script>
CertainPerformance
2021-04-17

不是最佳做法

正如 @CertainPerformance 所说,在生产代码中,您需要设置脚本加载,以便永远不会发生这种情况。但是,我在用户脚本环境中使用的一个技巧是使用 MutationObserver ,因为我对加载没有太多控制权。

window.addEventListener('load', function() {

    let button = document.querySelector('.blahblahblah');
    if (button) {
        button.click();
        console.log('Button found early');
    }

    // This waits to run until ".somerootelement" has changed
    let mo = new MutationObserver(function() {
        document.querySelector('.blahblahblah').click();
        console.log('Button found late');
    });
    mo.observe(document.querySelector('.somerootelement'), { childList: true });
});

但我想说 90% 的时间这是一种反模式。当然,如果您可以更改脚本加载,以便依赖项正确加载。

xdhmoore
2021-04-17