搜索
首页web前端css教程简化事件处理程序背后的思考

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
@KeyFrames vs CSS过渡:有什么区别?@KeyFrames vs CSS过渡:有什么区别?May 14, 2025 am 12:01 AM

@keyframesandCSSTransitionsdifferincomplexity:@keyframesallowsfordetailedanimationsequences,whileCSSTransitionshandlesimplestatechanges.UseCSSTransitionsforhovereffectslikebuttoncolorchanges,and@keyframesforintricateanimationslikerotatingspinners.

使用页面CMS进行静态站点内容管理使用页面CMS进行静态站点内容管理May 13, 2025 am 09:24 AM

我知道,我知道:有大量的内容管理系统选项可用,而我进行了几个测试,但实际上没有一个是一个,y&#039;知道吗?怪异的定价模型,艰难的自定义,有些甚至最终成为整个&

链接HTML中CSS文件的最终指南链接HTML中CSS文件的最终指南May 13, 2025 am 12:02 AM

链接CSS文件到HTML可以通过在HTML的部分使用元素实现。1)使用标签链接本地CSS文件。2)多个CSS文件可通过添加多个标签实现。3)外部CSS文件使用绝对URL链接,如。4)确保正确使用文件路径和CSS文件加载顺序,优化性能可使用CSS预处理器合并文件。

CSS Flexbox与网格:全面评论CSS Flexbox与网格:全面评论May 12, 2025 am 12:01 AM

选择Flexbox还是Grid取决于布局需求:1)Flexbox适用于一维布局,如导航栏;2)Grid适合二维布局,如杂志式布局。两者在项目中可结合使用,提升布局效果。

如何包括CSS文件:方法和最佳实践如何包括CSS文件:方法和最佳实践May 11, 2025 am 12:02 AM

包含CSS文件的最佳方法是使用标签在HTML的部分引入外部CSS文件。1.使用标签引入外部CSS文件,如。2.对于小型调整,可以使用内联CSS,但应谨慎使用。3.大型项目可使用CSS预处理器如Sass或Less,通过@import导入其他CSS文件。4.为了性能,应合并CSS文件并使用CDN,同时使用工具如CSSNano进行压缩。

Flexbox vs Grid:我应该学习两者吗?Flexbox vs Grid:我应该学习两者吗?May 10, 2025 am 12:01 AM

是的,youshouldlearnbothflexboxandgrid.1)flexboxisidealforone-demensional,flexiblelayoutslikenavigationmenus.2)gridexcelstcelsintwo-dimensional,confffferDesignssignssuchasmagagazineLayouts.3)blosebothenHancesSunHanceSlineHancesLayOutflexibilitibilitibilitibilitibilityAnderibilitibilityAndresponScormentilial anderingStruction

轨道力学(或我如何优化CSS KeyFrames动画)轨道力学(或我如何优化CSS KeyFrames动画)May 09, 2025 am 09:57 AM

重构自己的代码看起来是什么样的?约翰·瑞亚(John Rhea)挑选了他写的一个旧的CSS动画,并介绍了优化它的思维过程。

CSS动画:很难创建它们吗?CSS动画:很难创建它们吗?May 09, 2025 am 12:03 AM

CSSanimationsarenotinherentlyhardbutrequirepracticeandunderstandingofCSSpropertiesandtimingfunctions.1)Startwithsimpleanimationslikescalingabuttononhoverusingkeyframes.2)Useeasingfunctionslikecubic-bezierfornaturaleffects,suchasabounceanimation.3)For

See all articles

热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

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

热门文章

热工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

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

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器