From 9e1d4bc8546e48babed59654ead7a4ec6e0df10b Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 18 Jul 2020 15:08:51 +0800 Subject: [PATCH 1/2] update some chacters --- .../01-browser-environment/article.md | 10 +-- .../07-modifying-document/article.md | 37 ++++++--- 2-ui/1-document/09-size-and-scroll/article.md | 2 +- 2-ui/1-document/11-coordinates/article.md | 2 + .../01-introduction-browser-events/article.md | 75 +++++++++---------- 2-ui/2-events/03-event-delegation/article.md | 4 +- 6 files changed, 71 insertions(+), 59 deletions(-) diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index 69689d72d3..98ac34810d 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -2,7 +2,7 @@ JavaScript 语言最初是为 Web 浏览器创建的。此后,它已经发展成为一种具有多种用途和平台的语言。 -平台可以是一个浏览器,一个 Web 服务器,或其他 **主机(host)**,甚至是咖啡机。它们每个都提供了特定于平台的功能。JavaScript 规范将其称为 **主机环境**。 +平台可以是一个浏览器,一个 Web 服务器,或其他 **主机(host)**,甚至可以是一个“智能”咖啡机,如果它能运行 JavaScript 的话。它们每个都提供了特定于平台的功能。JavaScript 规范将其称为 **主机环境**。 主机环境提供了自己的对象和语言核心以外的函数。Web 浏览器提供了一种控制网页的方法。Node.JS 提供了服务器端功能,等等。 @@ -49,9 +49,7 @@ document.body.style.background = "red"; setTimeout(() => document.body.style.background = "", 1000); ``` -在这里,我们使用了 `document.body.style`,但还有很多很多其他的东西。规范中有属性和方法的详细描述: - -- **DOM Living Standard**: +在这里,我们使用了 `document.body.style`,但还有很多很多其他的东西。规范中有属性和方法的详细描述:[DOM Living Standard](https://dom.spec.whatwg.org)。 ```smart header="DOM 不仅仅用于浏览器" DOM 规范解释了文档的结构,并提供了操作文档的对象。有的非浏览器设备也使用 DOM。 @@ -60,9 +58,9 @@ DOM 规范解释了文档的结构,并提供了操作文档的对象。有的 ``` ```smart header="用于样式的 CSSOM" -CSS 规则和样式表的结构与 HTML 不同。有一个单独的规范 [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/),它解释了如何将 CSS 表示为对象,以及如何读写它们。 +There's also a separate specification, [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/) for CSS rules and stylesheets, that explains how they are represented as objects, and how to read and write them. -当我们修改文档的样式规则时,CSSOM 与 DOM 是一起使用的。但实际上,很少需要 CSSOM,因为通常 CSS 规则是静态的。我们很少需要从 JavaScript 中添加/删除 CSS 规则,但你要知道这是可行的。 +当我们修改文档的样式规则时,CSSOM 与 DOM 是一起使用的。但实际上,很少需要 CSSOM,因为 we rarely need to modify CSS rules from JavaScript (usually we just add/remove CSS classes, not modify their CSS rules), but that's also possible. ``` ## 浏览器对象模型(BOM) diff --git a/2-ui/1-document/07-modifying-document/article.md b/2-ui/1-document/07-modifying-document/article.md index 81848b2134..4560b59da2 100644 --- a/2-ui/1-document/07-modifying-document/article.md +++ b/2-ui/1-document/07-modifying-document/article.md @@ -28,7 +28,7 @@ DOM 修改是创建“实时”页面的关键。 */!* ``` -这是一个 HTML 示例。现在,让我们使用 JavaScript 创建一个相同的 `div`(假设样式在 HTML 或外部 CSS 文件中)。 +这是一个 HTML 示例。现在,让我们使用 JavaScript 创建一个相同的 `div`(假设样式已经在 HTML/CSS 文件中)。 ## 创建一个元素 @@ -48,21 +48,28 @@ DOM 修改是创建“实时”页面的关键。 let textNode = document.createTextNode('Here I am'); ``` +Most of the time we need to create element nodes, such as the `div` for the message. + ### 创建一条消息 -在我们的例子中,消息是一个带有 `alert` 类和 HTML 的 `div`: +Creating the message div takes 3 steps: ```js +// 1. Create
element let div = document.createElement('div'); + +// 2. Set its class to "alert" div.className = "alert"; + +// 3. Fill it with the content div.innerHTML = "Hi there! You've read an important message."; ``` -我们创建了元素,但到目前为止,它还只是在变量中。我们无法在页面上看到该元素,因为它还不是文档的一部分。 +我们已经创建了该元素。但到目前为止,它还只是在一个名为 `div` 的变量中,尚未在页面中。所以我们无法在页面上看到它。 ## 插入方法 -为了让 `div` 显示出来,我们需要将其插入到 `document` 中的某处。例如,在 `document.body` 中。 +为了让 `div` 显示出来,我们需要将其插入到 `document` 中的某处。例如,into `` element, referenced by `document.body`. 对此有一个特殊的方法 `append`:`document.body.append(div)`。 @@ -90,14 +97,20 @@ div.innerHTML = "Hi there! You've read an important message."; ``` -下面这些方法提供了更多的插入方式: +Here we called `append` on `document.body`, but we can call `append` method on any other element, to put another element into it. For instance, we can append something to `
` by calling `div.append(anotherElement)`. -- `node.append(...nodes or strings)` — 在 `node` 末尾插入节点或字符串, -- `node.prepend(...nodes or strings)` — 在 `node` 开头插入节点或字符串, -- `node.before(...nodes or strings)` — 在 `node` 前面插入节点或字符串, -- `node.after(...nodes or strings)` — 在 `node` 后面插入节点或字符串, +Here are more insertion methods, they specify different places where to insert: + +- `node.append(...nodes or strings)` — 在 `node` **末尾** 插入节点或字符串, +- `node.prepend(...nodes or strings)` — 在 `node` **开头** 插入节点或字符串, +- `node.before(...nodes or strings)` — 在 `node` **前面** 插入节点或字符串, +- `node.after(...nodes or strings)` — 在 `node` **后面** 插入节点或字符串, - `node.replaceWith(...nodes or strings)` — 将 `node` 替换为给定的节点或字符串。 +Arguments of these methods are an arbitrary list of DOM nodes to insert, or text strings (that become text nodes automatically). + +Let's see them in action. + 下面是使用这些方法将列表项添加到列表中,以及将文本添加到列表前面和后面的示例: ```html autorun @@ -139,7 +152,7 @@ before after ``` -这些方法可以在单个调用中插入多个节点列表和文本片段。 +如上所述,这些方法可以在单个调用中插入多个节点列表和文本片段。 例如,在这里插入了一个字符串和一个元素: @@ -150,7 +163,7 @@ after ``` -所有内容都被“作为文本”插入。 +Please note: the text is inserted "as text", not "as HTML", with proper escaping of characters such as `<`, `>`. 所以,最终的 HTML 为: @@ -166,7 +179,7 @@ after 所以,这些方法只能用来插入 DOM 节点或文本片段。 -但是,如果我们想在所有标签和内容正常工作的情况下,将这些内容“作为 HTML” 插入到 HTML 中,就像 `elem.innerHTML` 方法一样,那有什么方法可以实现吗? +But what if we'd like to insert an HTML string "as html", with all tags and stuff working, in the same manner as `elem.innerHTML` does it? ## insertAdjacentHTML/Text/Element diff --git a/2-ui/1-document/09-size-and-scroll/article.md b/2-ui/1-document/09-size-and-scroll/article.md index 6ee93a020d..84c659120b 100644 --- a/2-ui/1-document/09-size-and-scroll/article.md +++ b/2-ui/1-document/09-size-and-scroll/article.md @@ -211,7 +211,7 @@ element.style.height = `${element.scrollHeight}px`;
Click
Me
1
2
3
4
5
6
7
8
9
``` -将 `scrollTop` 设置为 `0` 或 `Infinity` 将会使元素滚动到顶部/底部。 +将 `scrollTop` 设置为 `0` 或一个大的值,例如 `1e9`,将会使元素滚动到顶部/底部。 ```` ## 不要从 CSS 中获取 width/height diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md index 40c42a042f..b3953016e7 100644 --- a/2-ui/1-document/11-coordinates/article.md +++ b/2-ui/1-document/11-coordinates/article.md @@ -216,6 +216,8 @@ function getCoords(elem) { return { top: box.top + window.pageYOffset, + right: box.right + window.pageXOffset, + bottom: box.bottom + window.pageYOffset, left: box.left + window.pageXOffset }; } diff --git a/2-ui/2-events/01-introduction-browser-events/article.md b/2-ui/2-events/01-introduction-browser-events/article.md index d5ac61a16b..29e5dfc5e7 100644 --- a/2-ui/2-events/01-introduction-browser-events/article.md +++ b/2-ui/2-events/01-introduction-browser-events/article.md @@ -11,13 +11,13 @@ - `mousedown` / `mouseup` —— 当在元素上按下/释放鼠标按钮时。 - `mousemove` —— 当鼠标移动时。 +**键盘事件**: +- `keydown` and `keyup` -- when a keyboard key is pressed and released. + **表单(form)元素事件**: - `submit` —— 当访问者提交了一个 `
` 时。 - `focus` —— 当访问者聚焦于一个元素时,例如聚焦于一个 ``。 -**键盘事件**: -- `keydown` 和 `keyup` —— 当访问者按下然后松开按键时。 - **Document 事件**: - `DOMContentLoaded` —— 当 HTML 的加载和处理均完成,DOM 被完全构建完成时。 @@ -87,8 +87,6 @@ HTML 特性不是编写大量代码的好位置,因此我们最好创建一个 因此,这种方法实际上与前一种方法相同。 -**处理程序总是在 DOM 属性中:HTML 特性只是初始化它的方法之一**。 - 这两段代码工作相同: 1. 只有 HTML: @@ -109,6 +107,8 @@ HTML 特性不是编写大量代码的好位置,因此我们最好创建一个 ``` +In the first example, the HTML attribute is used to initialize the `button.onclick`, while in the second example -- the script, that's all the difference. + **因为这里只有一个 `onclick` 属性,所以我们无法分配更多事件处理程序。** 在下面这个示例中,我们使用 JavaScript 添加了一个处理程序,覆盖了现有的处理程序: @@ -124,16 +124,6 @@ HTML 特性不是编写大量代码的好位置,因此我们最好创建一个 ``` -顺便说一下,我们可以直接将现有的函数指定为处理程序: - -```js -function sayThanks() { - alert('Thanks!'); -} - -elem.onclick = sayThanks; -``` - 要移除一个处理程序 —— 赋值 `elem.onclick = null`。 ## 访问元素:this @@ -150,7 +140,17 @@ elem.onclick = sayThanks; 如果你刚开始写事件 —— 请注意一些细微之处。 -**函数应该被以 `sayThanks` 的形式进行非配,而不是 `sayThanks()`**。 +We can set an existing function as a handler: + +```js +function sayThanks() { + alert('Thanks!'); +} + +elem.onclick = sayThanks; +``` + +But be careful: the function should be assigned as `sayThanks`, not `sayThanks()`. ```js // 正确 @@ -160,7 +160,7 @@ button.onclick = sayThanks; button.onclick = sayThanks(); ``` -如果我们添加了括号 `sayThanks()` —— 这是一个函数调用。所以,最后一行代码实际上获得的是函数执行的 **结果**,即 `undefined`(因为这个函数没有返回值)。此代码不会工作。 +如果我们添加了括号,那么 `sayThanks()` 就变成了一个函数调用。所以,最后一行代码实际上获得的是函数执行的 **结果**,即 `undefined`(因为这个函数没有返回值)。此代码不会工作。 ……但在标记(markup)中,我们确实需要括号: @@ -168,21 +168,17 @@ button.onclick = sayThanks(); ``` -这个区别很容易解释。当浏览器读取 HTML 特性(attribute)时,浏览器将会使用 **特性中的内容** 创建一个处理程序:`sayThanks()`。 +这个区别很容易解释。当浏览器读取 HTML 特性(attribute)时,浏览器将会使用 **特性中的内容** 创建一个处理程序。 所以,标记(markup)会生成下面这个属性: ```js button.onclick = function() { *!* - sayThanks(); // 特性中的内容 + sayThanks(); // <-- 特性(attribute)中的内容变到了这里 */!* }; ``` -**使用函数,而不是字符串。** - -`elem.onclick = "alert(1)"` 也可以执行。它能执行是出于兼容性,但强烈建议不要使用这种方式。 - **不要对处理程序使用 `setAttribute`。** 这样的调用会失效: @@ -201,7 +197,7 @@ document.body.setAttribute('onclick', function() { alert(1) }); 上述分配处理程序的方式的根本问题是 —— 我们不能为一个事件分配多个处理程序。 -例如,在我们点击了一个按钮时,我们代码中的一部分想要高亮显示这个按钮,另一部分则想要显示一条消息。 +假设,在我们点击了一个按钮时,我们代码中的一部分想要高亮显示这个按钮,另一部分则想要显示一条消息。 我们想为此事件分配两个处理程序。但是,新的 DOM 属性将覆盖现有的 DOM 属性: @@ -229,8 +225,7 @@ element.addEventListener(event, handler[, options]); : 具有以下属性的附加可选对象: - `once`:如果为 `true`,那么会在被触发后自动删除监听器。 - `capture`:事件处理的阶段,我们稍后将在 一章中介绍。由于历史原因,`options` 也可以是 `false/true`,它与 `{capture: false/true}` 相同。 - - `passive`:如果为 `true`,那么处理程序将不会 `preventDefault()`,我们稍后将在 一章中介绍。 - + - `passive`:如果为 `true`,那么处理程序将不会调用 `preventDefault()`,我们稍后将在 一章中介绍。 要移除处理程序,可以使用 `removeEventListener`: @@ -249,7 +244,7 @@ elem.addEventListener( "click" , () => alert('Thanks!')); elem.removeEventListener( "click", () => alert('Thanks!')); ``` -处理程序不会被移除,因为 `removeEventListener` 获取了另一个函数 —— 使用相同的代码,但这并不起作用。 +处理程序不会被移除,因为 `removeEventListener` 获取了另一个函数 —— 使用相同的代码,但这并不起作用,因为它是一个不同的函数对象。 下面是正确方法: @@ -291,19 +286,21 @@ input.removeEventListener("click", handler); 正如我们在上面这个例子中所看到的,我们可以 **同时** 使用 DOM 属性和 `addEventListener` 来设置处理程序。但通常我们只使用其中一种方式。 ````warn header="对于某些事件,只能通过 `addEventListener` 设置处理程序" -有些事件无法通过 DOM 属性进行分配。必须使用 `addEventListener`。 +有些事件无法通过 DOM 属性进行分配。只能使用 `addEventListener`。 例如,`DOMContentLoaded` 事件,该事件在文档加载完成并且 DOM 构建完成时触发。 ```js +// 永远不会运行 document.onDOMContentLoaded = function() { - alert("DOM built"); // 永远不会运行 + alert("DOM built"); }; ``` ```js +// 这种方式可以运行 document.addEventListener("DOMContentLoaded", function() { - alert("DOM built"); // 这种方式可以运行 + alert("DOM built"); }); ``` 所以 `addEventListener` 更通用。虽然这样的事件是特例而不是规则。 @@ -311,11 +308,11 @@ document.addEventListener("DOMContentLoaded", function() { ## 事件对象 -为了正确处理事件,我们需要更深入地了解发生了什么。不仅仅是 "click" 或 "keypress",还包括鼠标指针的坐标是什么?按下了哪个键?等等。 +为了正确处理事件,我们需要更深入地了解发生了什么。不仅仅是 "click" 或 "keydown",还包括鼠标指针的坐标是什么?按下了哪个键?等等。 当事件发生时,浏览器会创建一个 **`event` 对象**,将详细信息放入其中,并将其作为参数传递给处理程序。 -下面是一个从 `event` 对象获取鼠标坐标的示例: +下面是一个从 `event` 对象获取鼠标指针的坐标的示例: ```html run @@ -338,11 +335,11 @@ document.addEventListener("DOMContentLoaded", function() { : 处理事件的元素。这与 `this` 相同,除非处理程序是一个箭头函数,或者它的 `this` 被绑定到了其他东西上,之后我们就可以从 `event.currentTarget` 获取元素了。 `event.clientX / event.clientY` -: 鼠标事件的指针的窗口相对坐标。 +: 指针事件(pointer event)的指针的窗口相对坐标。 -还有很多属性。它们取决于事件类型,因此,稍后我们将详细讨论不同事件,那时我们再对其进行详细研究。 +还有很多属性。其中很多都取决于事件类型:键盘事件具有一组属性,指针事件具有另一组属性,稍后我们将详细讨论不同事件,那时我们再对其进行详细研究。 -````smart header="也可以从 HTML 中访问 `event` 对象" +````smart header="`event` 对象在 HTML 处理程序中也可用" 如果我们在 HTML 中分配了一个处理程序,那么我们也可以使用 `event` 对象,像这样: ```html autorun height=60 @@ -364,15 +361,17 @@ document.addEventListener("DOMContentLoaded", function() { ``` -正如我们所看到的,当 `addEventListener` 接收一个对象作为处理程序时,在事件发生时,它就会调用 `object.handleEvent(event)` 来处理事件。 +正如我们所看到的,当 `addEventListener` 接收一个对象作为处理程序时,在事件发生时,它就会调用 `obj.handleEvent(event)` 来处理事件。 我们也可以对此使用一个类: diff --git a/2-ui/2-events/03-event-delegation/article.md b/2-ui/2-events/03-event-delegation/article.md index d9ea7cc4e5..a694af3a1a 100644 --- a/2-ui/2-events/03-event-delegation/article.md +++ b/2-ui/2-events/03-event-delegation/article.md @@ -101,8 +101,8 @@ table.onclick = function(event) { 解释: 1. `elem.closest(selector)` 方法返回与 `selector` 匹配的最近的祖先。在我们的例子中,我们从源元素开始向上寻找 ``。 -2. 如果 `event.target` 不在任何 `` 中,那么调用将返回 `null`,我们不需要做任何事情。 -3. 在嵌套的表格的情况下,`event.target` 可能是位于当前表格之外的 ``。因此我们需要检查这是否是 **我们的表格中的** ``。 +2. 如果 `event.target` 不在任何 `` 中,那么调用将立即返回,因为这里没有什么事儿可做。 +3. 对于嵌套的表格,`event.target` 可能是一个 ``,但位于当前表格之外。因此我们需要检查它是否是 **我们的表格中的** ``。 4. 如果是的话,就高亮显示它。 最终,我们得到了一个快速、高效的用于高亮显示的代码,该代码与表格中的 `` 的数量无关。 From 5e357562df848611a2cdb7553702767b27ae02ea Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 18 Jul 2020 16:58:57 +0800 Subject: [PATCH 2/2] update some chacters --- 2-ui/2-events/05-dispatch-events/article.md | 20 +- .../1-mouse-events-basics/article.md | 84 ++++--- .../1-mouse-events-basics/head.html | 2 +- .../4-mouse-drag-and-drop/article.md | 32 +-- .../ball.view/index.html | 9 +- .../ball2.view/index.html | 9 +- .../6-pointer-events/article.md | 229 ++++++++++++++++++ .../6-pointer-events/ball-2.view/index.html | 38 +++ .../6-pointer-events/ball.view/index.html | 30 +++ .../multitouch.view/index.html | 28 +++ .../6-pointer-events/slider.view/index.html | 37 +++ .../6-pointer-events/slider.view/style.css | 19 ++ .../2-check-sync-keydown/solution.md | 0 .../solution.view/index.html | 0 .../2-check-sync-keydown/task.md | 0 .../article.md | 0 .../german-layout.svg | 0 .../keyboard-dump.view/index.html | 0 .../keyboard-dump.view/script.js | 0 .../keyboard-dump.view/style.css | 0 .../us-layout.svg | 0 21 files changed, 462 insertions(+), 75 deletions(-) create mode 100644 2-ui/3-event-details/6-pointer-events/article.md create mode 100644 2-ui/3-event-details/6-pointer-events/ball-2.view/index.html create mode 100644 2-ui/3-event-details/6-pointer-events/ball.view/index.html create mode 100644 2-ui/3-event-details/6-pointer-events/multitouch.view/index.html create mode 100644 2-ui/3-event-details/6-pointer-events/slider.view/index.html create mode 100644 2-ui/3-event-details/6-pointer-events/slider.view/style.css rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/2-check-sync-keydown/solution.md (100%) rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/2-check-sync-keydown/solution.view/index.html (100%) rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/2-check-sync-keydown/task.md (100%) rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/article.md (100%) rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/german-layout.svg (100%) rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/keyboard-dump.view/index.html (100%) rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/keyboard-dump.view/script.js (100%) rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/keyboard-dump.view/style.css (100%) rename 2-ui/3-event-details/{5-keyboard-events => 7-keyboard-events}/us-layout.svg (100%) diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md index 9ec16d6650..06c85c92be 100644 --- a/2-ui/2-events/05-dispatch-events/article.md +++ b/2-ui/2-events/05-dispatch-events/article.md @@ -211,13 +211,14 @@ alert(event.clientX); // undefined,未知的属性被忽略了! ## 事件中的事件是同步的 -通常事件是被异步处理的。也就是说:如果浏览器正在处理 `onclick`,在此处理过程中发生了一个新的事件,那么它将等待,直到 `onclick` 处理完成。 +通常事件是在队列中处理的。也就是说:如果浏览器正在处理 `onclick`,这时发生了一个新的事件,例如鼠标移动了,那么它会被排入队列,相应的 `mousemove` 处理程序将在 `onclick` 事件处理完成后被调用。 -唯一的例外就是,一个事件是在另一个事件中发起的。 +值得注意的例外情况就是,一个事件是在另一个事件中发起的。例如 using `dispatchEvent`. Such events are processed immediately: the new event handlers are called, and then the current event handling is resumed. -然后,程序执行的控制流会跳转到嵌套的事件的处理程序,执行完成后返回。 +For instance, in the code below the `menu-open` event is triggered during the `onclick`. + +It's processed immediately, without waiting for `onlick` handler to end: -例如,这里的嵌套事件 `menu-open` 在 `onclick` 期间被同步处理: ```html run autorun @@ -226,7 +227,6 @@ alert(event.clientX); // undefined,未知的属性被忽略了! menu.onclick = function() { alert(1); - // alert("nested") menu.dispatchEvent(new CustomEvent("menu-open", { bubbles: true })); @@ -234,17 +234,20 @@ alert(event.clientX); // undefined,未知的属性被忽略了! alert(2); }; + // triggers between 1 and 2 document.addEventListener('menu-open', () => alert('nested')); ``` 输出顺序为:1 -> nested -> 2。 -请注意,嵌套事件 `menu-open` 会完全冒泡,并在 `document` 上被处理。嵌套事件的传播(propagation)和处理必须完全完成,然后处理过程才会返回到外部代码(`onclick`)。 +请注意,嵌套事件 `menu-open` 会在 `document` 上被捕获。嵌套事件的传播(propagation)和处理先被完成,然后处理过程才会返回到外部代码(`onclick`)。 + +这不只是与 `dispatchEvent` 有关,还有其他情况。If an event handler calls methods that trigger to other events -- they are too processed synchronously, in a nested fashion. -这不仅与 `dispatchEvent` 有关,还有其他情况。事件处理程序中的 JavaScript 可以调用会引发其他事件的方法 —— 它们也是被同步处理的。 +Let's say we don't like it. We'd want `onclick` to be fully processed first, independently from `menu-open` or any other nested events. -如果我们不喜欢它,可以将 `dispatchEvent`(或者其他触发事件的调用)放在 `onclick` 末尾,或者最好将其包装到零延迟的 `setTimeout` 中: +然后,我们可以将 `dispatchEvent`(或另一个触发事件的调用)放在 `onclick` 末尾,或者最好将其包装到零延迟的 `setTimeout` 中: ```html run @@ -253,7 +256,6 @@ alert(event.clientX); // undefined,未知的属性被忽略了! menu.onclick = function() { alert(1); - // alert(2) setTimeout(() => menu.dispatchEvent(new CustomEvent("menu-open", { bubbles: true }))); diff --git a/2-ui/3-event-details/1-mouse-events-basics/article.md b/2-ui/3-event-details/1-mouse-events-basics/article.md index 2620a955b3..ce68d892d9 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/article.md +++ b/2-ui/3-event-details/1-mouse-events-basics/article.md @@ -1,4 +1,4 @@ -# 鼠标事件基础 +# 鼠标事件 在本章中,我们将详细介绍鼠标事件及其属性。 @@ -6,11 +6,7 @@ ## 鼠标事件类型 -我们可以将鼠标事件分成两类:“简单”和“复杂”。 - -### 简单事件 - -最常用的简单事件有: +我们已经见过了其中一些事件: `mousedown/mouseup` : 在元素上点击/释放鼠标按钮。 @@ -21,26 +17,22 @@ `mousemove` : 鼠标在元素上的每个移动都会触发此事件。 -`contextmenu` -: 尝试打开上下文菜单时触发。在最常见的情况下,此事件发生在鼠标右键被按下时。虽然,还有其他打开上下文菜单的方式,例如使用特殊的键盘键,所以它不完全是一个鼠标事件。 - -……还有其他几种事件类型,我们稍后会讨论它们。 - -### 复杂事件 - `click` : 如果使用的是鼠标左键,则在同一个元素上的 `mousedown` 及 `mouseup` 相继触发后,触发该事件。 `dblclick` -: 双击一个元素后触发。 +: 在短时间内双击同一元素后触发。如今已经很少使用了。 + +`contextmenu` +: 在鼠标右键被按下时触发。还有其他打开上下文菜单的方式,例如使用特殊的键盘按键,在这种情况下它也会被触发,因此它并不完全是鼠标事件。 -复杂事件是由简单事件组成的,因此,从理论上讲,如果没有这些复杂事件,我们也能实现相应的功能。但它们的存在却给我们提供了极大的便利。 +……还有其他几种事件,我们稍后会学习它们。 -### 事件顺序 +## 事件顺序 -一个行为可能会触发多个事件。 +从上面的列表中我们可以看到,一个用户操作可能会触发多个事件。 -比如,点击鼠标按钮,在按下鼠标按钮时,点击会首先触发 `mousedown`,然后释放鼠标按钮时,会触发 `mouseup` 和 `click`。 +例如,点击鼠标左键,在鼠标左键被按下时,会首先触发 `mousedown`,然后当鼠标左键被释放时,会触发 `mouseup` 和 `click`。 在单个动作触发多个事件时,事件的顺序是固定的。也就是说,会遵循 `mousedown` -> `mouseup` -> `click` 的顺序调用处理程序。 @@ -49,26 +41,42 @@ 在测试台下面记录了所有的鼠标事件,如果它们之间的延迟时间超过 1 秒,那么它们会被水平分割线分开。 -我们还可以看出 `which` 属性允许检查鼠标按钮。 +我们还可以看出 `button` 属性允许检测鼠标按钮,演示示例如下。 ``` -## 获取按钮:which +## 鼠标按钮 + +与点击相关的事件始终具有 `button` 属性,该属性允许获取确切的鼠标按钮。 -与点击相关的事件始终具有 `which` 属性,该属性允许获取确切的鼠标按钮。 +We usually don't use it for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click. -它不用于 `click` 和 `contextmenu` 事件,因为前者只在点击左键时发生,而后者只在点击右键时发生。 +From the other hand, `mousedown` and `mouseup` handlers we may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between "right-mousedown" and "left-mousedown". -但是,如果我们跟踪 `mousedown` 和 `mouseup`,那么我们就需要它,因为这些事件会在任何按钮上触发,所以 `which` 让我们能够区分 "right-mousedown" 和 "left-mousedown"。 +The possible values of `event.button` are: -有三个可能的值: +| Button state | `event.button` | +|--------------|----------------| +| Left button (primary) | 0 | +| Middle button (auxillary) | 1 | +| Right button (secondary) | 2 | +| X1 button (back) | 3 | +| X2 button (forward) | 4 | -- `event.which == 1` —— 左按钮 -- `event.which == 2` —— 中间按钮 -- `event.which == 3` —— 右按钮 +Most mouse devices only have the left and right buttons, so possible values are `0` or `2`. Touch devices also generate similar events when one taps on them. -中间按钮现在有些特殊,很少被使用了。 +Also there's `event.buttons` property that has all currently pressed buttons as an integer, one bit per button. In practice this property is very rarely used, you can find details at [MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons) if you ever need it. + +```warn header="The outdated `event.which`" +Old code may use `event.which` property that's an old non-standard way of getting a button, with possible values: + +- `event.which == 1` – left button, +- `event.which == 2` – middle button, +- `event.which == 3` – right button. + +As of now, `event.which` is deprecated, we shouldn't use it. +``` ## 组合键:shift,alt,ctrl,meta @@ -116,17 +124,25 @@ ``` ```warn header="还有移动设备" -键盘组合是工作流的一个补充。这样,如果访客使用键盘操作 —— 它就会起作用。而且,如果访客的设备没有键盘 —— 那么这里应该有另一种方法来做到这一点。 +键盘组合是工作流的一个补充。这样,如果访客使用键盘操作 —— 它们就会起作用。 + +但是,如果访客的设备没有键盘 —— 那么这里应该有另一种不使用键盘也能做到这一点的方式。 ``` ## 坐标:clientX/Y,pageX/Y -所有的鼠标事件都有两种形式的坐标: +所有的鼠标事件都提供了两种形式的坐标: 1. 相对于窗口的坐标:`clientX` 和 `clientY`。 2. 相对于文档的坐标:`pageX` 和 `pageY`。 -比如,如果我们有一个大小为 500x500 的窗口,并且鼠标在左上角,那么 `clientX` 和 `clientY` 均为 `0`。如果鼠标位于中间,那么 `clientX` 和 `clientY` 均为 `250`。这与它在文档中的位置无关。它们类似于 `position:fixed`。 +We already covered the difference between them in the chapter . + +In short, document-relative coordinates `pageX/Y` are counted from the left-upper corner of the document, and do not change when the page is scrolled, while `clientX/Y` are counted from the current window left-upper corner. When the page is scrolled, they change. + +例如,如果我们有一个大小为 500x500 的窗口,并且鼠标在左上角,那么 `clientX` 和 `clientY` 均为 `0`,无论页面如何滚动。 + +如果鼠标位于中间,那么 `clientX` 和 `clientY` 均为 `250`。这与它在文档中的位置无关。在这方面,它们类似于 `position:fixed`。 ````online 将鼠标移动到输入字段上,可以看到 `clientX/clientY`(此示例位于 `iframe` 中,因此坐标是相对于 `iframe` 的): @@ -136,9 +152,7 @@ ``` ```` -文档相对坐标 `pageX` 和 `pageY` 是从文档的左上角而不是窗口开始计算的。你可以在 一章中阅读有关坐标的更多细节。 - -## 禁用选择 +## 防止在鼠标按下时的选择 双击鼠标会有副作用,在某些界面中可能会出现干扰:它会选择文本。 @@ -185,7 +199,7 @@ Before... 鼠标事件有以下属性: -- 按钮:`which`。 +- 按钮:`button`。 - 组合键(如果被按下则为 `true`):`altKey`,`ctrlKey`,`shiftKey` 和 `metaKey`(Mac)。 - 如果你想处理 `key:Ctrl`,那么不要忘记 Mac 用户,他们通常使用的是 `key:Cmd`,所以最好检查 `if (e.metaKey || e.ctrlKey)`。 diff --git a/2-ui/3-event-details/1-mouse-events-basics/head.html b/2-ui/3-event-details/1-mouse-events-basics/head.html index f578fb7db0..1b9a73fca4 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/head.html +++ b/2-ui/3-event-details/1-mouse-events-basics/head.html @@ -25,7 +25,7 @@ function logMouse(e) { let evt = e.type; while (evt.length < 11) evt += ' '; - showmesg(evt + " which=" + e.which, 'test') + showmesg(evt + " button=" + e.button, 'test') return false; } diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md index 7db67458cd..cce41ed52f 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md @@ -4,9 +4,9 @@ 在现代 HTML 标准中有一个 [关于拖放的部分](https://html.spec.whatwg.org/multipage/interaction.html#dnd),其中包含了例如 `dragstart` 和 `dragend` 等特殊事件。 -这些事件很有用,因为它们使我们能够轻松地解决简单的任务。例如,使我们能够处理将“外部”文件拖放到浏览器中的操作,因此我们可以在 OS 文件管理器中获取文件,并将其拖放到浏览器窗口中,从而使 JavaScript 可以访问其内容。 +这些事件使我们能够支持特殊类型的拖放,例如处理从 OS 文件管理器中拖动文件,并将其拖放到浏览器窗口中。之后,JavaScript 便可以访问此类文件中的内容。 -但是,原生的拖放事件也有其局限性。例如,我们无法将拖放限制在某个区域内。并且,我们无法将拖放变成“水平”或“垂直”拖放。还有其他一些使用该 API 无法完成的拖放任务。此外,移动设备基本都不支持此事件。 +但是,原生的拖放事件也有其局限性。例如,我们无法阻止从特定区域的拖动。并且,我们无法将拖动变成“水平”或“竖直”的。还有很多其他使用它们无法完成的拖放任务。并且,移动设备对此类事件的支持非常有限。 因此,在这里我们将看到,如何使用鼠标事件来实现拖放。 @@ -14,26 +14,23 @@ 基础的拖放算法如下所示: -1. 在 `mousedown` 上 —— 根据需要准备要移动的元素(也许创建它的一个副本)。 -2. 然后在 `mousemove` 上,通过更改 `left/top` 和 `position:absolute` 来移动它。 +1. 在 `mousedown` 上 —— 根据需要准备要移动的元素(也许创建一个它的副本,向其中添加一个类或其他任何东西)。 +2. 然后在 `mousemove` 上,通过更改 `position:absolute` 情况下的 `left/top` 来移动它。 3. 在 `mouseup` 上 —— 执行与完成的拖放相关的所有行为。 -这些是基础。稍后我们可以扩展它,例如,当鼠标悬停在元素上方时,高亮显示 "droppable"(可用于放置到)的元素。 +这些都是基础内容。稍后,我们将看到如何实现其他功能,例如当我们将一个东西拖动到一个元素上方时,高亮显示该元素。 -下面是拖放一个球的算法: +下面是拖放一个球的实现代码: ```js -ball.onmousedown = function(event) { // (1) 启动处理 - - // (2) 准备移动:确保 absolute,并通过设置 z-index 以确保球在顶部 +ball.onmousedown = function(event) { + // (1) 准备移动:确保 absolute,并通过设置 z-index 以确保球在顶部 ball.style.position = 'absolute'; ball.style.zIndex = 1000; + // 将其从当前父元素中直接移动到 body 中 // 以使其定位是相对于 body 的 document.body.append(ball); - // ...并将绝对定位的球放在鼠标指针下方 - - moveAt(event.pageX, event.pageY); // 现在球的中心在 (pageX, pageY) 坐标上 function moveAt(pageX, pageY) { @@ -41,14 +38,17 @@ ball.onmousedown = function(event) { // (1) 启动处理 ball.style.top = pageY - ball.offsetHeight / 2 + 'px'; } + // 将我们绝对定位的球移到指针下方 + moveAt(event.pageX, event.pageY); + function onMouseMove(event) { moveAt(event.pageX, event.pageY); } - // (3) 在 mousemove 事件上移动球 + // (2) 在 mousemove 事件上移动球 document.addEventListener('mousemove', onMouseMove); - // (4) 放下球,并移除不需要的处理程序 + // (3) 放下球,并移除不需要的处理程序 ball.onmouseup = function() { document.removeEventListener('mousemove', onMouseMove); ball.onmouseup = null; @@ -64,10 +64,10 @@ ball.onmousedown = function(event) { // (1) 启动处理 [iframe src="ball" height=230] -尝试拖放鼠标,你会看到这种奇怪的行为。 +尝试使用鼠标进行拖放,你会看到这种奇怪的行为。 ``` -这是因为浏览器有自己的对图片和一些其他元素的拖放处理,会在我们拖放时自动运行,这与我们的拖放处理产生了冲突。 +这是因为浏览器有自己的对图片和一些其他元素的拖放处理。它会在我们进行拖放操作时自动运行,并与我们的拖放处理产生了冲突。 禁用它: diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html b/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html index 3fdd7fe764..36219e303b 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html @@ -13,16 +13,13 @@ + + + diff --git a/2-ui/3-event-details/6-pointer-events/ball.view/index.html b/2-ui/3-event-details/6-pointer-events/ball.view/index.html new file mode 100644 index 0000000000..8bbef8f631 --- /dev/null +++ b/2-ui/3-event-details/6-pointer-events/ball.view/index.html @@ -0,0 +1,30 @@ + + +

