aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/learn/javascript/building_blocks/events/index.html
blob: 605a4efae00f67411c79be3dbbbf399810919ef4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
---
title: 事件介绍
slug: Learn/JavaScript/Building_blocks/Events
tags:
  - Event
  - Guide
  - JavaScript
  - 事件
  - 事件处理
  - 初学者
translation_of: Learn/JavaScript/Building_blocks/Events
---
<div>{{LearnSidebar}}</div>

<div>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}</div>

<p class="summary">事件是您在编程时系统内发生的动作或者发生的事情,系统响应事件后,如果需要,您可以某种方式对事件做出回应。例如:如果用户在网页上单击一个按钮,您可能想通过显示一个信息框来响应这个动作。在这篇文章中,我们将讨论一些关于事件的重要概念,并且观察它们在浏览器上如何运行。这篇文章不会面面俱到,仅聚焦于您现阶段需要掌握的知识。</p>

<table class="learn-box standard-table">
 <tbody>
  <tr>
   <th scope="row">前提:</th>
   <td>基本电脑知识, 对HTML和CSS的基本了解,及 <a href="/en-US/docs/Learn/JavaScript/First_steps">JavaScript first steps</a>.</td>
  </tr>
  <tr>
   <th scope="row">目标:</th>
   <td>了解事件的基本理论,它们怎么在浏览器上运行的,以及在不同的编程环境下事件有何不同。</td>
  </tr>
 </tbody>
</table>

<h2 id="一系列事件">一系列事件</h2>

<p>就像上面提到的, <strong>事件</strong>是您在编程时系统内发生的动作或者发生的事情——系统会在事件出现时产生或触发某种信号,并且会提供一个自动加载某种动作(列如:运行一些代码)的机制,比如在一个机场,当跑道清理完成,飞机可以起飞时,飞行员会收到一个信号,因此他们开始起飞。</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/14077/MDN-mozilla-events-runway.png" style="display: block; margin: 0px auto;"></p>

<p>在 Web 中, 事件在浏览器窗口中被触发并且通常被绑定到窗口内部的特定部分 — 可能是一个元素、一系列元素、被加载到这个窗口的 HTML 代码或者是整个浏览器窗口。举几个可能发生的不同事件:</p>

<ul>
 <li>用户在某个元素上点击鼠标或悬停光标。</li>
 <li>用户在键盘中按下某个按键。</li>
 <li>用户调整浏览器的大小或者关闭浏览器窗口。</li>
 <li>一个网页停止加载。</li>
 <li>提交表单。</li>
 <li>播放、暂停、关闭视频。</li>
 <li>发生错误。</li>
</ul>

<p>如果您想看看更多其他的事件 ,请移步至MDN的<a href="/en-US/docs/Web/Events">Event reference</a></p>

<p>每个可用的事件都会有一个<strong>事件处理器</strong>,也就是事件触发时会运行的代码块。当我们定义了一个用来回应事件被激发的代码块的时候,我们说我们<strong>注册了一个事件处理器</strong>。注意事件处理器有时候被叫做<strong>事件监听器</strong>——从我们的用意来看这两个名字是相同的,尽管严格地来说这块代码既监听也处理事件。监听器留意事件是否发生,然后处理器就是对事件发生做出的回应。</p>

<div class="note">
<p><strong>注:</strong> 网络事件不是 JavaScript 语言的核心——它们被定义成内置于浏览器的 JavaScript APIs。</p>
</div>

<h3 id="一个简单的例子">一个简单的例子</h3>

<p>让我们看一个简单的例子。前面您已经见到过很多事件和事件监听器,现在我们概括一下以巩固我们的知识。在接下来的例子中,我们的页面中只有一个 button,按下时,背景会变成随机的一种颜色。</p>

<pre class="brush: html notranslate">&lt;button&gt;Change color&lt;/button&gt;</pre>

<div class="hidden">
<pre class="brush: css notranslate">button { margin: 10px };</pre>
</div>

<p>JavaScript代码如下所示:</p>

<pre class="brush: js notranslate">const btn = document.querySelector('button');

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

btn.onclick = function() {
  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}</pre>

