From 2961429306b494587545fa5541dc3fcd0e878ee8 Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Sat, 12 May 2018 11:21:17 +0800 Subject: [PATCH] 15. --- 15.md | 193 ++++++++++++++++++++++++---------------------------------- 1 file changed, 81 insertions(+), 112 deletions(-) diff --git a/15.md b/15.md index 3eef388..402bb1e 100644 --- a/15.md +++ b/15.md @@ -4,39 +4,39 @@ > > Marcus Aurelius, Meditations -有些程序需要处理用户的直接输入,比如鼠标和键盘输入。这些输入的时间和顺序无法提前预知。处理这些输入需要的控制流方法与我们迄今为止所使用的方法不同。 +有些程序需要处理用户的直接输入,比如鼠标和键盘动作。这种输入方式不是组织整齐的数据结构 - 它是一次一个地,实时地出现的,并且期望程序在发生时作出响应。 ### 14.1 事件处理器 想象一下,有一个接口,若想知道键盘上是否有一个键是否被按下,唯一的方法是读取那个按键的当前状态。为了能够响应按键动作,你需要不断读取键盘状态,以在按键被释放之前捕捉到按下状态。这种方法在执行时间密集计算时非常危险,因为你可能错过按键事件。 -这是原始机器上处理输入时的方法。有一种更进一步的方法,硬件或操作系统发现按键时间并将其放入队列中。程序可以周期性地检查队列,等待新事件并在发现事件时进行响应。 +一些原始机器可以像那样处理输入。有一种更进一步的方法,硬件或操作系统发现按键时间并将其放入队列中。程序可以周期性地检查队列,等待新事件并在发现事件时进行响应。 -当然,程序必须记得监视队列,并经常做这种事,因为任何时候,按键被按下和程序发现事件之间都会使得软件反应迟钝。该方法被称为轮询。大多数程序员都会尽可能避免这种方法。 +当然,程序必须记得监视队列,并经常做这种事,因为任何时候,按键被按下和程序发现事件之间都会使得软件反应迟钝。该方法被称为轮询。大多数程序员更希望避免这种方法。 -一种更好的机制是底层系统给予我们的代码在事件发生时响应的机会。浏览器实现了这种特性,支持我们将函数注册为特定事件的处理器。 +一个更好的机制是,系统在发生事件时主动通知我们的代码。浏览器实现了这种特性,支持我们将函数注册为特定事件的处理器。 ```html

Click this document to activate the handler.

``` -addEventListener函数会注册第二个参数,当第一个参数描述的时间触发时,就会调用第二个参数。 +`window`绑定指向浏览器提供的内置对象。 它代表包含文档的浏览器窗口。 调用它的`addEventListener`方法注册第二个参数,以便在第一个参数描述的事件发生时调用它。 ### 14.2 事件与DOM节点 -每个浏览器事件处理器被注册在上下文中。当你如上所示调用addEventListener时,你可以在整个窗口上调用该方法,因为在浏览器中全局作用域等同于window对象。每个DOM元素都有其自己的addEventListener方法,支持在特定元素上监听事件。 +每个浏览器事件处理器被注册在上下文中。在为整个窗口注册处理器之前,我们在`window`对象上调用了`addEventListener`。 这种方法也可以在 DOM 元素和一些其他类型的对象上找到。 仅当事件发生在其注册对象的上下文中时,才调用事件监听器。 ```html

No handler here.