Drag the ball.

+ + + + + + + diff --git a/2-ui/3-event-details/6-pointer-events/multitouch.view/index.html b/2-ui/3-event-details/6-pointer-events/multitouch.view/index.html new file mode 100644 index 0000000000..d46e1bc16f --- /dev/null +++ b/2-ui/3-event-details/6-pointer-events/multitouch.view/index.html @@ -0,0 +1,28 @@ + + + + +
+ Multi-touch here +
+ + + diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/index.html b/2-ui/3-event-details/6-pointer-events/slider.view/index.html new file mode 100644 index 0000000000..2c2a69ec77 --- /dev/null +++ b/2-ui/3-event-details/6-pointer-events/slider.view/index.html @@ -0,0 +1,37 @@ + + + +
+
+
+ + diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/style.css b/2-ui/3-event-details/6-pointer-events/slider.view/style.css new file mode 100644 index 0000000000..9b3d3b82d4 --- /dev/null +++ b/2-ui/3-event-details/6-pointer-events/slider.view/style.css @@ -0,0 +1,19 @@ +.slider { + border-radius: 5px; + background: #E0E0E0; + background: linear-gradient(left top, #E0E0E0, #EEEEEE); + width: 310px; + height: 15px; + margin: 5px; +} + +.thumb { + width: 10px; + height: 25px; + border-radius: 3px; + position: relative; + left: 10px; + top: -5px; + background: blue; + cursor: pointer; +} diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.md b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.md rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.view/index.html b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.view/index.html rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/task.md b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/task.md rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md diff --git a/2-ui/3-event-details/5-keyboard-events/article.md b/2-ui/3-event-details/7-keyboard-events/article.md similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/article.md rename to 2-ui/3-event-details/7-keyboard-events/article.md diff --git a/2-ui/3-event-details/5-keyboard-events/german-layout.svg b/2-ui/3-event-details/7-keyboard-events/german-layout.svg similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/german-layout.svg rename to 2-ui/3-event-details/7-keyboard-events/german-layout.svg diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/style.css b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/style.css rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css diff --git a/2-ui/3-event-details/5-keyboard-events/us-layout.svg b/2-ui/3-event-details/7-keyboard-events/us-layout.svg similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/us-layout.svg rename to 2-ui/3-event-details/7-keyboard-events/us-layout.svg