目录
直观的做法
减少重复代码
利用 Event 对象
使用事件冒泡
点击事件呢?
更多示例
列表
表单
结论
首页 web前端 css教程 简化事件处理程序背后的思考

简化事件处理程序背后的思考

Apr 21, 2025 am 10:31 AM

The Thinking Behind Simplifying Event Handlers

事件用于响应用户点击、键盘聚焦链接以及更改表单文本等操作。刚开始学习 JavaScript 时,我编写了复杂的事件监听器。最近,我学习了如何减少代码量和监听器数量。

让我们从一个简单的例子开始:几个可拖动的方块。我们想向用户显示他们拖动的彩色方块。

<div draggable="true">
    R
  </div>
  <div draggable="true">
    Y
  </div>
  <div draggable="true">
    G
  </div>
<p>拖动一个方块</p>
登录后复制

直观的做法

刚开始学习 JavaScript 事件时,我为每个元素编写了单独的事件监听器函数。这是一个常见的模式,因为它是最简单的入门方法。我们希望每个元素都有特定的行为,因此可以使用每个元素的特定代码。

document.querySelector('#red').addEventListener('dragstart', evt => {
  document.querySelector('#dragged').textContent = 'Dragged red';
});

document.querySelector('#yellow').addEventListener('dragstart', evt => {
  document.querySelector('#dragged').textContent = 'Dragged yellow';
});

document.querySelector('#green').addEventListener('dragstart', evt => {
  document.querySelector('#dragged').textContent = 'Dragged green';
});
登录后复制

减少重复代码

该示例中的事件监听器非常相似:每个函数都显示一些文本。此重复代码可以折叠到一个辅助函数中。

function preview(color) {
  document.querySelector('#dragged').textContent = `Dragged ${color}`;
}

document
  .querySelector('#red')
  .addEventListener('dragstart', evt => preview('red'));
document
  .querySelector('#yellow')
  .addEventListener('dragstart', evt => preview('yellow'));
document
  .querySelector('#green')
  .addEventListener('dragstart', evt => preview('green'));
登录后复制

这样更简洁,但仍然需要多个函数和事件监听器。

利用 Event 对象

Event 对象是简化监听器的关键。调用事件监听器时,它还会将 Event 对象作为第一个参数发送。此对象包含一些数据来描述发生的事件,例如事件发生的时间。为了简化我们的代码,我们可以使用 evt.currentTarget 属性,其中 currentTarget 指的是附加事件监听器的元素。在我们的示例中,它将是三个彩色方块之一。

const preview = evt => {
  const color = evt.currentTarget.id;
  document.querySelector('#dragged').textContent = `Dragged ${color}`;
};

document.querySelector('#red').addEventListener('dragstart', preview);
document.querySelector('#yellow').addEventListener('dragstart', preview);
document.querySelector('#green').addEventListener('dragstart', preview);
登录后复制

现在只有一个函数而不是四个函数了。我们可以将完全相同的函数作为事件监听器重用,而 evt.currentTarget.id 将根据触发事件的元素具有不同的值。

使用事件冒泡

最后一个更改是减少代码中的行数。与其将事件监听器附加到每个方块,不如将单个事件监听器附加到包含所有彩色方块的元素。

事件在触发时从事件起源的元素(其中一个方块)开始。但是,它不会就此停止。浏览器会转到该元素的每个父元素,并调用其上的任何事件监听器。这将持续到文档的根元素(HTML 中的 标签)。此过程称为“冒泡”,因为事件像气泡一样上升到文档树中。将事件监听器附加到 section 元素将导致焦点事件从被拖动的彩色方块冒泡到父元素。我们还可以利用 evt.target 属性,该属性包含触发事件的元素(其中一个方块),而不是附加事件监听器的元素(section 元素)。

const preview = evt => {
  const color = evt.target.id;
  document.querySelector('#dragged').textContent = `Dragged ${color}`;
};

document.querySelector('section').addEventListener('dragstart', preview);
登录后复制

现在我们已经将许多事件监听器减少到只有一个了!对于更复杂的代码,效果会更好。通过利用 Event 对象和冒泡,我们可以控制 JavaScript 事件并简化事件处理程序的代码。

点击事件呢?

evt.targetdragstartchange 等事件非常有效,在这些事件中,只有少量元素可以接收焦点或更改输入。

但是,我们通常希望监听点击事件,以便我们可以响应用户点击应用程序中的按钮。点击事件会针对文档中的任何元素触发,从大型 div 到小型 span。

让我们将我们的可拖动彩色方块改为可点击的。

<div draggable="true">
    R
  </div>
  <div draggable="true">
    Y
  </div>
  <div draggable="true">
    G
  </div>
<p>点击一个方块</p>
登录后复制
const preview = evt => {
  const color = evt.target.id;
  document.querySelector('#clicked').textContent = `Clicked ${color}`;
};

document.querySelector('section').addEventListener('click', preview);
登录后复制

在测试此代码时,请注意,有时不会附加到“Clicked”,而不是点击方块时。它不起作用的原因是每个方块都包含一个可以点击的 <span></span> 元素,而不是可拖动的 <div> 元素。由于 span 没有设置 ID,因此 <code>evt.target.id 属性为空字符串。我们只关心代码中的彩色方块。如果我们在方块内某处点击,我们需要找到父方块元素。我们可以使用 element.closest() 来查找最接近被点击元素的父元素。

const preview = evt => {
  const element = evt.target.closest('div[draggable]');
  if (element != null) {
    const color = element.id;
    document.querySelector('#clicked').textContent = `Clicked ${color}`;
  }
};
登录后复制

