
在 ejs 模板中直接使用 document.getelementsbytagname('li').addeventlistener() 会报错,因为该方法返回的是 htmlcollection(类数组),而非单个元素,需遍历后为每个元素单独绑定事件。
在 ejs 模板中直接使用 document.getelementsbytagname('li').addeventlistener() 会报错,因为该方法返回的是 htmlcollection(类数组),而非单个元素,需遍历后为每个元素单独绑定事件。
在服务端渲染的 EJS 模板中,JavaScript 脚本若在 DOM 元素尚未完全加载或未正确获取目标节点时执行,极易引发运行时错误。你遇到的 Uncaught TypeError: document.getElementsByTagName(...).addEventListener is not a function 正是典型表现——getElementsByTagName() 返回的是 HTMLCollection(类似数组的对象),它本身没有 addEventListener 方法,只有单个 DOM 元素才具备该方法。
✅ 正确做法:遍历 + 绑定
你需要先获取所有 <li> 元素,再逐个为其添加事件监听器。推荐使用现代、语义清晰的写法:
<script type="text/javascript">
// 确保 DOM 已就绪(推荐方式)
document.addEventListener('DOMContentLoaded', function () {
const listItems = document.querySelectorAll('li.check'); // 更精准地选择带 class 的 li
listItems.forEach(function (item) {
item.addEventListener('click', function () {
console.log(2); // 直接输出 2,更符合预期
});
});
});
</script>? 提示:document.querySelectorAll('li.check') 比 getElementsByTagName('li') 更安全、更精确,且返回的是静态 NodeList(支持 forEach),避免因 DOM 动态变化导致的意外行为。
⚠️ 关键注意事项
脚本位置很重要:你的 <script> 标签位于模板底部(在 <li> 渲染之后),理论上可行;但为保险起见,仍建议包裹在 DOMContentLoaded 事件中,确保 DOM 完全解析。
-
EJS 渲染时机不可控:<% tasks.forEach(...) %> 是服务端渲染,生成的 <li> 在页面加载时已存在,因此无需事件委托(除非后续通过 JS 动态追加任务);但若未来支持前端增删任务,则应改用事件委托:
document.querySelector('ul').addEventListener('click', function (e) { if (e.target && e.target.matches('li.check')) { console.log(2); } }); -
避免 jQuery 和原生混用陷阱:你引入了 jQuery,但未使用 $(),若后续转向 jQuery 风格,可简写为:
$('li.check').on('click', function() { console.log(2); });
✅ 最终修正版(完整嵌入点)
将原错误脚本替换为以下健壮版本:
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('li.check').forEach(li => {
li.addEventListener('click', () => {
console.log(2); // ✅ 现在控制台将稳定输出 2
});
});
});
</script>这样既解决了类型错误,又提升了代码可维护性与兼容性。记住:操作 DOM 前务必确认元素存在,优先使用 querySelectorAll + forEach,并善用 DOMContentLoaded 保障执行时机。


















