JavaScript 30

今天的练习是关于 JavaScript 的事件流,下面做一些扩展与整理。

事件流

事件流描述的是从页面中接收事件的顺序。IE 和 Netscape 团队提出了两种相反的事件流概念。IE 的事件流是事件冒泡流,而 Netscape 的事件流是事件捕获流。

事件冒泡

事件冒泡,即事件开始时由最具体的元素接收,然后逐层向上传播到较为不具体的节点。例如下面的代码

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html lang="en">
<head>
<title>Title</title>
</head>
<body>
<div>Click Me!</div>
</body>
</html>

如果单击页面中的 <div> 元素,那么这个 click 事件会按照如下顺序传播:

  1. <div>
  2. <body>
  3. <html>
  4. document

事件捕获

事件捕获的思想是不太具体的节点应该更早的接收到事件,而具体的节点应该最后接收到事件。使用上文提到的例子,点击 <div> 元素时,事件的传播顺序就会变成

  1. document
  2. <html>
  3. <body>
  4. <div>

DOM 事件流

“DOM2 级事件” 规定了事件流包括三个阶段

  1. 捕获阶段 CAPTURING_PHASE
  2. 目标阶段 AT_TARGET
  3. 冒泡阶段 BUBBLING_PHASE

我们可以通过事件对象的 eventPhase 属性,得知事件处于哪个阶段。

DOM 事件在传播时,会从根节点开始往下传递到 target,若注册了事件监听,则监听器处于捕获阶段,为截获事件提供了机会。

target 就是触发事件的具体对象,这时注册在 target 上的事件监听器处于目标阶段。

最后,事件再往上从 target 一路逆向传递到根节点,若注册了事件监听器,则监听器处于冒泡阶段,可以在这个阶段对事件作出响应。

以前面的 HTML 实例代码为例,单击 <div> 元素完整的事件流如下如所示

图片来源于《JavaScript 高级程序设计》

在 DOM 事件流中,实际的目标 <div> 在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从 document 到 <html> 再到 <body> 后就停止了。下一个阶段是处于目标阶段,于是事件在 <div> 上发生,并在事件处理中被看成冒泡阶段的一部分。然后冒泡阶段发生,事件又传回了 document。

事件处理

在今天的练习中,通过 Event​Target​.add​Event​Listener() 来添加事件监听。

语法

target.addEventListener(type, listener[, options]);

target.addEventListener(type, listener[, useCapture]);

方法接收三个参数

type

表示需要监听的事件类型

listener

当所监听的事件类型触发时,接到一个事件通知对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数。

options 可选

  1. capture 默认为 false,即事件只会在冒泡阶段才会被执行。若为 true,即事件在捕获阶段就会被执行
  2. once 表示事件监听被添加后之后执行一次,默认为 false。如果被设置为 true,listener 在被调用后就会被移除。

useCapture 可选

默认为 false,表示注册事件是冒泡事件。若为 true,则表示注册事件为捕获事件。

1
2
3
4
5
6
7
8
9
10
11
12
one.addEventListener('click', (e) => { 
console.log("one capture mode", e.eventPhase);
}, true);
one.addEventListener('click', (e) => {
console.log("one bubble mode", e.eventPhase);
}, false);
two.addEventListener('click', (e) => {
console.log("two capture mode 2");
}, true)
two.addEventListener('click', (e) => { console.log("two bubble mode", e.eventPhase); }, false);
three.addEventListener('click', (e) => { console.log("three capture mode", e.eventPhase); }, true);
three.addEventListener('click', (e) => { console.log("three bubble mode", e.eventPhase); }, false);

打印结果为

one capture mode 1

two capture mode 1

three capture mode 2

three bubble mode 2

tow bubble mode 3

one bubble mode 3

且打印顺序不和事件注册顺序有关。

阻止事件冒泡

1
2
3
4
two.addEventListener('click', (e) => { 
console.log("two capture mode", e.eventPhase);
e.stopPropagation();
}, true);

通过 stopPropagation() 可以阻止事件的冒泡,也阻止了事件的继续捕获。但无法阻止同一个元素其他绑定事件的执行。

使用 stopImmediatePropagation() 即阻止了事件的继续传递,也阻止了同一个元素的其他绑定事件的执行。

今天的练习是使用摄像头,实时记录,并通过 canvas 实时绘制到画布中。

通过 Media​Devices​.get​User​Media() 方法,在通过授权之后,可以获取摄像头图像。具体代码如下

1
2
3
4
5
6
7
8
9
10
11
navigator.mediaDevices.getUserMedia({ video: true, audio: false})
.then((videostream) => {
console.log(videostream);
video.srcObject = videostream;
video.onloadedmetadata = function() {
video.play();
};
})
.catch((error) => {
console.error('OH, Don\'t have permission to use your local cam!', error);
});

阅读更多

今天的练习是对数组的排序练习,特别的是需要去除字符串开头的 a an the 之后进行排序。

去除 a an the 的操作放到了一个 function 里,代码如下

1
2
3
function strip(str) {
return str.replace(/^(a |an |the )/i, '').trim();
}

String.prototype.replace() 接收两个参数,第一个参数可以是一个 正则表达式 或者 待替换的子字符串,第二个参数为 新字符串 或者 函数,返回替换后的新的字符串。

通过 Array.prototype.sort() 排序后,通过 map 方法,转换为 HTML 代码字符串,插入 HTML 代码中。

GitHub 地址

今天的练习实现了一个鼠标移动修改文字阴影的效果。通过添加 'mousemove' 监听鼠标移动事件,计算鼠标移动的距离与阴影偏移的关系,修改文字阴影样式即可。

阅读更多

今天的练习是来了解一下 JavaScript 中关于引用与拷贝的问题。

值类型

在 JavaScript 中,string/number/boolean/null/undefined 等基本类型是值类型。例如下面的例子

1
2
3
4
5
6
7
let age = 100;
let age2 = age;
console.log(age, age2);
// 100 100
age = 200;
console.log(age, age2);
// 200 100

阅读更多


day-13


第十三天的练习是实现页面滚动过程中,滑动到图片位置时,图片从左右伴随动画进入。

Preview

Key Point

  • 监听滚动事件
  • 计算图片出现的位置,添加动画

阅读更多

第十二天实现了一个键盘事件监听,当输入了特定字符串后,在页面上添加一些特殊效果。

Key Point

  • 全局绑定 'keyup' 事件,通过 e.key 获取用户输入
  • 截取与 secretCode 相同长度的用户输入字符串,比较两者,相同出发添加效果

阅读更多

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×