1
0
mirror of https://github.com/apachecn/eloquent-js-3e-zh.git synced 2025-05-23 20:02:20 +00:00
This commit is contained in:
wizardforcel 2018-05-12 12:05:55 +08:00
parent 2961429306
commit f5c3f37455

236
15.md
View File

@ -6,7 +6,7 @@
有些程序需要处理用户的直接输入,比如鼠标和键盘动作。这种输入方式不是组织整齐的数据结构 - 它是一次一个地,实时地出现的,并且期望程序在发生时作出响应。 有些程序需要处理用户的直接输入,比如鼠标和键盘动作。这种输入方式不是组织整齐的数据结构 - 它是一次一个地,实时地出现的,并且期望程序在发生时作出响应。
### 14.1 事件处理器 ## 事件处理器
想象一下,有一个接口,若想知道键盘上是否有一个键是否被按下,唯一的方法是读取那个按键的当前状态。为了能够响应按键动作,你需要不断读取键盘状态,以在按键被释放之前捕捉到按下状态。这种方法在执行时间密集计算时非常危险,因为你可能错过按键事件。 想象一下,有一个接口,若想知道键盘上是否有一个键是否被按下,唯一的方法是读取那个按键的当前状态。为了能够响应按键动作,你需要不断读取键盘状态,以在按键被释放之前捕捉到按下状态。这种方法在执行时间密集计算时非常危险,因为你可能错过按键事件。
@ -27,7 +27,7 @@
`window`绑定指向浏览器提供的内置对象。 它代表包含文档的浏览器窗口。 调用它的`addEventListener`方法注册第二个参数,以便在第一个参数描述的事件发生时调用它。 `window`绑定指向浏览器提供的内置对象。 它代表包含文档的浏览器窗口。 调用它的`addEventListener`方法注册第二个参数,以便在第一个参数描述的事件发生时调用它。
### 14.2 事件与DOM节点 ## 事件与DOM节点
每个浏览器事件处理器被注册在上下文中。在为整个窗口注册处理器之前,我们在`window`对象上调用了`addEventListener`。 这种方法也可以在 DOM 元素和一些其他类型的对象上找到。 仅当事件发生在其注册对象的上下文中时,才调用事件监听器。 每个浏览器事件处理器被注册在上下文中。在为整个窗口注册处理器之前,我们在`window`对象上调用了`addEventListener`。 这种方法也可以在 DOM 元素和一些其他类型的对象上找到。 仅当事件发生在其注册对象的上下文中时,才调用事件监听器。
@ -64,7 +64,7 @@
赋予`removeEventListener`的函数必须是赋予`addEventListener`的完全相同的函数值。 因此,要注销一个处理其,您需要为该函数提供一个名称(在本例中为`once`),以便能够将相同的函数值传递给这两个方法。 赋予`removeEventListener`的函数必须是赋予`addEventListener`的完全相同的函数值。 因此,要注销一个处理其,您需要为该函数提供一个名称(在本例中为`once`),以便能够将相同的函数值传递给这两个方法。
### 14.3 事件对象 ## 事件对象
虽然目前为止我们忽略了它事件处理器函数作为对象传递事件Event对象。这个对象持有事件的额外信息。例如如果我们想知道哪个鼠标按键被按下我们可以查看事件对象的which属性。 虽然目前为止我们忽略了它事件处理器函数作为对象传递事件Event对象。这个对象持有事件的额外信息。例如如果我们想知道哪个鼠标按键被按下我们可以查看事件对象的which属性。
@ -86,7 +86,7 @@
存储在各种类型事件对象中的信息是有差别的。随后本章将会讨论许多类型的事件。对象的type属性一般持有一个字符串表示事件例如“click”和“mousedown” 存储在各种类型事件对象中的信息是有差别的。随后本章将会讨论许多类型的事件。对象的type属性一般持有一个字符串表示事件例如“click”和“mousedown”
### 14.4 传播 ## 传播
对于大多数事件类型,在具有子节点的节点上注册的处理器,也将接收发生在子节点中的事件。若点击一个段落中的按钮,段落的事件处理器也会收到点击事件。 对于大多数事件类型,在具有子节点的节点上注册的处理器,也将接收发生在子节点中的事件。若点击一个段落中的按钮,段落的事件处理器也会收到点击事件。
@ -128,7 +128,7 @@
</script> </script>
``` ```
### 14.5 默认动作 ## 默认动作
大多数事件都有与其关联的默认动作。若点击链接,就会跳转到链接目标。若点击向下的箭头,浏览器会向下翻页。若右击鼠标,可以得到一个上下文菜单等。 大多数事件都有与其关联的默认动作。若点击链接,就会跳转到链接目标。若点击向下的箭头,浏览器会向下翻页。若右击鼠标,可以得到一个上下文菜单等。
@ -151,7 +151,7 @@
在有些浏览器中你完全无法拦截某些事件。比如在Chrome中关闭键盘快捷键CTRL-W或COMMAND-W无法由JavaScript处理。 在有些浏览器中你完全无法拦截某些事件。比如在Chrome中关闭键盘快捷键CTRL-W或COMMAND-W无法由JavaScript处理。
### 14.6 按键事件 ## 按键事件
当按下键盘上的按键时浏览器会触发“keydown”事件。当松开按键时会触发“keyup”事件。 当按下键盘上的按键时浏览器会触发“keydown”事件。当松开按键时会触发“keyup”事件。
@ -198,7 +198,7 @@
目前有两种广泛使用的方式,用于指向屏幕上的东西:鼠标(包括类似鼠标的设备,如触摸板和轨迹球)和触摸屏。 它们产生不同类型的事件。 目前有两种广泛使用的方式,用于指向屏幕上的东西:鼠标(包括类似鼠标的设备,如触摸板和轨迹球)和触摸屏。 它们产生不同类型的事件。
### 鼠标点击 ## 鼠标点击
点击鼠标按钮会触发一系列事件。“mousedown”事件和“mouseup”事件类似于“keydown”和“keyup”事件当鼠标按钮按下或释放时触发。当事件发生时由鼠标指针下方的DOM节点触发事件。 点击鼠标按钮会触发一系列事件。“mousedown”事件和“mouseup”事件类似于“keydown”和“keyup”事件当鼠标按钮按下或释放时触发。当事件发生时由鼠标指针下方的DOM节点触发事件。
@ -234,7 +234,7 @@
</script> </script>
``` ```
### 鼠标移动 ## 鼠标移动
每次鼠标移动时都会触发“mousemove”事件。该事件可用于跟踪鼠标位置。当实现某些形式的鼠标拖拽功能时该事件非常有用。 每次鼠标移动时都会触发“mousemove”事件。该事件可用于跟踪鼠标位置。当实现某些形式的鼠标拖拽功能时该事件非常有用。
@ -276,67 +276,86 @@
请注意,这些代码的顺序与`button`使用的顺序不同,中键位于右键之前。 如前所述,一致性并不是浏览器编程接口的强项。 请注意,这些代码的顺序与`button`使用的顺序不同,中键位于右键之前。 如前所述,一致性并不是浏览器编程接口的强项。
### 触摸事件 ## 触摸事件
我们使用的图形浏览器的风格,是考虑到鼠标界面的情况下而设计的,那个时候触摸屏非常罕见。 为了使网络在早期的触摸屏手机上“工作”,在某种程度上,这些设备的浏览器假装触摸事件是鼠标事件。 如果你点击你的屏幕,你会得到`'mousedown'``'mouseup'``'click'`事件。 我们使用的图形浏览器的风格,是考虑到鼠标界面的情况下而设计的,那个时候触摸屏非常罕见。 为了使网络在早期的触摸屏手机上“工作”,在某种程度上,这些设备的浏览器假装触摸事件是鼠标事件。 如果你点击你的屏幕,你会得到`'mousedown'``'mouseup'``'click'`事件。
但是这种错觉不是很健壮。 触摸屏与鼠标的工作方式不同:它没有多个按钮,当手指不在屏幕上时不能跟踪手指(来模拟`"mousemove"`),并且允许多个手指同时在屏幕上。
函数isInside会沿着指定节点父节点链接寻找节点直到到达文档顶部当node为null时或找到了目标父节点为止 鼠标事件只涵盖了简单情况下的触摸交互 - 如果您为按钮添加`"click"`处理器,触摸用户仍然可以使用它。 但是像上一个示例中的可调整大小的栏在触摸屏上不起作用
我们可以使用CSS的伪选择子pseudoselectorhover来更轻松地实现同样的效果。但当浮动效果更加复杂不只是改变目标节点效果你必须使用mouseover和mouseout事件这种技巧 触摸交互触发了特定的事件类型。 当手指开始触摸屏幕时,您会看到`'touchstart'`事件。 当它在触摸中移动时,触发`"touchmove"`事件。 最后,当它停止触摸屏幕时,您会看到`"touchend"`事件
由于许多触摸屏可以同时检测多个手指,这些事件没有与其关联的一组坐标。 相反,它们的事件对象拥有`touches`属性,它拥有一个类数组对象,每个对象都有自己的`clientX``clientY``pageX``pageY`属性。
你可以这样,在每个触摸手指周围显示红色圆圈。
```html ```html
<style> <style>
p:hover { color: red } dot { position: absolute; display: block;
border: 2px solid red; border-radius: 50px;
height: 100px; width: 100px; }
</style> </style>
<p>Hover over this <strong>paragraph</strong>.</p> <p>Touch this page</p>
<script>
function update(event) {
for (let dot; dot = document.querySelector("dot");) {
dot.remove();
}
for (let i = 0; i < event.touches.length; i++) {
let {pageX, pageY} = event.touches[i];
let dot = document.createElement("dot");
dot.style.left = (pageX - 50) + "px";
dot.style.top = (pageY - 50) + "px";
document.body.appendChild(dot);
}
}
window.addEventListener("touchstart", update);
window.addEventListener("touchmove", update);
window.addEventListener("touchend", update);
</script>
``` ```
### 14.9 滚动事件 您经常希望在触摸事件处理器中调用`preventDefault`,来覆盖浏览器的默认行为(可能包括在滑动时滚动页面),并防止触发鼠标事件,您也可能拥有它的处理器。
## 滚动事件
每当元素滚动时会触发scroll事件。该事件用处极多比如知道用户当前查看的元素禁用用户视线以外的动画或向邪恶的指挥部发送监视报告或展示一些滚动的迹象通过高亮表格的部分内容或显示页码 每当元素滚动时会触发scroll事件。该事件用处极多比如知道用户当前查看的元素禁用用户视线以外的动画或向邪恶的指挥部发送监视报告或展示一些滚动的迹象通过高亮表格的部分内容或显示页码
下面的示例在文档右下角绘制进度条,向下滚动时更新进度条: 以下示例在文档上方绘制一个进度条,并在您向下滚动时更新它来填充
```html ```html
<style> <style>
.progress { #progress {
border: 1px solid blue; border-bottom: 2px solid blue;
width: 100px; width: 0;
position: fixed; position: fixed;
top: 10px; right: 10px; top: 0; left: 0;
}
.progress > div {
height: 12px;
background: blue;
width: 0%;
}
body {
height: 2000px;
} }
</style> </style>
<div class="progress"><div></div></div> <div id="progress"></div>
<p>Scroll me...</p>
<script> <script>
var bar = document.querySelector(".progress div"); // Create some content
addEventListener("scroll", function() { document.body.appendChild(document.createTextNode(
var max = document.body.scrollHeight - innerHeight; "supercalifragilisticexpialidocious ".repeat(1000)));
var percent = (pageYOffset / max) * 100;
bar.style.width = percent + "%"; let bar = document.querySelector("#progress");
window.addEventListener("scroll", () => {
let max = document.body.scrollHeight - innerHeight;
bar.style.width = `${(pageYOffset / max) * 100}%`;
}); });
</script> </script>
``` ```
将元素的position属性指定为fixed时其行为和absolute很像但可以防止在文档滚动时期跟着文档一起滚动。其效果是使及进度条一直待在角落。进度条内还有其他元素,会随着当前进度改变尺寸。我们使用%而非px作为宽度单位这使得元素尺寸是相对于整个进度条来计算的 将元素的position属性指定为fixed时其行为和absolute很像但可以防止在文档滚动时期跟着文档一起滚动。其效果是让我们的进度条呆在最顶上。 改变其宽度来指示当前进度。 在设置宽度时,我们使用`%`而不是`px`作为单位,使元素的大小相对于页面宽度
innerHeight全局变量是窗口高度,我们必须要减去滚动条的高度。你点击文档底部的时候是无法继续滚动的和innerHeight一样还有innerWidth变量。使用pageYOffset当前滚动位置除以最大滚动位置并乘以100就可以得到进度条长度。 innerHeight全局绑定是窗口高度,我们必须要减去滚动条的高度。你点击文档底部的时候是无法继续滚动的。对于窗口高度来说,也存在`innerWidth`。使用pageYOffset当前滚动位置除以最大滚动位置并乘以100就可以得到进度条长度。
调用滚动事件的preventDefault无法阻止滚动。实际上事件处理器是在进行滚动之后才触发的。 调用滚动事件的preventDefault无法阻止滚动。实际上事件处理器是在进行滚动之后才触发的。
### 14.10 焦点事件 ## 焦点事件
当元素获得焦点时浏览器会触发其上的focus事件。当失去焦点时触发blur事件。 当元素获得焦点时浏览器会触发其上的focus事件。当失去焦点时,元素会获得blur事件。
与前文讨论的事件不同,这两个事件不会传播。子元素获得或失去焦点时,不会激活父元素的处理器。 与前文讨论的事件不同,这两个事件不会传播。子元素获得或失去焦点时,不会激活父元素的处理器。
@ -344,18 +363,18 @@ innerHeight全局变量是窗口高度我们必须要减去滚动条的高度
```html ```html
<p>Name: <input type="text" data-help="Your full name"></p> <p>Name: <input type="text" data-help="Your full name"></p>
<p>Age: <input type="text" data-help="Age in years"></p> <p>Age: <input type="text" data-help="Your age in years"></p>
<p id="help"></p> <p id="help"></p>
<script> <script>
var help = document.querySelector("#help"); let help = document.querySelector("#help");
var fields = document.querySelectorAll("input"); let fields = document.querySelectorAll("input");
for (var i = 0; i < fields.length; i++) { for (let field of Array.from(fields)) {
fields[i].addEventListener("focus", function(event) { field.addEventListener("focus", event => {
var text = event.target.getAttribute("data-help"); let text = event.target.getAttribute("data-help");
help.textContent = text; help.textContent = text;
}); });
fields[i].addEventListener("blur", function(event) { field.addEventListener("blur", event => {
help.textContent = ""; help.textContent = "";
}); });
} }
@ -364,62 +383,53 @@ innerHeight全局变量是窗口高度我们必须要减去滚动条的高度
当用户从浏览器标签或窗口移开时窗口对象会收到focus事件当移动到标签或窗口上时则收到blur事件。 当用户从浏览器标签或窗口移开时窗口对象会收到focus事件当移动到标签或窗口上时则收到blur事件。
### 14.11 加载事件 ## 加载事件
当界面结束装载时会触发窗口对象和文档body对象的“load”事件。该事件通常用于在当整个文档构建完成时进行初始化。请记住&lt;script&gt;标签的内容是一遇到就执行的。很多时候这样执行得太早,比如有时脚本需要处理在&lt;script&gt;标签后出现的内容。 当界面结束装载时会触发窗口对象和文档body对象的“load”事件。该事件通常用于在当整个文档构建完成时进行初始化。请记住<script><script>
诸如image或script这类会装载外部文件的标签都有load事件指示其引用文件装载完毕。类似于焦点事件装载事件是不会传播的。 诸如image或script这类会装载外部文件的标签都有load事件指示其引用文件装载完毕。类似于焦点事件装载事件是不会传播的。
当页面关闭或跳转比如跳转到一个链接会触发beforeunload事件。该事件用于防止用户突然关闭文档而丢失工作结果。你无法使用preventDefault方法阻止页面卸载。该处理器通过返回字符串来完成该功能。该字符串会用在询问用户是否关闭页面的对话框中。该机制确保用户可以离开页面,即使是那些想要保持页面,让用户看广告的恶意脚本也无能为力 当页面关闭或跳转比如跳转到一个链接会触发beforeunload事件。该事件用于防止用户突然关闭文档而丢失工作结果。你无法使用preventDefault方法阻止页面卸载。它通过从处理器返回非空值来完成。当你这样做时,浏览器会通过显示一个对话框,询问用户是否关闭页面的对话框中。该机制确保用户可以离开,即使在那些想要留住用户,强制用户看广告的恶意页面上,也是这样
### 14.12 脚本执行时间线 ## 事件和事件循环
有许多事件会触发脚本开始执行,读取&lt;script&gt;标签就是其中一个事件触发是另一种。第13章讨论了requestAnimationFrame函数该函数用于在下一次页面重绘之前调用某个函数。这是启动脚本运行的另一方法 在事件循环的上下文中,如第 11 章中所述,浏览器事件处理器的行为,类似于其他异步通知。 它们是在事件发生时调度的,但在它们有机会运行之前,必须等待其他正在运行的脚本完成
需要重点理解的是即使任何时候都可以触发事件,但同一文档中无法同时执行两个脚本。若一个脚本已经在运行,事件处理器和使用其他方法调度的代码会使该脚本等待执行。这就是长时间执行脚本时文档无法响应的原因。此时浏览器无法回应点击或其他文档内事件,因为其无法在当前脚本完成运行之前去执行时间处理器 仅当没有别的事情正在运行时,才能处理事件,这个事实意味着,如果事件循环与其他工作捆绑在一起,任何页面交互(通过事件发生)都将延迟,直到有时间处理它为止。 因此,如果您安排了太多工作,无论是长时间运行的事件处理器还是大量短时间运行的工作,该页面都会变得缓慢且麻烦
许多程序设计环境都支持在同一时间执行多线程。同时执行许多任务可以使得程序运行更快。但当多个线程同一时间访问系统中同一部分时,构思一个程序就比原来难很多了 如果您想在背后做一些耗时的事情而不会冻结页面,浏览器会提供一些名为 Web Worker 的东西。 Web Worker 是一个 JavaScript 过程,与主脚本一起在自己的时间线上运行
JavaScript程序同时只能做一件事情让事情变得更简单。若读者想将耗时操作放到后台进行同时防止页面无响应可以使用浏览器提供的Web Worker。这个工作单元是独立的JavaScript环境可以独立于文档主程序运行而且只能和其发送接收消息。 想象一下,计算一个数字的平方运算是一个重量级的,长期运行的计算,我们希望在一个单独的线程中执行。 我们可以编写一个名为`code/squareworker.js`的文件,通过计算平方并发回消息来响应消息:
假设我们有以下代码名为code/squareworker.js。
```js ```js
addEventListener("message", function(event) { addEventListener("message", event => {
postMessage(event.data * event.data); postMessage(event.data * event.data);
}); });
``` ```
想象一下,计算数字平方的任务是艰巨耗时的计算任务,我们想要将其放在后台线程执行。该代码会产生一个工作单元,相继发送一些消息,并输出响应。 为了避免多线程触及相同数据的问题Web Worker 不会将其全局作用域或任何其他数据与主脚本的环境共享。 相反,你必须通过来回发送消息与他们沟通。
此代码会生成一个运行该脚本的 Web Worker向其发送几条消息并输出响应。
```js ```js
var squareWorker = new Worker("code/squareworker.js"); let squareWorker = new Worker("code/squareworker.js");
squareWorker.addEventListener("message", function(event) { squareWorker.addEventListener("message", event => {
console.log("The worker responded:", event.data); console.log("The worker responded:", event.data);
}); });
squareWorker.postMessage(10); squareWorker.postMessage(10);
squareWorker.postMessage(24); squareWorker.postMessage(24);
``` ```
函数postMessage会发送一条消息触发接收方的message事件。创建工作单元的脚本通过Worker对象收发消息而worker则直接向其全局作用域(新的全局作用域,与初始脚本不同)发送消息,或监听其消息。 函数postMessage会发送一条消息触发接收方的message事件。创建工作单元的脚本通过Worker对象收发消息而worker则直接向其全局作用域发送消息或监听其消息。只有可以表示为 JSON 的值可以作为消息发送 - 另一方将接收它们的副本,而不是值本身。
### 14.13 设置定时器 ## 定时器
函数setTimeout类似于requestAnimationFrame。该函数随后会调用另一个函数。但不是下次重绘时而是在等待指定毫秒数之后调用函数。该页面会在两秒之后从蓝色变为黄色。 我们在第 11 章中看到了`setTimeout`函数。 它会在给定的毫秒数之后,调度另一个函数在稍后调用。
```html
<script>
document.body.style.background = "blue";
setTimeout(function() {
document.body.style.background = "yellow";
}, 2000);
</script>
```
有时读者需要取消调度的函数。可以存储setTimeout的返回值并将作为参数调用clearTimeout。 有时读者需要取消调度的函数。可以存储setTimeout的返回值并将作为参数调用clearTimeout。
```html ```html
var bombTimer = setTimeout(function() { let bombTimer = setTimeout(() => {
console.log("BOOM!"); console.log("BOOM!");
}, 500); }, 500);
@ -434,8 +444,8 @@ if (Math.random() < 0.5) { // 50% chance
还有setInterval和clearInterval这种相似的函数用于设置计时器每隔一定毫秒数重复执行一次。 还有setInterval和clearInterval这种相似的函数用于设置计时器每隔一定毫秒数重复执行一次。
```html ```html
var ticks = 0; let ticks = 0;
var clock = setInterval(function() { let clock = setInterval(() => {
console.log("tick", ticks++); console.log("tick", ticks++);
if (ticks == 10) { if (ticks == 10) {
clearInterval(clock); clearInterval(clock);
@ -444,24 +454,22 @@ var clock = setInterval(function() {
}, 200); }, 200);
``` ```
### 14.14 降频 ## 降频
某些类型的事件可能会连续、迅速触发多次例如mousemove和scroll事件。处理这类事件时你必须小心谨慎防止处理任务耗时过长否则处理器会占据过多事件导致用户与文档交互变得非常慢且卡顿 某些类型的事件可能会连续、迅速触发多次例如mousemove和scroll事件。处理这类事件时你必须小心谨慎防止处理任务耗时过长否则处理器会占据过多事件导致用户与文档交互变得非常慢。
若你需要在这类处理器中编写一些重要任务可以使用setTimeout来确保不会频繁进行这些任务。我们通常称之为“事件降频Debounce”。有许多方法可以完成该任务。 若你需要在这类处理器中编写一些重要任务可以使用setTimeout来确保不会频繁进行这些任务。我们通常称之为“事件降频Debounce”。有许多方法可以完成该任务。
在第一个示例中,当用户输入某些字符时,我们想要完成某些任务,但我们不想在每个按键事件中立即处理该任务。当用户输入过快时,我们希望暂停一下然后进行处理。我们不是立即在事件处理器中执行动作,而是设置一个定时器。我们也会清除上一次的定时器(如果有),因此当两个事件触发间隔过短(比定时器延时短),就会取消上一次事件设置的定时器。 在第一个示例中,当用户输入某些字符时,我们想要有所反应,但我们不想在每个按键事件中立即处理该任务。当用户输入过快时,我们希望暂停一下然后进行处理。我们不是立即在事件处理器中执行动作,而是设置一个定时器。我们也会清除上一次的定时器(如果有),因此当两个事件触发间隔过短(比定时器延时短),就会取消上一次事件设置的定时器。
```html ```html
<textarea>Type something here...</textarea> <textarea>Type something here...</textarea>
<script> <script>
var textarea = document.querySelector("textarea"); let textarea = document.querySelector("textarea");
var timeout; let timeout;
textarea.addEventListener("keydown", function() { textarea.addEventListener("input", () => {
clearTimeout(timeout); clearTimeout(timeout);
timeout = setTimeout(function() { timeout = setTimeout(() => console.log("Typed!"), 500);
console.log("You stopped typing.");
}, 500);
}); });
</script> </script>
``` ```
@ -472,64 +480,58 @@ var clock = setInterval(function() {
```html ```html
<script> <script>
function displayCoords(event) { let scheduled = null;
document.body.textContent = window.addEventListener("mousemove", event => {
"Mouse at " + event.pageX + ", " + event.pageY;
}
var scheduled = false, lastEvent;
addEventListener("mousemove", function(event) {
lastEvent = event;
if (!scheduled) { if (!scheduled) {
scheduled = true; setTimeout(() => {
setTimeout(function() { document.body.textContent =
scheduled = false; `Mouse at ${scheduled.pageX}, ${scheduled.pageY}`;
displayCoords(lastEvent); scheduled = null;
}, 250); }, 250);
} }
scheduled = event;
}); });
</script> </script>
``` ```
### 14.15 本章小结 ## 本章小结
事件处理器可以检测并响应事件,而不需要直接控制事件。addEventListener方法用于注册处理器。 事件处理器可以检测并响应发生在我们的 Web 页面上的事件。addEventListener方法用于注册处理器。
每个事件都有标识事件的类型keydown、focus等。大多数方法都会在特定DOM元素上调用接着向其父代节点传播允许每个父代元素的处理器都能处理这些事件。 每个事件都有标识事件的类型keydown、focus等。大多数方法都会在特定DOM元素上调用接着向其父代节点传播允许每个父代元素的处理器都能处理这些事件。
JavaScript调用事件处理器时会传递一个包含事件额外信息的事件对象。该对象也有方法支持停止进一步传播stopPropagation也支持阻止浏览器执行事件的默认处理器preventDefault JavaScript调用事件处理器时会传递一个包含事件额外信息的事件对象。该对象也有方法支持停止进一步传播stopPropagation也支持阻止浏览器执行事件的默认处理器preventDefault
按下键盘按键时会触发keydown、keypress和keyup事件。按下鼠标按钮时会触发mousedown、mouseup和click事件。移动鼠标会触发mousemove事件也可能触发mouseenter和mouseout事件。 按下键盘按键时会触发keydown和keyup事件。按下鼠标按钮时会触发mousedown、mouseup和click事件。移动鼠标会触发mousemove事件。触摸屏交互会导致`"touchstart"``"touchmove"``"touchend"`事件。
我们可以通过scroll事件监测滚动行为可以通过focus和blur事件监控焦点改变。当文档完成加载后会触发窗口的load事件。 我们可以通过scroll事件监测滚动行为可以通过focus和blur事件监控焦点改变。当文档完成加载后会触发窗口的load事件。
同时只能执行一段JavaScript程序。因此事件处理器和其他调度脚本需要等待脚本结束才能得到机会执行。 ## 习题
### 14.16 习题 ### 气球
#### 14.16.1 少了几个按键的键盘 编写一个显示气球的页面(使用气球 emoji`\ud83c\udf88`)。 当你按下上箭头时它应该变大膨胀10%而当你按下下箭头时它应该缩小放气10%。
在1928年和1933年之间土耳其法律禁止在官方文档中使用Q、W和X。这是用于扼杀库尔德的文化大量措施中的一部分。这些字母在库尔德人中使用但伊斯坦布尔的土耳其人不使用 您可以通过在其父元素上设置`font-size` CSS 属性(`style.fontSize`来控制文本大小emoji 是文本)。 请记住在该值中包含一个单位,例如像素(`10px`
作为一个练习,通过技术来实现这种阉割键盘按键的功能,编写一个文本域(使用&lt;input type="text"标签),使得用户无法输入这些字母 箭头键的键名是`"ArrowUp"``"ArrowDown"`。确保按键只更改气球,而不滚动页面
(不用处理复制粘贴,以及其他可能的漏洞。) 实现了之后,添加一个功能,如果你将气球吹过一定的尺寸,它就会爆炸。 在这种情况下,爆炸意味着将其替换为“爆炸 emoji`\ud83d\udca5`”,并且移除事件处理器(以便您不能使爆炸变大变小)。
```html ```html
<input type="text"> <p>&#x1f4a5;</p>
<script> <script>
var field = document.querySelector("input"); // Your code here
// Your code here.
</script> </script>
``` ```
#### 14.16.2 鼠标轨迹 ### 鼠标轨迹
在JavaScript早期有许多主页都会在页面上使用大量的动画人们想出了许多该语言的创造性用法。 在JavaScript早期有许多主页都会在页面上使用大量的动画人们想出了许多该语言的创造性用法。
其中一种用途是“鼠标踪迹”,也就是沿着鼠标指针在页面上滑动的轨迹画出一连串图像 其中一种是“鼠标踪迹”,也就是一系列的元素,随着你在页面上移动鼠标,它会跟着你的鼠标指针
在本习题中实现鼠标轨迹的功能。使用绝对定位、固定尺寸的&lt;div&gt;元素,背景为黑色(请参考鼠标点击一节中的示例)。创建一系列此类元素,当鼠标移动时,伴随鼠标指针显示它们。 在本习题中实现鼠标轨迹的功能。使用绝对定位、固定尺寸的<div>元素,背景为黑色(请参考鼠标点击一节中的示例)。创建一系列此类元素,当鼠标移动时,伴随鼠标指针显示它们。
有许多方案可以实现我们所需的功能。你可以根据你的需要实现简单的或复杂的方法。简单的解决方案是保存固定鼠标的轨迹元素并循环使用它们每次mousemove事件触发时将下一个元素移动到鼠标当前位置。 有许多方案可以实现我们所需的功能。你可以根据你的需要实现简单的或复杂的方法。简单的解决方案是保存固定鼠标的轨迹元素并循环使用它们每次mousemove事件触发时将下一个元素移动到鼠标当前位置。
@ -551,24 +553,24 @@ JavaScript调用事件处理器时会传递一个包含事件额外信息的
</script> </script>
``` ```
#### 14.16.3 选项卡界面 ### 选项卡
选项卡界面是常见的设计模型。选项卡支持用户通过选择一系列元素上突出的标签来选择一个面板。 选项卡面板广泛用于用户界面。它支持用户通过选择元素上方的很多突出的选项卡来选择一个面板。
本习题要求实现一个简单的选项卡界面。编写asTabs函数接受一个DOM节点并创建选项卡界面来展现该节点的子元素。该函数应该在顶层节点中插入大量&lt;button&gt;元素与每个孩子元素一一对应按钮文本从孩子的data-tabname中获取。除了显示一个初始孩子节点其他孩子节点都应该隐藏将display样式设置成none并通过点击按钮来选择当前显示的节点。 本习题中,你必须实现一个简单的选项卡界面。编写asTabs函数接受一个DOM节点并创建选项卡界面来展现该节点的子元素。该函数应该在顶层节点中插入大量<button>元素与每个孩子元素一一对应按钮文本从孩子的data-tabname中获取。除了显示一个初始孩子节点其他孩子节点都应该隐藏将display样式设置成none并通过点击按钮来选择当前显示的节点。
该函数工作时,扩展该函数,支持在当前激活按钮上使用不同的样式 它生效时将其扩展,为当前选中的选项卡,将按钮的样式设为不同的,以便明确选择了哪个选项卡
```html ```html
<div id="wrapper"> <tab-panel>
<div data-tabname="one">Tab one</div> <div data-tabname="one">Tab one</div>
<div data-tabname="two">Tab two</div> <div data-tabname="two">Tab two</div>
<div data-tabname="three">Tab three</div> <div data-tabname="three">Tab three</div>
</div> </tab-panel>
<script> <script>
function asTabs(node) { function asTabs(node) {
// Your code here. // Your code here.
} }
asTabs(document.querySelector("#wrapper")); asTabs(document.querySelector("tab-panel"));
</script> </script>
``` ```