codecamp

Introduction to events

先决条件: 基本的计算机素养,对HTML和CSS的基本了解, JavaScript第一步
目的: 理解事件的基本理论,它们如何在浏览器中工作,以及事件在不同的编程环境中如何不同。

一系列幸运事件

如上所述,事件是在您正在编程的系统中发生的操作或事件,系统将在事件发生时触发某种信号,并提供某种机制 可以自动采取(例如一些代码运行)事件发生时。 例如,在机场,当跑道对于飞机起飞是清楚的时,信号被传送到飞行员,并且因此他们开始飞行飞机。

在Web的情况下,事件在浏览器窗口内触发,并且倾向于附加到驻留在其中的特定项目 - 这可以是单个元素,元素集合,当前选项卡中加载的HTML文档,或者 整个浏览器窗口。 有很多不同类型的事件可以发生,例如:

  • The user clicking the mouse over a certain element, or hovering the cursor over a certain element.
  • The user pressing a key on the keyboard.
  • The user resizing or closing the browser window.
  • A web page finishing loading.
  • A form being submitted.
  • A video being played, or paused, or finishing play.
  • An error occuring.

您将从此处收集(以及查看MDN 事件引用),有 >很多可以响应的事件。

每个可用的事件都有一个事件处理程序,这是一个代码块,通常由开发人员定义,将在事件触发时运行。 当这样的代码块被定义为响应事件触发而运行时,我们说我们注册了一个事件处理程序 请注意,事件处理程序有时称为事件侦听器 - 为了我们的目的,它们是可以互换的,虽然严格来说他们一起工作。 侦听器侦听事件发生,处理程序是响应它发生运行的代码。

注意:请务必注意,网络事件不是核心JavaScript语言的一部分 - 它们定义为浏览器中内置的JavaScript API的一部分。 您可以通过检出上面列出的事件引用来了解哪个API规范定义了每个事件。

一个简单的例子

让我们来看一个简单的例子来解释我们在这里的意思。 您已经看过本课程中的许多示例中使用的事件和事件处理程序,但让我们回顾一下,以巩固我们的知识。 在下面的示例中,我们有一个 "> < button> ,按下时会将背景更改为随机颜色:

<button>Change color</button>

JavaScript看起来像这样:

var btn = document.querySelector('button');

function random(number) {
  return Math.floor(Math.random()*number);
}

btn.onclick = function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

在这段代码中,我们使用"返回与指定的选择器组匹配的文档内的第一个元素(使用文档标记中的第一个元素的深度优先预先遍历文档节点|并按顺序节点迭代次序)。 Document.querySelector() function.\">> Document.querySelector() 函数。我们还定义一个返回随机数的函数。代码的第三部分是事件处理程序。 btn variable points to a <button> element, and this type of object has a number of events that can fire on it, and therefore, event handlers available.\"> btn 变量指向一个< button> 元素,这种类型的对象有许多事件可以触发它,因此,事件处理程序可用。onclick event handler property to equal an anonymous function containing code that generated a random RGB color and sets the <body>\">我们正在侦听点击事件触发,通过将 onclick 事件处理程序属性设置为等于包含生成随机RGB颜色的代码的匿名函数,并设置< body> 背景颜色等于它。

每当点击事件在< button> 元素上触发时,即当用户点击它时,此代码将运行。

示例输出如下:

它不只是网页

在这一点上值得提及的另一件事是,事件不是JavaScript特有的 - 大多数编程语言都有某种类型的事件模型,它的工作方式通常不同于JavaScript的方式。 事实上,用于网页的JavaScript中的事件模型与用于其他环境中的JavaScript的事件模型不同。

例如, Node.js 是一个非常受欢迎的JavaScript运行时,它允许开发人员使用JavaScript来构建网络和服务器端应用程序。 Node.js事件模型依赖于侦听器侦听事件 和发射器周期性地发出事件 - 它听起来不一样,但代码是完全不同的,使用 on()这样的函数注册一个事件监听器,并且 once 以注册在运行一次之后取消注册的事件侦听器。 HTTP连接事件文档提供了一个很好的使用示例 。

作为另一个示例,您现在还可以使用JavaScript来开发跨浏览器插件 - 浏览器功能增强 - 使用名为 Add-ons / WebExtensions"> WebExtensions 。 事件模型类似于web事件模型,但有点不同 - 事件监听器属性是驼峰式的(例如 onMessage 而不是 onmessage ), 与 addListener 函数。 有关示例,请参见 runtime.onMessage页面