<p>我们使用 <code>btn</code> 变量存储 button,并使用了<code>Document.querySelector()</code> 函数。我们也定义了一个返回随机数字的函数。代码第三部分就是事件处理器。<code>btn</code>变量指向 button 元素,在 button 这种对象上可触发一系列的事件,因此也就可以使用事件处理器。我们通过将一个匿名函数(这个赋值函数包括生成随机色并赋值给背景色的代码)赋值给“点击”事件处理器参数,监听“点击”这个事件。</p>

<p>只要点击事件在<code>&lt;button&gt;</code>元素上触发,该段代码就会被执行。即每当用户点击它时,都会运行此段代码。</p>

<p>示例输出如下:</p>

<p>{{ EmbedLiveSample('A_simple_example', '100%', 200, "", "", "hide-codepen-jsfiddle") }}</p>

<h3 id="这不仅应用在网页上">这不仅应用在网页上</h3>

<p>值得注意的是并不是只有 JavaScript 使用事件——大多的编程语言都有这种机制,并且它们的工作方式不同于 JavaScript。实际上,JavaScript 网页上的事件机制不同于在其他环境中的事件机制。</p>

<p>比如, <a href="/en-US/docs/Learn/Server-side/Express_Nodejs">Node.js</a> 是一种非常流行的允许开发者使用 JavaScript 来建造网络和服务器端应用的运行环境。<a href="https://nodejs.org/docs/latest-v5.x/api/events.html">Node.js event model</a> 依赖定期监听事件的监听器和定期处理事件的处理器——虽然听起来好像差不多,但是实现两者的代码是非常不同的,Node.js 使用像 on ( ) 这样的函数来注册一个事件监听器,使用 once ( ) 这样函数来注册一个在运行一次之后注销的监听器。 <a href="https://nodejs.org/docs/latest-v5.x/api/http.html#http_event_connect">HTTP connect event docs</a> 提供了很多例子。</p>