现在我们可以对点击事件使用单个监听器了!如果 element.closest() 返回 null,则表示用户在彩色方块之外的某处点击,我们应该忽略该事件。

更多示例

以下是一些其他示例,用于演示如何利用单个事件监听器。

列表

一个常见的模式是拥有一个可以交互的项目列表,其中新的项目是使用 JavaScript 动态插入的。如果我们为每个项目附加事件监听器,那么每次生成新元素时,代码都必须处理事件监听器。

<div id="buttons-container"></div>
<button id="add">添加新按钮</button>
登录后复制
let buttonCounter = 0;
document.querySelector('#add').addEventListener('click', evt => {
  const newButton = document.createElement('button');
  newButton.textContent = buttonCounter;

  // 每次点击“添加新按钮”时创建一个新的事件监听器
  newButton.addEventListener('click', evt => {

    // 点击时,记录被点击按钮的编号。
    document.querySelector('#clicked').textContent = `Clicked button #${newButton.textContent}`;
  });

  buttonCounter  ;

  const container = document.querySelector('#buttons-container');
  container.appendChild(newButton);
});
登录后复制

通过利用冒泡,我们可以在容器上使用单个事件监听器。如果我们在应用程序中创建许多元素,这会将监听器的数量从 n 减少到两个。

let buttonCounter = 0;
const container = document.querySelector('#buttons-container');
document.querySelector('#add').addEventListener('click', evt => {
  const newButton = document.createElement('button');
  newButton.dataset.number = buttonCounter;
  buttonCounter  ;

  container.appendChild(newButton);
});
container.addEventListener('click', evt => {
  const clickedButton = evt.target.closest('button');
  if (clickedButton != null) {
    // 点击时,记录被点击按钮的编号。
    document.querySelector('#clicked').textContent = `Clicked button #${clickedButton.dataset.number}`;
  }
});
登录后复制

表单

也许有一个包含许多输入的表单,我们想将所有用户响应收集到单个对象中。

登录后复制



let responses = {
  name: '',
  email: '',
  password: ''
};

document
  .querySelector('input[name="name"]')
  .addEventListener('change', evt => {
    const inputElement = document.querySelector('input[name="name"]');
    responses.name = inputElement.value;
    document.querySelector('#preview').textContent = JSON.stringify(responses);
  });
document
  .querySelector('input[name="email"]')
  .addEventListener('change', evt => {
    const inputElement = document.querySelector('input[name="email"]');
    responses.email = inputElement.value;
    document.querySelector('#preview').textContent = JSON.stringify(responses);
  });
document
  .querySelector('input[name="password"]')
  .addEventListener('change', evt => {
    const inputElement = document.querySelector('input[name="password"]');
    responses.password = inputElement.value;
    document.querySelector('#preview').textContent = JSON.stringify(responses);
  });
登录后复制

让我们改用父 <form></form> 元素上的单个监听器。

let responses = {
  name: '',
  email: '',
  password: ''
};

document.querySelector('form').addEventListener('change', evt => {
  responses[evt.target.name] = evt.target.value;
  document.querySelector('#preview').textContent = JSON.stringify(responses);
});
登录后复制

结论

现在我们知道如何利用事件冒泡和事件对象将复杂的事件处理程序混乱简化为几个……有时甚至减少到一个!希望本文能帮助您从新的角度思考事件处理程序。我知道在我早期的开发生涯中花费大量时间编写重复的代码来完成同样的事情之后,这对我来说是一个启示。

以上是简化事件处理程序背后的思考的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

VUE 3 VUE 3 Apr 02, 2025 pm 06:32 PM

它的出局!恭喜Vue团队完成了完成,我知道这是一项巨大的努力,而且很长时间。所有新文档也是如此。

使用Redwood.js和Fauna构建以太坊应用 使用Redwood.js和Fauna构建以太坊应用 Mar 28, 2025 am 09:18 AM

随着最近比特币价格超过20k美元的攀升,最近打破了3万美元,我认为值得深入研究创建以太坊

您可以从浏览器获得有效的CSS属性值吗? 您可以从浏览器获得有效的CSS属性值吗? Apr 02, 2025 pm 06:17 PM

我有人写了这个非常合法的问题。 Lea只是在博客上介绍了如何从浏览器中获得有效的CSS属性。那样的是这样。

带有粘性定位的堆叠卡和一点点的杂物 带有粘性定位的堆叠卡和一点点的杂物 Apr 03, 2025 am 10:30 AM

前几天,我发现了科里·金尼文(Corey Ginnivan)网站上的这一点,当您滚动时,彼此之间的卡片堆放集。

在CI/CD上有点 在CI/CD上有点 Apr 02, 2025 pm 06:21 PM

我说的“网站”比“移动应用程序”更合适,但我喜欢Max Lynch的框架:

比较浏览器的响应式设计 比较浏览器的响应式设计 Apr 02, 2025 pm 06:25 PM

这些桌面应用程序中有许多目标是同时在不同的维度上显示您的网站。因此,例如,您可以写作

在WordPress块编辑器中使用Markdown和本地化 在WordPress块编辑器中使用Markdown和本地化 Apr 02, 2025 am 04:27 AM

如果我们需要直接在WordPress编辑器中向用户显示文档,那么最佳方法是什么?

为什么Flex布局中的紫色斜线区域会被误认为是'溢出空间”? 为什么Flex布局中的紫色斜线区域会被误认为是'溢出空间”? Apr 05, 2025 pm 05:51 PM

关于Flex布局中紫色斜线区域的疑问在使用Flex布局时,你可能会遇到一些令人困惑的现象,比如在开发者工具(d...

See all articles