在学习的这个阶段,你不需要了解其他这样的环境; 我们只是想清楚地表明,事件可能在不同的编程环境中有所不同。

使用网络事件的方式

有多种不同的方法可以向Web页面添加事件侦听器代码,以便在关联的事件触发时运行。 在本节中,我们将回顾不同的机制,并讨论应该使用哪些机制。

事件处理程序属性

这些是存在的属性,包含我们在课程中最常见的事件处理程序代码。 回到上面的例子:

var btn = document.querySelector('button');

btn.onclick = function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

onclick 属性是事件处理程序属性 用于这种情况。 它本质上是按钮上可用的任何其他属性(例如 btn.textContent btn.style ),但它是一种特殊类型 - 当您将其设置为等于某些代码时,该代码将在事件触发按钮时运行。

您还可以将处理程序属性设置为等于命名的函数名称(就像我们在构建自己的函数中看到的)。 以下将工作相同:

var btn = document.querySelector('button');

function bgChange() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

btn.onclick = bgChange;

有许多不同的事件处理程序属性可用。 让我们做一个实验。

首先,制作本地副本 "external"> random-color-eventhandlerproperty.html ,然后在浏览器中打开它。 它只是我们已经在本文中已经玩过的简单随机颜色示例的副本。 现在尝试将 btn.onclick 依次改为以下不同的值,并观察示例中的结果:

  • btn.onfocus and btn.onblur — The color will change when the button is focused and unfocused (try pressing tab to tab on to the button and off again). These are often used to display information about how to fill in form fields when they are focused, or display an error message if a form field has just been filled in with an incorrect value.
  • btn.ondblclick — The color will change only when it is double-clicked.
  • window.onkeypress, window.onkeydown, window.onkeyup — The color will change when a key is pressed on the keyboard. keypress refers to a general press (button down and then up), while keydown and keyup refer to just the key down and key up parts of the keystroke, respectively. Note that it doesn't work if you try to register this event handler on the button itself — we've had to register it on the window object, which represents the entire browser window.
  • btn.onmouseover and btn.onmouseout — The color will change when the mouse pointer is moved so it begins hovering over the button, or when it stops hover over the button and moves off it, respectively.

一些事件非常通用,几乎可以在任何地方使用(例如 onclick 处理程序可以在几乎任何元素上注册),而一些事件更具体,只在某些情况下有用(例如使用 仅 onplay 仅适用于特定元素,例如 ://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/video"title ="使用HTML视频元素将视频内容嵌入到文档中。"> < video> 代码> )。

内联事件处理程序 - 不要使用这些

您可能还会在代码中看到这样的模式:

<button onclick="bgChange()">Press me</button>
function bgChange() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

注册在Web上发现的事件处理程序的最早方法涉及事件处理程序HTML属性(又称内联事件处理程序),如上所示 - 属性值字面上是JavaScript代码 你想在事件发生时运行。 上述示例调用在 在同一页面上引用可执行脚本"> < script> 元素,但也可以直接在属性内插入JavaScript,例如:

<button onclick="alert('Hello, this is my old-fashioned event handler!');">Press me</button>

你会发现许多事件处理程序属性的HTML属性等价物; 然而,你不应该使用这些 - 它们被认为是不良做法。 看起来很容易使用事件处理程序属性,如果你只是做一些快速的事情,但他们很快就变得难以管理和低效率。

首先,将HTML和JavaScript混合起来并不是一个好主意,因为它很难解析 - 将JavaScript集中在一个位置更好; 如果它在一个单独的文件中,您可以将其应用于多个HTML文档。

即使在单个文件中,内联事件处理程序也不是一个好主意。 一个按钮是OK,但如果你有100个按钮怎么办? 您必须向文件中添加100个属性; 它很快就变成了维修噩梦。 使用JavaScript,您可以轻松地添加一个事件处理函数到页面上的所有按钮,无论有多少,使用这样的东西:

var buttons = document.querySelectorAll('button');

for (var i = 0; i < buttons.length; i++) {
  buttons[i].onclick = bgChange;
}

注意:将您的编程逻辑与您的内容分开也可以使您的网站对搜索引擎更加友好。

addEventListener()和removeEventListener()

最新类型的事件机制在文档对象模型(DOM)级别2中定义 活动规格,为浏览器提供新功能 - addEventListener( ) 。 这个函数与事件处理程序属性的方式类似,但语法明显不同。 我们可以重写我们的随机颜色示例,看起来像这样:

var btn = document.querySelector('button');

function bgChange() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}   

