javascript - 如何解决 addEventListener : Cannot read property 'currentTarget' of undefined 的问题

标签 javascript dom

我是 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: 无法读取未定义的属性“currentTarget”。它失败的线路是:

evt.currentTarget.className += " active";

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

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

感谢任何帮助。

最佳答案

当您像这样声明事件处理程序时 - tab[i].addEventListener('click', tabbedWindows(event,c), false);,您将调用 tabbedWindows 具有 未定义 值。由于 evt未定义,此语句 - 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>

关于javascript - 如何解决 addEventListener : Cannot read property 'currentTarget' of undefined 的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58494061/

相关文章:

javascript - 在每次 DOM 更改时触发 javascript 事件 "complete"

javascript - 使用 js 创建两个元素,然后在另一个函数中访问它们

html - Chrome 在低 DPI 和高 DPI 显示器上以不同方式重绘滚动条上的 div

jquery - 迭代页面中的每个文本元素?

javascript - 创建后动态分配 gridster 数据行

javascript - 滚动时 div 在固定 div 后面

javascript - openweathermap API 到 Angular ES6

javascript - Firebase 如何动态搜索用户数据下的推送键

javascript - 在 Type Script 中添加对象值而不是对新变量的引用

javascript - 在此浏览器对象层次结构中继承的角色和职责