<p>另外一个例子:您可以使用 JavaScript 来开发跨浏览器的插件(使用 <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions">WebExtensions</a> 开发技术。事件模型和网站的事件模型是相似的,仅有一点点不同——事件监听属性是大驼峰的(如<code>onMessage</code>而不是<code>onmessage</code>),还需要与 <code>addListener</code> 函数结合, 参见 <a href="/en-US/Add-ons/WebExtensions/API/runtime/onMessage#Examples">runtime.onMessage page</a> 上的一个例子。</p>

<p>您现在不需要掌握这些,我们只想表明不同的编程环境下事件机制是不同的,</p>

<h2 id="使用网页事件的方式">使用网页事件的方式</h2>

<p>您可以通过多种不同的方法将事件侦听器代码添加到网页,以便在关联的事件被触发时运行它。在本节中,我们将回顾不同的机制,并讨论应该使用哪些机制。</p>

<h3 id="事件处理器属性">事件处理器属性</h3>

<p><em>这些是我们的课程中最常见到的代码 - 存在于事件处理程序过程的属性中</em>。回到上面的例子:</p>

<pre class="brush: js notranslate">const btn = document.querySelector('button');

btn.onclick = function() {
  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}</pre>

<p>这个 <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onclick">onclick</a></code> 是被用在这个情景下的事件处理器的属性,它就像 button 其他的属性(如 <code><a href="/en-US/docs/Web/API/Node/textContent">btn.textContent</a></code>, or <code><a href="/en-US/docs/Web/API/HTMLElement/style">btn.style</a></code>), 但是有一个特别的地方——当您将一些代码赋值给它的时候,只要事件触发代码就会运行。</p>

<p>您也可以将一个有名字的函数赋值给事件处理参数(正如我们在 <a href="/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Build your own function</a> 中看到的),下面的代码也是这样工作的:</p>

<pre class="brush: js notranslate">const btn = document.querySelector('button');

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

btn.onclick = bgChange;</pre>

<p>有很多事件处理参数可供选择,我们来做一个实验。</p>

<p>首先将 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventhandlerproperty.html">random-color-eventhandlerproperty.html</a> 复制到本地,然后用浏览器打开。别慌,这只是我们之前已经进行过的一个简单随机颜色的示例的代码复制。将 <code>btn.onclick</code> 依次换成其他值,在浏览器中观察效果。</p>

<ul>
 <li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onfocus">btn.onfocus</a></code><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onblur">btn.onblur</a></code> — 颜色将于按钮被置于焦点或解除焦点时改变(尝试使用Tab移动至按钮上,然后再移开)。这些通常用于显示有关如何在置于焦点时填写表单字段的信息,或者如果表单字段刚刚填入不正确的值,则显示错误消息。</li>
 <li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/ondblclick">btn.ondblclick</a></code> — 颜色将仅于按钮被双击时改变。</li>
 <li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeypress">window.onkeypress</a></code>, <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeydown">window.onkeydown</a></code>, <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeyup">window.onkeyup</a></code> — 当按钮被按下时颜色会发生改变. <code>keypress</code> 指的是通俗意义上的按下按钮 (按下并松开), 而 <code>keydown</code> 和 <code>keyup</code> 指的是按键动作的一部分,分别指按下和松开. 注意如果你将事件处理器添加到按钮本身,它将不会工作 — 我们只能将它添加到代表整个浏览器窗口的 <a href="/en-US/docs/Web/API/Window">window</a>对象中。</li>
 <li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onmouseover">btn.onmouseover</a></code> 和 <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onmouseout">btn.onmouseout</a></code> — 颜色将会在鼠标移入按钮上方时发生改变, 或者当它从按钮移出时.</li>
</ul>

<p>一些事件非常通用,几乎在任何地方都可以用(比如 onclick 几乎可以用在几乎每一个元素上),然而另一些元素就只能在特定场景下使用,比如我们只能在 video 元素上使用 <a href="/en-US/docs/Web/API/GlobalEventHandlers/GlobalEventHandlers.onplay">onplay</a> 。</p>

<h3 id="行内事件处理器_-_请勿使用">行内事件处理器 - 请勿使用</h3>

<p>你也许在你的代码中看到过这么一种写法:</p>

<pre class="brush: html notranslate">&lt;button onclick="bgChange()"&gt;Press me&lt;/button&gt;
</pre>

<pre class="brush: js notranslate">function bgChange() {
  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}</pre>

<div class="note">
<p><strong>Note</strong>: 您可以在<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventhandlerattributes.html">GitHub</a>上找到这个示例的完整源代码(也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-eventhandlerattributes.html">在线运行</a>).</p>
</div>

<p>在Web上注册事件处理程序的最早方法是类似于上面所示的<strong>事件处理程序HTML属性</strong>(也称为内联事件处理程序)—属性值实际上是当事件发生时要运行的JavaScript代码。上面的例子中调用一个在{{htmlelement("script")}}元素在同一个页面上,但也可以直接在属性内插入JavaScript,例如:</p>

<pre class="brush: html notranslate">&lt;button onclick="alert('Hello, this is my old-fashioned event handler!');"&gt;Press me&lt;/button&gt;</pre>

<p>你会发现HTML属性等价于对许多事件处理程序的属性;但是,你不应该使用这些 —— 他们被认为是不好的做法。使用一个事件处理属性似乎看起来很简单,如果你只是在做一些非常快的事情,但很快就变得难以管理和效率低下。</p>

<p>一开始,您不应该混用 HTML 和 JavaScript,因为这样文档很难解析——最好的办法是只在一块地方写 JavaScript 代码。</p>

<p>即使在单一文件中,内置事件处理器也不是一个好主意。一个按钮看起来还好,但是如果有一百个按钮呢?您得在文件中加上100个属性。这很快就会成为维护人员的噩梦。使用 Java Script,您可以给网页中的 button 都加上事件处理器。就像下面这样:</p>

<pre class="brush: js notranslate">const buttons = document.querySelectorAll('button');

for (let i = 0; i &lt; buttons.length; i++) {
  buttons[i].onclick = bgChange;
}</pre>

<div class="note">
<p><strong>注释</strong>: 将您的编程逻辑与内容分离也会让您的站点对搜索引擎更加友好。</p>
</div>

<h3 id="addEventListener_和removeEventListener">addEventListener() 和removeEventListener()</h3>

<p>新的事件触发机制被定义在 <a href="https://www.w3.org/TR/DOM-Level-2-Events/">Document Object Model (DOM) Level 2 Events</a> Specification, 这个细则给浏览器提供了一个函数 — <code><a href="/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>。这个函数和事件处理属性是类似的,但是语法略有不同。我们可以重写上面的随机颜色背景代码:</p>

<pre class="brush: js notranslate">const btn = document.querySelector('button');

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

btn.addEventListener('click', bgChange);</pre>

<div class="note">
<p><strong>注释</strong>: 您可以在<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-addeventlistener.html">Github</a>上找到这个示例的完整源代码(也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-addeventlistener.html"> 在线运行</a>)。</p>
</div>

