JavaScript箭头函数中如何选取当前点击元素的相邻DOM节点
问题说明
现有某设置菜单的JS与HTML实现,菜单展示URL列表,每条URL旁配有一个'x'删除按钮,为某RSS应用的二次开发版本,实际运行效果如下:
x foo.com x bar.com/rss x baz.com/feed
renderSettings() 函数负责将JSON数组中的数据注入到<template id="settings-feed-item">模板节点中,模板内容不会直接在页面预览中展示,该函数同时会为每个删除按钮绑定onclick事件。
当前需求:点击某条URL旁的删除按钮时,为同条目的URL节点应用指定CSS样式。
现存问题:无法正确选中点击按钮对应的URL节点,尝试使用this关键字、closest()方法均失败,控制台抛出TypeError: undefined is not an object错误。
现有代码
HTML
<section id="feedsection"> <ul id="feeds"> <template id="settings-feed-item"> <li> <a id="remove">x </a><span id="feedurl"></span><a></a> </li> </template> </ul> </section>
JS
// 渲染设置菜单 function renderSettings() { keywords.value = state.keywords; newsFeeds.innerHTML = ''; state.feeds.forEach(f => { const el = document.importNode(feedItem.content, true).querySelector('li'); el.querySelector('span').innerText = simplifyLink(f.url); // ... // 绑定删除按钮点击事件 el.querySelectorAll('a')[0].onclick = () => { // 此处需要选中点击按钮旁的URL节点 }; // ... }); }
错误原因
- 事件绑定使用了箭头函数,箭头函数不绑定自身的
this上下文,因此回调内无法通过this获取触发事件的按钮元素,这是this相关写法失效的核心原因。 - 模板内重复使用
id属性:id="remove"、id="feedurl"违反HTML规范,id要求全局唯一,列表循环渲染多份条目后会出现id重复,导致DOM选择器匹配异常。 - 原代码片段未将导入模板生成的
li元素插入到页面DOM树中,也可能导致DOM查找方法执行失败。
正确实现
- 首先修改模板,将重复id替换为class,避免全局id冲突:
<template id="settings-feed-item"> <li> <a class="remove">x </a><span class="feedurl"></span><a></a> </li> </template>
- 修改JS逻辑,在循环渲染条目时提前缓存当前条目的相关元素,事件回调内直接操作缓存的节点即可,无需额外做DOM查找,同时补全节点插入逻辑:
function renderSettings() { keywords.value = state.keywords; newsFeeds.innerHTML = ''; state.feeds.forEach(f => { const el = document.importNode(feedItem.content, true).querySelector('li'); // 提前缓存当前条目的URL节点和删除按钮 const feedUrlEl = el.querySelector('.feedurl'); const removeBtn = el.querySelector('.remove'); feedUrlEl.innerText = simplifyLink(f.url); removeBtn.onclick = function() { // 直接操作当前条目的URL节点,添加样式 // 方式1:添加预定义的CSS类 feedUrlEl.classList.add('deleted-item'); // 方式2:直接修改内联样式 // feedUrlEl.style.textDecoration = 'line-through'; // feedUrlEl.style.opacity = '0.5'; }; // 其余原有业务逻辑 // ... // 将生成的条目插入页面列表 newsFeeds.appendChild(el); }); }
如果不想修改原有id、也不想更换函数写法,也可以通过事件对象获取触发元素,再匹配相邻节点:
el.querySelectorAll('a')[0].onclick = (e) => { const currentBtn = e.currentTarget; const feedUrlEl = currentBtn.nextElementSibling; // 操作feedUrlEl添加样式即可 }但该写法强依赖DOM相邻结构,后续模板结构调整很容易失效,且重复id的问题依然存在,不推荐使用。
内容的提问来源于stack exchange,提问作者Evan
相关产品推荐
相关产品推荐