btn.addEventListener('click', bgChange);

因此,在 addEventListener()函数中,我们指定两个参数 - 我们想要注册此处理程序的事件的名称,以及包含我们要响应它运行的处理程序函数的代码。 请注意,将所有代码放在 addEventListener()函数内,这是一个匿名函数,如下所示:

btn.addEventListener('click', function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
});

这种机制比前面讨论的旧机制具有一些优点。 对于开始,有一个对应的函数, removeEventListener() ,它删除以前添加的侦听器。 例如,这将删除本节中第一个代码块中的侦听器集:

btn.removeEventListener('click', bgChange);

这对于简单的小程序来说并不重要,但是对于更大,更复杂的程序,它可以提高清理旧未使用的事件处理程序的效率,此外还允许您在不同的情况下使用相同的按钮执行不同的操作 - 我们需要做的是根据需要添加/删除事件处理程序。

其次,您还可以为同一个侦听器注册多个处理程序。 将不应用以下两个处理程序:

myElement.onclick = functionA;
myElement.onclick = functionB;

因为第二行将覆盖 onclick 设置的第一个值。 然而,这将工作:

myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);

这两个函数现在将在单击元素时运行。

此外,此事件机制还提供了许多其他强大的功能和选项。 这些内容有点超出了本文的范围,但如果您想阅读它们,请查看 Web / API / EventTarget / addEventListener"> addEventListener() / removeEventListener"> removeEventListener() 参考页。

我应该使用什么机制?

在这三种机制中,您绝对不应该使用HTML事件处理程序属性 - 这些属性已经过时,而且是上面提到的坏做法。

其他两个是相对可互换的,至少对于简单的用途:

  • Event handler properties have less power and options, but better cross browser compatibility (being supported as far back as Internet Explorer 8). You should probably start with these as you are learning.
  • DOM Level 2 Events (addEventListener(), etc.) are more powerful, but can also become more complex and are less well supported (supported as far back as Internet Explorer 9). You should also experiment with these, and aim to use them where possible.

