首页 > 解决方案 > 如何解决 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";

我认为原因是当函数尝试附加侦听器时,事件变量已经超出范围。这正是我能够从研究中收集到的信息。总而言之,我很困惑,甚至不确定这是否是正确的方法。我想我有一些问题是:

  1. 为什么“事件”在通过 onclick 属性传递时作为变量工作,但在作为参数传递给通过 for 循环调用的函数时不工作?我想我已经回答了这个问题,但也许更有知识的人可以提供更好的描述。
  2. 我的 for 循环似乎不必要地令人费解。我在这里把事情复杂化了吗?我猜是的。

任何帮助表示赞赏。

标签: javascriptdom

解决方案


当您像这样声明事件处理程序 -tab[i].addEventListener('click', tabbedWindows(event,c), false);时,您tabbedWindows使用undefined值调用。因为evtundefined这个语句 - 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>


推荐阅读