@@ -44,14 +44,16 @@ addEventListener函数会注册第二个参数,当第一个参数描述的时 示例代码中将处理器附加到按钮节点上。因此,点击按钮时会触发并执行处理器,而点击文档的其他部分则没有反应。 -为节点执行onclick属性也有类似效果。但一个节点只有一个onclick属性,因此只能使用这种方法为每个节点注册一个处理器。而addEventListener方法则允许你添加任意数量的方法,因此你不会无意间替换掉已经注册的处理器。 +向节点提供onclick属性也有类似效果。这适用于大多数类型的事件 - 您可以为属性附加处理器,属性名称为前面带有`on`的事件名称。 -使用参数调用removeEventListener方法与addEventListener方式相似,可以用于移除处理器。 +但是一个节点只能有一个`onclick`属性,所以你只能用这种方式为每个节点注册一个处理器。 `addEventListener`方法允许您添加任意数量的处理器,因此即使元素上已经存在另一个处理器,添加处理器也是安全的。 + +`removeEventListener`方法将删除一个处理器,使用类似于`addEventListener`的参数调用。 ```html ``` -我们需要给事件处理函数一个名称(比如once),才能反注册处理器函数,并将处理函数名称传递给addEventListener和removeEventListener。 +赋予`removeEventListener`的函数必须是赋予`addEventListener`的完全相同的函数值。 因此,要注销一个处理其,您需要为该函数提供一个名称(在本例中为`once`),以便能够将相同的函数值传递给这两个方法。 ### 14.3 事件对象 -虽然在前面示例中被我们忽略了,但我们要知道事件触发时会为事件处理函数传递一个参数:事件(Event)对象。例如,如果我们想知道哪个鼠标按键被按下,我们可以查看事件对象的which属性。 +虽然目前为止我们忽略了它,事件处理器函数作为对象传递:事件(Event)对象。这个对象持有事件的额外信息。例如,如果我们想知道哪个鼠标按键被按下,我们可以查看事件对象的which属性。 ```html ``` @@ -85,7 +88,7 @@ addEventListener函数会注册第二个参数,当第一个参数描述的时 ### 14.4 传播 -若节点含有子节点,则在节点上注册的事件处理器也会接收到在孩子节点中发生的事件。若点击一个段落中的按钮,段落的事件处理器也会收到点击事件。 +对于大多数事件类型,在具有子节点的节点上注册的处理器,也将接收发生在子节点中的事件。若点击一个段落中的按钮,段落的事件处理器也会收到点击事件。 但若段落和按钮都有事件处理器,则先执行最特殊的事件处理器(按钮的事件处理器)。也就是说事件向外传播,从触发事件的节点到其父节点,最后直到文档根节点。最后,当某个特定节点上注册的所有事件处理器按其顺序全部执行完毕后,窗口对象的事件处理器才有机会响应事件。 @@ -96,15 +99,14 @@ addEventListener函数会注册第二个参数,当第一个参数描述的时 ```html

A paragraph with a .

``` @@ -118,9 +120,10 @@ addEventListener函数会注册第二个参数,当第一个参数描述的时 ``` @@ -129,24 +132,24 @@ addEventListener函数会注册第二个参数,当第一个参数描述的时 大多数事件都有与其关联的默认动作。若点击链接,就会跳转到链接目标。若点击向下的箭头,浏览器会向下翻页。若右击鼠标,可以得到一个上下文菜单等。 -对于大多数类型的事件,JavaScript事件处理器会在默认处理器执行前被调用。若事件处理器不希望执行默认行为(通常是因为已经处理了该事件),会调用preventDefault事件对象的方法。 +对于大多数类型的事件,JavaScript事件处理器会在默认行为发生之前调用。若事件处理器不希望执行默认行为(通常是因为已经处理了该事件),会调用preventDefault事件对象的方法。 你可以实现你自己的键盘快捷键或交互式菜单。你也可以干扰用户期望的行为。例如,这里实现一个无法跳转的链接。 ```html MDN ``` -除非你有非常充足的理由,否则不要这样做。由于人们都会使用你的网页,当其期望的行为被破坏时会非常不开心。 +除非你有非常充足的理由,否则不要这样做。当预期的行为被打破时,使用你的页面的人会感到不快。 -在有些浏览器中,你无法拦截某些事件。比如在Chrome中,关闭键盘快捷键(CTRL-W或COMMAND-W)无法由JavaScript处理。 +在有些浏览器中,你完全无法拦截某些事件。比如在Chrome中,关闭键盘快捷键(CTRL-W或COMMAND-W)无法由JavaScript处理。 ### 14.6 按键事件 @@ -155,68 +158,57 @@ addEventListener函数会注册第二个参数,当第一个参数描述的时 ```html