<p><code>在addEventListener()</code> 函数中, 我们具体化了两个参数——我们想要将处理器应用上去的事件名称,和包含我们用来回应事件的函数的代码。注意将这些代码全部放到一个匿名函数中是可行的:</p>

<pre class="brush: js notranslate">btn.addEventListener('click', function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
});
</pre>

<p>这个机制带来了一些相较于旧方式的优点。有一个相对应的方法,<code><a href="/en-US/docs/Web/API/EventTarget/removeEventListener">removeEventListener()</a></code>这个方法移除事件监听器。例如,下面的代码将会移除上个代码块中的事件监听器:</p>

<pre class="brush: js notranslate">btn.removeEventListener('click', bgChange);</pre>

<p>在这个简单的、小型的项目中可能不是很有用,但是在大型的、复杂的项目中就非常有用了,可以非常高效地清除不用的事件处理器,另外在其他的一些场景中也非常有效——比如您需要在不同环境下运行不同的事件处理器,您只需要恰当地删除或者添加事件处理器即可。</p>

<p>您也可以给同一个监听器注册多个处理器,下面这种方式不能实现这一点:</p>

<pre class="brush: js notranslate">myElement.onclick = functionA;
myElement.onclick = functionB;</pre>

<p>第二行会覆盖第一行,但是下面这种方式就会正常工作了:</p>

<pre class="brush: js notranslate">myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);</pre>

<p>当元素被点击时两个函数都会工作:</p>

<p>此外,该事件机制还提供了许多其他强大的特性和选项。这对于本文来说有点超出范围,但是如果您想要阅读它们,请查看<code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code><code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener">removeEventListener()</a></code>参考页面。</p>

<h3 id="我该使用哪种机制?">我该使用哪种机制?</h3>

<p>在三种机制中,您绝对不应该使用HTML事件处理程序属性 - 这些属性已经过时了,而且也是不好的做法,如上所述.</p>

<p>另外两种是相对可互换的,至少对于简单的用途:</p>

<ul>
 <li>事件处理程序属性功能和选项会更少,但是具有更好的跨浏览器兼容性(在Internet Explorer 8的支持下),您应该从这些开始学起。</li>
 <li>DOM Level 2 Events (<code>addEventListener()</code>, etc.) 更强大,但也可以变得更加复杂,并且支持不足(只支持到Internet Explorer 9)。 但是您也应该尝试这个方法,并尽可能地使用它们。</li>
</ul>

<p>第三种机制(DOM Level 2 Events (<code>addEventListener()</code>, etc.))的主要优点是,如果需要的话,可以使用<code>removeEventListener()</code>删除事件处理程序代码,而且如果有需要,您可以向同一类型的元素添加多个监听器。例如,您可以在一个元素上多次调用<code>addEventListener('click', function() { ... })</code>,并可在第二个参数中指定不同的函数。对于事件处理程序属性来说,这是不可能的,因为后面任何设置的属性都会尝试覆盖较早的属性,例如:</p>

<pre class="brush: js notranslate">element.onclick = function1;
element.onclick = function2;
etc.</pre>

<div class="note">
<p><strong>注解</strong>:如果您在工作中被要求支持比Internet Explorer 8更老的浏览器,那么您可能会遇到困难,因为这些古老的浏览器会使用与现代浏览器不同的事件处理模型。但是不要害怕,大多数 JavaScript 库(例如 jQuery )都内置了能够跨浏览器差异的函数。在你学习 JavaScript 旅程里的这个阶段,不要太担心这个问题。</p>
</div>

<h2 id="其他事件概念">其他事件概念</h2>

<p>本节我们将简要介绍一些与事件相关的高级概念。在这一点并不需要完全理解透彻,但它可能有助于你解释一些经常会遇到的代码模式。</p>

<h3 id="事件对象">事件对象</h3>

<p>有时候在事件处理函数内部,您可能会看到一个固定指定名称的参数,例如<code>event</code><code>evt</code>或简单的<code>e</code>。 这被称为<strong>事件对象</strong>,它被自动传递给事件处理函数,以提供额外的功能和信息。 例如,让我们稍稍重写一遍我们的随机颜色示例:</p>

<pre class="brush: js notranslate">function bgChange(e) {
  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  e.target.style.backgroundColor = rndCol;
  console.log(e);
}

btn.addEventListener('click', bgChange);</pre>

<div class="note">
<p><strong>Note</strong>: 您可以在Github上查看这个示例的 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventobject.html">完整代码</a> ,或者在这里查看 <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-eventobject.html">实时演示</a></p>
</div>

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

<div class="note">
<p><strong>Note</strong>: 您可以使用任何您喜欢的名称作为事件对象 - 您只需要选择一个名称,然后可以在事件处理函数中引用它。 开发人员最常使用 e / evt / event,因为它们很简单易记。 坚持标准总是很好。</p>
</div>

<p>当您要在多个元素上设置相同的事件处理程序时,<code>e.target</code>非常有用,并且在发生事件时对所有元素执行某些操作.  例如,你可能有一组16块方格,当它们被点击时就会消失。用e.target总是能准确选择当前操作的东西(方格)并执行操作让它消失,而不是必须以更困难的方式选择它。在下面的示例中(请参见<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/useful-eventtarget.html">useful-eventtarget.html</a>完整代码;也可以在线运行<a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/useful-eventtarget.html">running live</a>)我们使用JavaScript创建了16个<code>&lt;div&gt;</code>元素。接着我们使用 <code>document.querySelectorAll()</code>选择全部的元素,然后遍历每一个,为每一个元素都添加一个<code>onclick</code>单击事件,每当它们点击时就会为背景添加一个随机颜色。</p>

<pre class="brush: js notranslate">const divs = document.querySelectorAll('div');

for (let i = 0; i &lt; divs.length; i++) {
  divs[i].onclick = function(e) {
    e.target.style.backgroundColor = bgChange();
  }
}</pre>

<p>输出如下(试着点击它-玩的开心):</p>

<div class="hidden">
<h6 id="Hidden_example">Hidden example</h6>

<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;title&gt;Useful event target example&lt;/title&gt;
    &lt;style&gt;
      div {
        background-color: red;
        height: 100px;
        width: 25%;
        float: left;
      }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;script&gt;
      for (let i = 1; i &lt;= 16; i++) {
        const myDiv = document.createElement('div');
        document.body.appendChild(myDiv);
      }

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

      function bgChange() {
        var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
        return rndCol;
      }

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

      for (let i = 0; i &lt; divs.length; i++) {
        divs[i].onclick = function(e) {
          e.target.style.backgroundColor = bgChange();
        }
      }
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>
</div>

<p>{{ EmbedLiveSample('Hidden_example', '100%', 400) }}</p>

<p>你遇到的大多数事件处理器的事件对象都有可用的标准属性和函数(方法)(请参阅完整列表 <code>Event</code> 对象引用 )。然而,一些更高级的处理程序会添加一些专业属性,这些属性包含它们需要运行的额外数据。例如,媒体记录器API有一个<code>dataavailable</code>事件,它会在录制一些音频或视频时触发,并且可以用来做一些事情(例如保存它,或者回放)。对应的<code>ondataavailable</code>处理程序的事件对象有一个可用的数据属性。</p>

<h3 id="阻止默认行为">阻止默认行为</h3>

<p>有时,你会遇到一些情况,你希望事件不执行它的默认行为。 最常见的例子是Web表单,例如自定义注册表单。 当你填写详细信息并按提交按钮时,自然行为是将数据提交到服务器上的指定页面进行处理,并将浏览器重定向到某种“成功消息”页面(或 相同的页面,如果另一个没有指定。)</p>

<p>当用户没有正确提交数据时,麻烦就来了 - 作为开发人员,你希望停止提交信息给服务器,并给他们一个错误提示,告诉他们什么做错了,以及需要做些什么来修正错误。 一些浏览器支持自动的表单数据验证功能,但由于许多浏览器不支持,因此建议你不要依赖这些功能,并实现自己的验证检查。 我们来看一个简单的例子。</p>

<p>首先,一个简单的HTML表单,需要你填入名(first name)和姓(last name)</p>

<pre class="brush: html notranslate">&lt;form&gt;
  &lt;div&gt;
    &lt;label for="fname"&gt;First name: &lt;/label&gt;
    &lt;input id="fname" type="text"&gt;
  &lt;/div&gt;
  &lt;div&gt;
    &lt;label for="lname"&gt;Last name: &lt;/label&gt;
    &lt;input id="lname" type="text"&gt;
  &lt;/div&gt;
  &lt;div&gt;
     &lt;input id="submit" type="submit"&gt;
  &lt;/div&gt;
&lt;/form&gt;
&lt;p&gt;&lt;/p&gt;</pre>

<div class="hidden">
<pre class="brush: css notranslate">div {
  margin-bottom: 10px;
}
</pre>
</div>

<p>这里我们用一个<code>onsubmit</code>事件处理程序(在提交的时候,在一个表单上发起<code>submit</code>事件)来实现一个非常简单的检查,用于测试文本字段是否为空。 如果是,我们在事件对象上调用<code>preventDefault()</code>函数,这样就停止了表单提交,然后在我们表单下面的段落中显示一条错误消息,告诉用户什么是错误的:</p>

<pre class="brush: js notranslate">const form = document.querySelector('form');
const fname = document.getElementById('fname');
const lname = document.getElementById('lname');
const submit = document.getElementById('submit');
const para = document.querySelector('p');

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

<p>显然,这是一种非常弱的表单验证——例如,用户输入空格或数字提交表单,表单验证并不会阻止用户提交——这不是我们例子想要达到的目的。输出如下:</p>

<p>{{ EmbedLiveSample('Preventing_default_behaviour', '100%', 140) }}</p>

<div class="note">
<p><strong>Note</strong>: 查看完整的源代码 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/preventdefault-validation.html">preventdefault-validation.html</a> (也可以 <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/preventdefault-validation.html">running live</a> )</p>
</div>

<h3 id="事件冒泡及捕获">事件冒泡及捕获</h3>

<p>最后即将介绍的这个主题你常常不会深究,但如果你不理解这个主题,就会十分痛苦。事件冒泡和捕捉是两种机制,主要描述当在一个元素上有两个相同类型的事件处理器被激活会发生什么。为了容易理解,我们来看一个例子——在新标签页打开这个<a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/show-video-box.html">show-video-box.html</a> 例子(在这里可以查看源码 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box.html">source code</a>)。也可以在下面查看:</p>

<div class="hidden">
<h6 id="Hidden_video_example">Hidden video example</h6>

<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;title&gt;Show video box example&lt;/title&gt;
    &lt;style&gt;
      div {
        position: absolute;
        top: 50%;
        transform: translate(-50%,-50%);
        width: 480px;
        height: 380px;
        border-radius: 10px;
        background-color: #eee;
        background-image: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,0.1));
      }

      .hidden {
        left: -50%;
      }

      .showing {
        left: 50%;
      }

      div video {
        display: block;
        width: 400px;
        margin: 40px auto;
      }

    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;button&gt;Display video&lt;/button&gt;

    &lt;div class="hidden"&gt;
      &lt;video&gt;
        &lt;source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.mp4" type="video/mp4"&gt;
        &lt;source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.webm" type="video/webm"&gt;
        &lt;p&gt;Your browser doesn't support HTML5 video. Here is a &lt;a href="rabbit320.mp4"&gt;link to the video&lt;/a&gt; instead.&lt;/p&gt;
      &lt;/video&gt;
    &lt;/div&gt;

    &lt;script&gt;

      const btn = document.querySelector('button');
      const videoBox = document.querySelector('div');
      const video = document.querySelector('video');

      btn.onclick = function() {
        displayVideo();
      }

      function displayVideo() {
        if(videoBox.getAttribute('class') === 'hidden') {
          videoBox.setAttribute('class','showing');
        }
      }

      videoBox.addEventListener('click',function() {
        videoBox.setAttribute('class','hidden');
      });

      video.addEventListener('click',function() {
        video.play();
      });

    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>
</div>

<p>{{ EmbedLiveSample('Hidden_video_example', '100%', 500) }}</p>

<p>这是一个非常简单的例子,它显示和隐藏一个包含<code>&lt;video&gt;</code>元素的<code>&lt;div&gt;</code>元素:</p>

<pre class="brush: html notranslate">&lt;button&gt;Display video&lt;/button&gt;

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

<p>当‘’button‘’元素按钮被单击时,将显示视频,它是通过将改变<code>&lt;div&gt;</code>class属性值从<code>hidden</code>变为<code>showing</code>(这个例子的CSS包含两个<code>class</code>,它们分别控制这个<code>&lt;div&gt;</code>盒子在屏幕上显示还是隐藏。):</p>

<pre class="brush: js notranslate">btn.onclick = function() {
  videoBox.setAttribute('class', 'showing');
}</pre>

<p>然后我们再添加几个<code>onclick</code>事件处理器,第一个添加在<code>&lt;div&gt;</code>元素上,第二个添加在<code>&lt;video&gt;</code>元素上。这个想法是当视频(<code>&lt;video&gt;</code>)外 <code>&lt;div&gt;</code>元素内这块区域被单击时,这个视频盒子应该再次隐藏;当单击视频(<code>&lt;video&gt;</code>)本身,这个视频将开始播放。</p>

<pre class="notranslate">videoBox.onclick = function() {
  videoBox.setAttribute('class', 'hidden');
};

video.onclick = function() {
  video.play();
};</pre>

<p>但是有一个问题 - 当您点击<code>video</code>开始播放的视频时,它会在同一时间导致<code>&lt;div&gt;</code>也被隐藏。 这是因为<code>video</code><code>&lt;div&gt;</code>之内 - <code>video</code><code>&lt;div&gt;</code>的一个子元素 - 所以点击<code>video</code>实际上是同时也运行<code>&lt;div&gt;</code>上的事件处理程序。</p>

<h4 id="对事件冒泡和捕捉的解释">对事件冒泡和捕捉的解释</h4>

<p>当一个事件发生在具有父元素的元素上(例如,在我们的例子中是<code>&lt;video&gt;</code>元素)时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。 在捕获阶段:</p>

<ul>
 <li>浏览器检查元素的最外层祖先<code>&lt;html&gt;</code>,是否在捕获阶段中注册了一个<code>onclick</code>事件处理程序,如果是,则运行它。</li>
 <li>然后,它移动到<code>&lt;html&gt;</code>中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。</li>
</ul>

<p>在冒泡阶段,恰恰相反:</p>

<ul>
 <li>浏览器检查实际点击的元素是否在冒泡阶段中注册了一个<code>onclick</code>事件处理程序,如果是,则运行它</li>
 <li>然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达<code>&lt;html&gt;</code>元素。</li>
</ul>

<p><a href="https://mdn.mozillademos.org/files/14075/bubbling-capturing.png"><img alt="" src="https://mdn.mozillademos.org/files/14075/bubbling-capturing.png" style="display: block; height: 452px; margin: 0px auto; width: 960px;"></a></p>

<p>(单击图片可以放大这个图表)</p>

<p>在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册。因此,在我们当前的示例中,当您单击视频时,这个单击事件从 <code>&lt;video&gt;</code>元素向外冒泡直到<code>&lt;html&gt;</code>元素。沿着这个事件冒泡线路:</p>

<ul>
 <li>它发现了<code>video.onclick...</code>事件处理器并且运行它,因此这个视频<code>&lt;video&gt;</code>第一次开始播放。</li>
 <li>接着它发现了(往外冒泡找到的) <code>videoBox.onclick...</code>事件处理器并且运行它,因此这个视频<code>&lt;video&gt;</code>也隐藏起来了。</li>
</ul>

<h4 id="用_stopPropagation_修复问题">用 stopPropagation() 修复问题</h4>

<p>这是令人讨厌的行为,但有一种方法来解决它!标准事件对象具有可用的名为 <code><a href="/en-US/docs/Web/API/Event/stopPropagation">stopPropagation()</a></code>的函数, 当在事件对象上调用该函数时,它只会让当前事件处理程序运行,但事件不会在<strong>冒泡</strong>链上进一步扩大,因此将不会有更多事件处理器被运行(不会向上冒泡)。所以,我们可以通过改变前面代码块中的第二个处理函数来解决当前的问题:</p>

<pre class="brush: js notranslate">video.onclick = function(e) {
  e.stopPropagation();
  video.play();
};</pre>

<p>你可以尝试把 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box.html">show-video-box.html source code</a> 拷贝到本地,然后自己动手修复它,或者在 <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/show-video-box-fixed.html">show-video-box-fixed.html</a> 页面查看修复结果(也可以在这里 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box-fixed.html">source code</a> 查看源码)。</p>

<div class="note">
<p><strong>注解</strong>: 为什么我们要弄清楚捕捉和冒泡呢?那是因为,在过去糟糕的日子里,浏览器的兼容性比现在要小得多,Netscape(网景)只使用事件捕获,而Internet Explorer只使用事件冒泡。当W3C决定尝试规范这些行为并达成共识时,他们最终得到了包括这两种情况(捕捉和冒泡)的系统,最终被应用在现在浏览器里。</p>
</div>

<div class="note">
<p><strong>注解</strong>: 如上所述,默认情况下,所有事件处理程序都是在冒泡阶段注册的,这在大多数情况下更有意义。如果您真的想在捕获阶段注册一个事件,那么您可以通过使用<code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>注册您的处理程序,并将可选的第三个属性设置为true。</p>
</div>

<h4 id="事件委托">事件委托</h4>

<p>冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。</p>

<p>一个很好的例子是一系列列表项,如果你想让每个列表项被点击时弹出一条信息,您可以将<code>click</code>单击事件监听器设置在父元素<code>&lt;ul&gt;</code>上,这样事件就会从列表项冒泡到其父元素<code>&lt;ul&gt;</code>上。</p>

<p>这个的概念在David Walsh的博客上有更多的解释,并有多个例子——看看<a href="https://davidwalsh.name/event-delegate">How JavaScript Event Delegation Works</a>.</p>

<h2 id="结论">结论</h2>

<p>现在您应该知道在这个早期阶段您需要了解的所有web事件。如上所述,事件并不是JavaScript的核心部分——它们是在浏览器Web APIs中定义的。</p>

<p>另外,理解JavaScript在不同环境下使用不同的事件模型很重要——从Web api到其他领域,如浏览器WebExtensions和Node.js(服务器端JavaScript)。我们并不期望您现在了解所有这些领域,但是当您在学习web开发的过程中,理解这些事件的基础是很有帮助的。</p>

<p>如果你有什么不明白的地方,请重新阅读这篇文章,或者联系<a href="https://developer.mozilla.org/en-US/Learn#Contact_us">contact us</a>我们寻求帮助。</p>

<h2 id="参见">参见</h2>

<ul>
 <li><a href="http://www.quirksmode.org/js/events_order.html">Event order</a> (discussion of capturing and bubbling) — an excellently detailed piece by Peter-Paul Koch.</li>
 <li><a href="http://www.quirksmode.org/js/events_access.html">Event accessing</a> (discussing of the event object) — another excellently detailed piece by Peter-Paul Koch.</li>
 <li><a href="/en-US/docs/Web/Events">Event reference</a></li>
</ul>

<p>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}</p>