javascript - 如何解决 addEventListener 的问题:无法读取未定义的属性“currentTarget”
问题描述
我是 JavaScript 的新手,所以请耐心等待。
我正在尝试使用 JavaScript 为特定的内容生成选项卡式窗口。HTML 如下所示:
<div class="tab-win" id="tablinks-d38e44">
<button class="tablinks" data-target="tab-bash-d38e44">bash</button>
<button class="tablinks" data-target="tab-gui-d38e44">gui</button>
</div>
<div class="tabcontents" id="tabcontents-d38e44">
<div class="tabcontent" id="tab-bash-d38e44">This is bash.</div>
<div class="tabcontent" id="tab-gui-d38e44">This is plain old GUI...</div>
</div>
<p class="p">Here is another one.</p>
<div class="tab-win" id="tablinks-d38e56">
<button class="tablinks" data-target="tab-bash-d38e56">bash</button>
<button class="tablinks" data-target="tab-gui-d38e56">gui</button>
</div>
<div class="tabcontents" id="tabcontents-d38e56">
<div class="tabcontent" id="tab-bash-d38e56">This is bash #2.</div>
<div class="tabcontent" id="tab-gui-d38e56">This is plain old GUI..#2.</div>
</div>
</div>
我拼凑了这个功能。
function tabbedWindows(evt, env) {
// Declare all variables
var i, tabcontent, tablinks;
// Get all elements with class="tabcontent" and hide them
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// Get all elements with class="tablinks" and remove the class "active"
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the button that opened the tab
document.getElementById(env).style.display = "block";
evt.currentTarget.className += " active";
}
如果我将 onclick 属性直接添加到按钮元素,如下所示:
onclick="tabbedWindows(event,'content-id')"
效果很好。但是,我试图介意关注点分离并希望动态添加事件。我正在尝试通过以下方式做到这一点:
const tab = document.querySelectorAll(".tablinks");
const content = document.querySelectorAll(".tabcontent");
for (let i = 0; i < tab.length; i++) {
let conId = tab[i].getAttribute('data-target');
let conArray = Array.from(content);
let con = conArray.find((c) => c.getAttribute('id') === conId);
let c = con.getAttribute('id');
tab[i].addEventListener('click', tabbedWindows(event,c), false);
}
由于以下错误,这不起作用:main.js:23 Uncaught TypeError: Cannot read property 'currentTarget' of undefined。它失败的行是:
evt.currentTarget.className += " active";
我认为原因是当函数尝试附加侦听器时,事件变量已经超出范围。这正是我能够从研究中收集到的信息。总而言之,我很困惑,甚至不确定这是否是正确的方法。我想我有一些问题是:
- 为什么“事件”在通过 onclick 属性传递时作为变量工作,但在作为参数传递给通过 for 循环调用的函数时不工作?我想我已经回答了这个问题,但也许更有知识的人可以提供更好的描述。
- 我的 for 循环似乎不必要地令人费解。我在这里把事情复杂化了吗?我猜是的。
任何帮助表示赞赏。
解决方案
当您像这样声明事件处理程序 -tab[i].addEventListener('click', tabbedWindows(event,c), false);
时,您tabbedWindows
使用undefined
值调用。因为evt
是undefined
这个语句 - evt.currentTarget
- 引发错误。
您需要将第二个参数传递给事件处理程序,因为您需要从选项卡“data-target”获取的内容 id。但是,选项卡是事件的目标,您可以在单击它时获取信息。我已经重构了代码,并删除了冗余。
const tabLinks = document.querySelectorAll(".tablinks");
const tabContents = document.querySelectorAll(".tabcontent");
const activateTab = target => {
const contentId = target.getAttribute('data-target');
// Get all elements with class="tabcontent" and hide them, and show the current tab
tabContents.forEach(tabContent => {
tabContent.style.display = tabContent.id === contentId ? 'block' : 'none';
});
// Get all elements with class="tablinks" and remove the class "active"
tabLinks.forEach(tablink => {
tablink.classList.remove('active');
});
// Add an "active" class to the button that opened the tab
target.classList.add('active');
};
const tabbedWindows = evt => {
const target = evt.currentTarget;
activateTab(target);
}
document.querySelectorAll(".tablinks").forEach(tab => {
tab.addEventListener('click', tabbedWindows, false);
});
activateTab(tabLinks[0]);
<div class="tab-win" id="tablinks-d38e44">
<button class="tablinks" data-target="tab-bash-d38e44">bash</button>
<button class="tablinks" data-target="tab-gui-d38e44">gui</button>
</div>
<div class="tabcontents" id="tabcontents-d38e44">
<div class="tabcontent" id="tab-bash-d38e44">This is bash.</div>
<div class="tabcontent" id="tab-gui-d38e44">This is plain old GUI...</div>
</div>
<p class="p">Here is another one.</p>
<div class="tab-win" id="tablinks-d38e56">
<button class="tablinks" data-target="tab-bash-d38e56">bash</button>
<button class="tablinks" data-target="tab-gui-d38e56">gui</button>
</div>
<div class="tabcontents" id="tabcontents-d38e56">
<div class="tabcontent" id="tab-bash-d38e56">This is bash #2.</div>
<div class="tabcontent" id="tab-gui-d38e56">This is plain old GUI..#2.</div>
</div>
</div>
上一个答案:
您可以使用 currying 来获取env
,并返回一个等待事件对象的新函数:
/** get env and return a new function that will be called with the event object **/
const tabbedWindows = env => evt => {
// Declare all variables
var i, tabcontent, tablinks;
// Get all elements with class="tabcontent" and hide them
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// Get all elements with class="tablinks" and remove the class "active"
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the button that opened the tab
document.getElementById(env).style.display = "block";
evt.currentTarget.className += " active";
}
const tab = document.querySelectorAll(".tablinks");
const content = document.querySelectorAll(".tabcontent");
for (let i = 0; i < tab.length; i++) {
let conId = tab[i].getAttribute('data-target');
let conArray = Array.from(content);
let con = conArray.find((c) => c.getAttribute('id') === conId);
let c = con.getAttribute('id');
tab[i].addEventListener('click', tabbedWindows(c), false); // call tabbedWindows with c
}
<div class="tab-win" id="tablinks-d38e44">
<button class="tablinks" data-target="tab-bash-d38e44">bash</button>
<button class="tablinks" data-target="tab-gui-d38e44">gui</button>
</div>
<div class="tabcontents" id="tabcontents-d38e44">
<div class="tabcontent" id="tab-bash-d38e44">This is bash.</div>
<div class="tabcontent" id="tab-gui-d38e44">This is plain old GUI...</div>
</div>
<p class="p">Here is another one.</p>
<div class="tab-win" id="tablinks-d38e56">
<button class="tablinks" data-target="tab-bash-d38e56">bash</button>
<button class="tablinks" data-target="tab-gui-d38e56">gui</button>
</div>
<div class="tabcontents" id="tabcontents-d38e56">
<div class="tabcontent" id="tab-bash-d38e56">This is bash #2.</div>
<div class="tabcontent" id="tab-gui-d38e56">This is plain old GUI..#2.</div>
</div>
</div>
推荐阅读
- c# - 如何将有序的多行文本拆分为数组c#
- python - 我怎样才能杀死我用python启动的进程
- java - 反序列化具有一个元素的列表,如果列表大小为 1,则直接作为元素本身(Json 格式)
- excel - 将所有值从一张表复制并粘贴到另一张表
- git - 如何修复在非主分支上提交的修改子模块引用?
- apache-spark - 为什么 SparkContext 会创建大量与 Hive Metastore 的连接并扫描所有数据库?
- python - 如何:在 Windows 上为 Mac OS 创建 Python 应用程序
- python - 我无法在 Windows 上为 anaconda jupyter notebook 安装 pandas-datareader
- python - 当列名中有 `.` 时,Pandas 查询或评估
- php - 如何显示今天之前的日期,但我的查询是所有日期> = CURDATE(),来自同一列