This page turns violet when you hold the V key.

``` -尽管从keydown这个事件名上看应该是物理按键按下时触发,但当持续按下某个按键时,会循环触发该事件。有时,例如你想在按键按下时增加游戏属性,而在按键释放时减缓游戏属性,此时你要注意,不要在每次触发按键事件时都增加游戏属性,否则最后无意之间会得到巨大的值。 +尽管从keydown这个事件名上看应该是物理按键按下时触发,但当持续按下某个按键时,会循环触发该事件。有时,你想谨慎对待它。例如,如果您在按下某个按键时向 DOM 添加按钮,并且在释放按键时再次将其删除,则可能会在按住某个按键的时间过长时,意外添加数百个按钮。 -前面的示例查看了事件对象的keyCode属性。该属性标识了用户按下或释放了哪个按键。遗憾的是将数值按键代码转换成实际按键的方法并不总是显而易见的。 - -对于字母和数字键,其按键代码是与键盘上的字母(大写)或数字相关的Unicode字符。而字符串的charCodeAt方法则让我们可以找到字符对应的代码。 - -```html -console.log("Violet".charCodeAt(0)); -// → 86 -console.log("1".charCodeAt(0)); -// → 49 -``` - -其他的按键就不是那么可预见的了。寻找代码常常需要通过实验,注册一个按键事件处理器,记录下获得的按键代码然后按下你想了解的按键。 +该示例查看了事件对象的`key`属性,来查看事件关于哪个键。 该属性包含一个字符串,对于大多数键,它对应于按下该键时将键入的内容。 对于像`Enter`这样的特殊键,它包含一个用于命名键的字符串(在本例中为`"Enter"`)。 如果你按住一个键的同时按住`Shift`键,这也可能影响键的名称 - `"v"`变为`"V"`,`"1"`可能变成`"!"`,这是按下`Shift-1`键 在键盘上产生的东西。 诸如shift、ctrl、alt和meta(Mac上的command)之类的修饰按键会像普通按键一样产生事件。但在查找组合键时,你也可以查看键盘和鼠标事件的shiftKey、ctrlKey、altKey和metaKey属性来判断这些键是否被按下。 ```html

Press Ctrl-Space to continue.

``` -keydown和keyup事件告知我们按下的物理按键信息。但假如你想得到的是实际按下的文本该怎么办呢?从按键代码获取文本是很烦琐的。有另一个事件可以取而代之,该事件名为keypress,该事件在keydown之后触发(当按键按住不放时与keydown一样会反复触发),但只获得按键的输入。 +按键事件发生的 DOM 节点取决于按下按键时具有焦点的元素。 大多数节点不能拥有焦点,除非你给他们一个`tabindex`属性,但像链接,按钮和表单字段可以。 我们将在第 18 章中回顾表单字段。 当没有特别的焦点时,`document.body`充当按键事件的目标节点。 -```html -

Focus this page and type something.

- -``` +当用户键入文本时,使用按键事件来确定正在键入的内容是有问题的。 某些平台,尤其是 Android 手机上的虚拟键盘,不会触发按键事件。 但即使你有一个老式键盘,某些类型的文本输入也不能直接匹配按键,例如其脚本不适合键盘的人所使用的 IME(“输入法编辑器”)软件 ,其中组合多个热键来创建字符。 -按键事件的起源DOM节点取决于按键按下时,焦点在哪个元素上。正常的节点无法拥有焦点(除非你赋予其tabindex属性),但诸如链接、按钮和表单字段之类的元素都可以拥有焦点。我们将在第18章中回过头来看表单字段。当任何元素都没有焦点时,document.body扮演了按键事件的目标节点。 +要注意什么时候输入了内容,每当用户更改其内容时,可以键入的元素(例如``和`