第三种机制的主要优点是,如果需要,可以使用 removeEventListener()删除事件处理程序代码,如果需要,您可以向元素添加多个相同类型的侦听器。 例如,您可以多次在元素上调用 addEventListener(\'click\',function(){...}),并在第二个参数中指定不同的函数。 这对于事件处理程序属性是不可能的,因为设置属性的任何后续尝试都将覆盖先前的属性,例如:

element.onclick = function1;
element.onclick = function2;
etc.

注意:如果您被要求支持早于Internet Explorer 8的浏览器,您可能会遇到困难,因为这些古老的浏览器在新的浏览器中使用不同的事件模型。 但不要害怕,大多数JavaScript库(例如 jQuery )内置了抽象出跨浏览器差异的函数。 在你的学习之旅中,不要担心这一点。

其他事件概念

在本节中,我们将简要介绍一些与事件相关的高级概念。 在这一点上完全理解这些并不重要,但它可能会解释你可能会不时遇到的一些代码模式。

事件对象

有时在一个事件处理程序函数中,你可能会看到一个以 event evt 或简单的 e 这称为事件对象,它会自动传递给事件处理程序,以提供额外的功能和信息。 例如,让我们再次重写我们的随机颜色示例:

function bgChange(e) {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  e.target.style.backgroundColor = rndCol;
  console.log(e);
}  

btn.addEventListener('click', bgChange);

在这里,您可以看到我们在函数中包含一个事件对象 e ,并且在 e.target 上设置背景颜色样式的函数 - 这是按钮 本身。 事件对象的 target 属性始终是对事件刚刚发生的元素的引用。 所以在这个例子中,我们在按钮上设置一个随机的背景颜色,而不是页面。

注意:您可以为事件对象使用任何您喜欢的名称 - 您只需要选择一个名称,然后您可以在事件处理函数中引用它。 e / evt / event 是开发人员最常用的,因为它们简短易记。 坚持标准总是好的。

注意:您可以为事件对象使用任何您喜欢的名称 - 您只需要选择一个名称,然后您可以在事件处理函数中引用它。 e / evt / event 是开发人员最常用的,因为它们简短易记。 坚持标准总是好的。

var divs = document.querySelectorAll('div');

for (var i = 0; i < divs.length; i++) {
  divs[i].onclick = function(e) {
    e.target.style.backgroundColor = bgChange();
  }
}

输出如下(尝试点击它 - 有乐趣):

您将遇到的大多数事件处理程序只有一组属性和函数(方法)可用于事件对象(请参阅API / Event"title ="Event接口表示在DOM中发生的任何事件;一些是用户生成的(例如鼠标或键盘事件),而其他是由API生成的(例如指示动画已完成的事件\">运行,视频已暂停,等等)。有很多类型的事件,其中一些使用的是基于主Event接口的其他接口。事件本身包含所有事件通用的属性和方法。"> Event object reference for a full list).\"> Event 对象引用完整列表)。然而,一些更高级的处理程序添加专家属性包含他们需要运行的额外数据。Media Recorder API for example has a dataavailable event,\">例如,媒体记录器API 具有 dataavailable 事件,当一些音频或视频已经被记录并且可用于做某事(例如,保存它或者回放)时触发。ondataavailable handler\'s event object has a data\">相应的 ondataavailable 处理程序的事件对象具有 data 属性可用包含录制的音频或视频数据,以允许您访问它,并做一些与它。

防止默认行为

有时候,你会遇到一个情况,你想要停止一个事件做默认情况下做什么。 最常见的示例是Web表单,例如自定义注册表单。 当您填写详细信息并按提交按钮时,自然的行为是将数据提交到服务器上的指定页面进行处理,并将浏览器重定向到某种类型的"成功消息"页面(或 同一页,如果没有指定另一个。)

麻烦来了,当用户没有正确地提交数据 - 作为开发人员,你会想要停止提交到服务器,并给他们一个错误消息告诉他们什么是错误,需要做什么,以妥善处理。 某些浏览器支持自动表单数据验证功能,但由于许多浏览器不支持,因此建议您不要依赖这些功能,并实施自己的验证检查。 让我们来看一个简单的例子。

首先,一个简单的HTML表单,要求您输入您的姓名:

<form>
  <div>
    <label for="fname">First name: </label>
    <input id="fname" type="text">
  </div>
  <div>
    <label for="lname">Last name: </label>
    <input id="lname" type="text">
  </div>
  <div>
     <input id="submit" type="submit">
  </div>
</form>
<p></p>

现在一些JavaScript - 这里我们在 onsubmit 事件处理程序中实现非常简单的检查 (提交事件在表单提交时触发),测试文本字段是否为空。 如果是,我们调用 preventDefault() >对事件对象的函数 - 这将停止提交表单 - 然后在我们的表单下面的段落中显示一条错误消息,告诉用户出现了什么问题:

var form = document.querySelector('form');
var fname = document.getElementById('fname');
var lname = document.getElementById('lname');
var submit = document.getElementById('submit');
var para = document.querySelector('p');

form.onsubmit = function(e) {
  if (fname.value === '' || lname.value === '') {
    e.preventDefault();
    para.textContent = 'You need to fill in both names!';
  }
}

显然,这是相当弱的表单验证 - 它不会阻止用户验证表单与空格或数字输入到字段,例如 - 但它是确定为示例的目的。 输出如下:

事件冒泡和捕获

这里涵盖的最后一个主题是你不会经常碰到的,但如果你不明白它可能是一个真正的痛苦。 事件冒泡和捕获是描述当在一个元素上激活相同事件类型的两个处理程序时发生什么的两种机制。 让我们来看一个例子,让这更容易 - 打开 "external"> show-video-box.html example in a new tab(and the block / events / show-video-box.html"class ="external">源代码)。它也可以在下面提供:

这是一个非常简单的示例,用于显示和隐藏 容器用于流内容,并不固有地表示任何内容。使用它来为元素分组,例如样式(使用类或id属性),用不同语言标记文档的一部分(使用lang属性),等等 。"> < div> ="使用HTML视频元素将视频内容嵌入到文档中。"> < video> 元素:

<button>Display video</button>

<div class="hidden">
  <video>
    <source src="rabbit320.mp4" type="video/mp4">
    <source src="rabbit320.webm" type="video/webm">
    <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
  </video>
</div>

< 按钮> ,通过将< div> 隐藏的类属性更改为 (示例的CSS包含这两个类,分别将框定位在屏幕和屏幕上):

btn.onclick = function() {
  videoBox.setAttribute('class', 'showing');
}

然后我们再添加一些 onclick 事件处理程序 - 第一个到< div> ,第二个到< video> 。 这种想法是,当视频之外的< div> 区域被点击时,框应该再次隐藏; 当视频本身被点击时,视频应该开始播放。

videoBox.onclick = function() {
  videoBox.setAttribute('class', 'hidden');
};

video.onclick = function() {
  video.play();
};

但有一个问题 - 目前,当您点击开始播放的视频,但它会导致< div> 也同时隐藏。 这是因为视频位于< div> - 它是它的一部分 - 所以点击视频实际上运行 以上事件处理程序。

Bubbling and capturing explained

当对具有父元素的元素触发事件时(例如 视频元素在文档中嵌入视频内容。"> < video> ),现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。 在捕获阶段:

  • The browser checks to see if the element's outer-most ancestor (<html>) has an onclick event handler registered on it in the capturing phase, and runs it if so.
  • Then it moves on to the next element inside <html> and does the same thing, then the next one, and so on until it reaches the element that was actually clicked on.

在起泡阶段,发生完全相反的情况:

  • The browser checks to see if the element that was actually clicked on has an onclick event handler registered on it in the bubbling phase, and runs it if so.
  • Then it moves on to the next immediate ancestor element and does the same thing, then the next one, and so on until it reaches the <html> element.

(点击图片查看大图)

在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段注册。 因此,在我们当前的示例中,当您点击视频时,点击事件气泡从< video> 元素向外到< html> 元素。 一路上:

  • It finds the video.onclick... handler and runs it, so the video first starts playing.
  • It then finds the videoBox.onclick... handler and runs it, so the video is hidden as well.

Fixing the problem with stopPropagation()

这是恼人的行为,但有一种方法来解决它! 标准事件对象具有可用的函数,其名称为 stopPropagation() > ,当在处理程序的事件对象上调用时,它使得处理程序运行,但事件不会在链上冒泡,因此不会运行更多的处理程序。

因此,我们可以通过将前面代码块中的第二个处理函数改为:

video.onclick = function(e) {
  e.stopPropagation();
  video.play();
};

您可以尝试制作本地副本 "external"> show-video-box.html源代码,并自己修复它,或查看固定的结果 区域/ javascript / building-blocks / events / show-video-box-fixed.html"class ="external"> show-video-box-fixed.html (另请参阅 //github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box-fixed.html"class ="external">源代码)。

注意:为什么要同时捕获和冒泡? 好吧,在糟糕的旧时代,浏览器交叉兼容性比现在少得多,Netscape只使用事件捕获,而Internet Explorer只使用事件冒泡。 当W3C决定尝试标准化行为并达成共识时,他们最终得到了包括两者的系统,这是一个现代浏览器实现的。

注意:如上所述,默认情况下,所有事件处理程序都在冒泡阶段注册,这在大多数时间更有意义。 如果您真的想要在捕获阶段注册一个事件,您可以使用 / EventTarget / addEventListener"> addEventListener() ,并将可选的第三个属性设置为 true

Event delegation

冒泡也允许我们利用事件委托 - 这个概念依赖于这样一个事实,如果你想要一些代码运行时,你点击任何一个大量的子元素,你可以设置 事件监听器,并且对每个孩子具有事件监听器泡泡的效果,而不必对每个孩子单独设置事件监听器。

一个很好的例子是一系列列表项 - 如果你想让每个人在点击时弹出一个消息,你可以在父代码< ul>上设置 click 事件监听器。 ,它会弹出列表项。

David Walsh的博客中进一步解释了这一概念,其中包含多个示例 - 请参见 JavaScript事件委托的工作原理

结论

在这个早期阶段,您现在应该知道所有您需要了解的Web事件。 如上所述,事件不是核心JavaScript的一部分 - 它们是在浏览器Web API中定义的。

此外,重要的是要了解JavaScript使用的不同上下文往往有不同的事件模型 - 从Web API到其他领域,如浏览器WebExtensions和Node.js(服务器端JavaScript)。 我们不希望你现在了解所有这些领域,但它肯定有助于了解事件的基础知识,因为你在开发学习Web开发。

如果您有任何不明白的地方,请随时阅读本文,或与我们联系以寻求帮助。

也可以看看

  • Event order (discussion of capturing and bubbling) — an excellently detailed piece by Peter-Paul Koch.
  • Event accessing (discussing of the event object) — another excellently detailed piece by Peter-Paul Koch.
  • Event reference

Function return values
Image gallery
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录
CSS

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }