diff --git a/2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md b/2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md index 121e42fef3..2d81bf5155 100644 --- a/2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md +++ b/2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md @@ -4,7 +4,7 @@ importance: 5 # 点击隐藏 -为 `button` 添加 JavaScript ,使我们在点击的时候让 `
` 消失。 +为 `button` 添加 JavaScript 代码,使得 `
` 在我们点击该按钮时消失。 示例: diff --git a/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md b/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md index c9fa4993f9..ff6d483b44 100644 --- a/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md @@ -1,4 +1,4 @@ -可以在处理器中使用 `this` 来引用自身: +可以在处理程序中使用 `this` 来引用“元素自身”: ```html run height=50 diff --git a/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md b/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md index f02ff6ea24..b914e5178c 100644 --- a/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md +++ b/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md @@ -4,7 +4,7 @@ importance: 5 # 隐藏自己 -创建一个按钮,在单击时,隐藏自己。 +创建一个按钮,在被单击时,隐藏自己。 ```online 就像这样: diff --git a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md index a484489f2a..2b4a836a98 100644 --- a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md @@ -1,8 +1,8 @@ 答案:`1` 和 `2`。 -第一个处理器会触发,因为它没有被 `removeEventListener` 移除。要移除处理器,我们需要传递正确分发的函数。在代码中,传递了一个新的函数,看起来相同,但仍然是另一个函数。 +第一个处理程序会触发,因为它没有被 `removeEventListener` 移除。要移除处理程序,我们需要传递正确的所分配的函数。在代码中,传递了一个新的函数,该函数看起来相同,但仍然是另一个函数。 -要移除函数对象,我们需要存储对它的引用,就像这样: +要移除一个函数对象,我们需要存储对它的引用,像这样: ```js function handler() { @@ -13,4 +13,4 @@ button.addEventListener("click", handler); button.removeEventListener("click", handler); ``` -处理器 `button.onclick` 独立于 `addEventListener` 之外工作。 +无论 `addEventListener` 怎样,`button.onclick` 处理程序都会触发。 diff --git a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md index ab20c3874b..63b190801f 100644 --- a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md +++ b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# 哪一个处理器会运行? +# 哪个处理程序会运行? -变量中有一个按钮,上面没有处理器。 +在变量中有一个按钮。它上面没有处理程序。 -在下面代码之后单击哪些处理器会运行?会出现哪些警报? +执行以下代码之后,哪些处理程序会在按钮被点击时运行?会显示哪些 alert? ```js no-beautify button.addEventListener("click", () => alert("1")); diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md index 9c152278c5..1d68516ec8 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md @@ -1,11 +1,11 @@ -首先我们需要选择一种定位球的方法。 +首先,我们需要选择一种定位球的方法。 -我们不能使用 `position:fixed`,因为滑动页面会让球在球场上移动。 +我们不能使用 `position:fixed`,因为滚动页面会造成球被移出球场。 -因此我们应该使用 `position:absolute`,并使定位稳定,让 `field` 自身定位。 +所以我们应该使用 `position:absolute`,并且要使定位真正可靠,应该使 `field` 自身具有 `position:absolute`。 -然后球会相对于球场定位: +然后,球将相对于球场定位: ```css #field { @@ -16,36 +16,36 @@ #ball { position: absolute; - left: 0; /* 相对于最近位置的祖先(字段) */ + left: 0; /* 相对于最接近的祖先(field) */ top: 0; - transition: 1s all; /* 在左上方的 CSS 特效会让球飞起来 */ + transition: 1s all; /* left/top 的 CSS 动画,使球飞起来 */ } ``` -接下来我们需要指定正确的 `ball.style.position.left/top`。它们现在包含球场的相对坐标。 +接下来我们需要指定正确的 `ball.style.left/top`。它们现在包含相对于球场的坐标。 -这是图片: +这是示意图: ![](move-ball-coords.svg) -我们有 `event.clientX/clientY`—— 单击窗口时的相对坐标。 +我们有 `event.clientX/clientY` —— 单击位置的窗口相对坐标。 -要获取单击字段的相对 `left` 坐标,我们可以减去字段的左边缘和边框宽度: +要获取单击位置的球场相对坐标 `left`,我们可以减去球场左边缘和边框的宽度: ```js -let left = event.clientX - fieldInnerCoords.left - field.clientLeft; +let left = event.clientX - fieldCoords.left - field.clientLeft; ``` -通常情况下,`ball.style.position.left` 是指“元素的左边缘”(球)。因此,如果我们指定 `left`,那么球的边缘就会在鼠标光标下面。 +通常情况下,`ball.style.left` 表示“元素的左边缘”(球)。因此,如果我们将其指定为 `left`,那么球的边缘而非球的中心将位于鼠标光标下方。 -我们需要将球向左移动宽度的一半,向上移动高度的一半,使其居中。 +我们需要将球向左移动球宽度的一半,向上移动球高度的一半,以使其居中。 -因此,最后 `left` 是: +所以,最后的 `left` 将是: ```js -let left = event.clientX - fieldInnerCoords.left - field.clientLeft - ball.offsetWidth/2; +let left = event.clientX - fieldCoords.left - field.clientLeft - ball.offsetWidth/2; ``` -使用相同的逻辑计算垂直坐标。 +使用相同的逻辑来计算垂直坐标。 -请注意,球的宽度/高度必须在我们设置 `ball.offsetWidth` 时就已知。应该在 HTML 或 CSS 中指定。 +请注意,球的宽度/高度必须在我们访问 `ball.offsetWidth` 时就已知。应该在 HTML 或 CSS 中指定。 diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md index a4088783a4..bb7469706f 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md @@ -4,18 +4,18 @@ importance: 5 # 让球在球场中移动 -单击一下让球在球场中移动。就像这样: +单击球场中任意一点,让球在球场中移动。就像这样: [iframe src="solution" height="260" link] 要求: -- 球的中心应该准确的在单击时指针位置的下方(如果可能的话,不越过球场边缘)。 -- CSS 动画很受欢迎。 +- 球的中心应该恰好在单击时指针位置的下方(如果在球不越过球场边缘的情况下,能实现的话)。 +- 最好添加一些 CSS 动画。 - 球不能越过场地边界。 -- 当页面被滚动时,任何东西都不应该中断。 +- 页面滚动时,不会有任何中断。 注意: -- 代码还应该能在不同的球和球场大小中工作,而不是绑定到任何固定的值。 +- 代码还应该适用于不同大小的球和球场,而不应该绑定到任何固定值。 - 使用 `event.clientX/event.clientY` 属性来获取点击坐标。 diff --git a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md index 8a7bc1ad57..ac939abee3 100644 --- a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md @@ -1,12 +1,13 @@ # HTML/CSS -首先,我们创建 HTML/CSS。 -菜单是页面上的一个独立的图形组件,所以最好把它放在一个 DOM 元素中。 +首先,让我们创建 HTML/CSS。 -菜单项列表可以分层为列表 `ul/li`。 +菜单是页面上的一个独立图形组件,所以最好把它放入一个单独的 DOM 元素中。 -下面是示例的结构: +菜单项的列表可以被作为列表 `ul/li` 列出。 + +下面是示例结构: ```html ``` -我们的标题使用 ``,因为 `
` 有一个隐式的 `display:block`,它会 100% 的占据水平宽度。 +我们对标题使用 ``,因为 `
` 有一个隐式的 `display:block`,它会占据 100% 的水平宽度。 就像这样: @@ -27,21 +28,21 @@
Sweeties (click me)!
``` -因此如果我们在它上面设置 `onclick`,就会在文本的右边捕获点击事件。 +因此,如果我们在它上面设置 `onclick`,那么它也会捕获文本右侧的点击。 -...但 `` 有一个隐式 `display: inline`,因此它会占据足够的位置来适应所有的文本: +……由于 `` 有一个隐式的 `display: inline`,它恰好占据了足以容纳所有文本的位置: ```html autorun height=50 Sweeties (click me)! ``` - # 切换菜单 +# 切换菜单 切换菜单应更改箭头并显示/隐藏菜单列表。 -所以这些更改都能被 CSS 完美处理。在 JavaScript 中,我们应该通过添加/移除 `.open` 类来标记菜单的当前状态。 +所有这些更改都可以通过 CSS 完美处理。在 JavaScript 中,我们应该通过添加/移除 `.open` 类来标记菜单的当前状态。 -没有它,菜单就会关闭: +没有它,菜单就会被关闭: ```css .menu ul { @@ -58,7 +59,7 @@ } ``` -...使用 `.open` 后,箭头会改变,列表会出现: +……有 `.open` 后,箭头会改变,列表会出现: ```css .menu.open .title::before { diff --git a/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md b/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md index a4709c4cab..cb30b86e06 100644 --- a/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md @@ -1,12 +1,12 @@ -我们可以使用 `position:absolute`(使 pane `position:relative`)或者 `float:right` 来添加按钮。`float:right` 的好处是按钮永远都不会重叠文本,但是 `position:absolute` 有更多的灵活性,选择权在你手上。 +我们可以使用 `position:absolute`(并使窗格 `position:relative`)或者 `float:right` 来添加按钮。`float:right` 的好处是按钮永远都不会与文本重叠,但是 `position:absolute` 则提供了更大的自由度。选择权在你自己手上。 -然后对于每个 pane 来说,代码都是如此: +然后,对于每个窗格(pane),代码可以像这样: ```js pane.insertAdjacentHTML("afterbegin", ''); ``` -然后 ` @@ -148,96 +148,98 @@ elem.onclick = sayThanks; ## 可能出现的错误 -如果你刚开始处理事件 —— 请注意一些微妙的地方。 +如果你刚开始写事件 —— 请注意一些细微之处。 -**函数应该作为 `sayThanks` 进行分发,而不是 `sayThanks()`**。 +**函数应该被以 `sayThanks` 的形式进行非配,而不是 `sayThanks()`**。 ```js -// right +// 正确 button.onclick = sayThanks; -// wrong +// 错误 button.onclick = sayThanks(); ``` -如果我们添加括号,那么就是 `sayThanks()` —— 将是函数执行的**结果**,所以最后一行代码中的 `onclick` 变成了 `undefined`(函数返回的内容将什么也没有)。这是不可取的。 +如果我们添加了括号 `sayThanks()` —— 这是一个函数调用。所以,最后一行代码实际上获得的是函数执行的 **结果**,即 `undefined`(因为这个函数没有返回值)。此代码不会工作。 -...但在标记中,我们确实需要括号: +……但在标记(markup,译注:也就是 HTML 标签)中,我们确实需要括号: ```html ``` -这个区别很容易解释。当浏览器读取属性时,它会从其内容中创建一个处理器函数。 +这个区别很容易解释。当浏览器读取 HTML 特性(attribute)时,浏览器将会使用 **特性中的内容** 创建一个处理函数:`sayThanks()`。 -所以最后的示例相同: +所以,标记会生成下面这个属性: ```js button.onclick = function() { *!* - sayThanks(); // the attribute content + sayThanks(); // 特性中的内容 */!* }; ``` -**使用函数,而不是字符串**。 +**使用函数,而不是字符串。** -`elem.onclick = "alert(1)"` 也可以执行,这适用于兼容性原因,但是强烈建议不使用这种方式。 +`elem.onclick = "alert(1)"` 也可以执行。它能执行是出于兼容性,但强烈建议不要使用这种方式。 -**不要为处理器使用 `setAttribute`**。 +**不要对处理程序使用 `setAttribute`。** 这样的调用会失效: ```js run no-beautify -// 单击 将产生错误, -// 因为属性总是字符串,函数就变成了字符串。 +// 单击 将产生 error, +// 因为特性总是字符串的,函数变成了一个字符串 document.body.setAttribute('onclick', function() { alert(1) }); ``` -**DOM 属性大小写的重要性**。 +**DOM 属性是大小写敏感的。** -为 `elem.onclick` 分发处理器,而不是 `elem.ONCLICK`,因为 DOM 属性是大小写敏感的。 +将处理程序分配给 `elem.onclick`,而不是 `elem.ONCLICK`,因为 DOM 属性是大小写敏感的。 ## addEventListener -前面提到分发处理器的基本问题是 —— 我们不能为一个事件分发多个处理器。 +上述分配处理程序的方式的根本问题是 —— 我们不能为一个事件分配多个处理程序。 -例如,我们代码的一部分希望在单击时高亮显示按钮,另一部分希望显示消息。 +例如,在我们点击了一个按钮时,我们代码中的一部分想要高亮显示这个按钮,另一部分则想要显示一条消息。 -我们想为此分发两个处理器。但是一个新的 DOM 属性将重写现有的 DOM 属性: +我们想为此事件分配两个处理程序。但是,新的 DOM 属性将覆盖现有的 DOM 属性: ```js no-beautify input.onclick = function() { alert(1); } // ... -input.onclick = function() { alert(2); } // replaces the previous handler +input.onclick = function() { alert(2); } // 替换了前一个处理程序 ``` -Web 标准的开发者很久之前就明白了这一点,并提出了一种使用特殊方法 `addEventListener` 和 `removeEventListener` 来管理处理器的替代方法。它们没有这样的问题。 +Web 标准的开发者很早就了解到了这一点,并提出了一种使用特殊方法 `addEventListener` 和 `removeEventListener` 来管理处理程序的替代方法。它们没有这样的问题。 -添加处理器的语法: +添加处理程序的语法: ```js -element.addEventListener(event, handler[, phase]); +element.addEventListener(event, handler, [options]); ``` `event` : 事件名,例如:`"click"`。 `handler` -: 处理器函数。 +: 处理函数。 -`phase` -: 一个可选的参数,即处理器的工作“阶段”。之后会讨论。我们通常不会使用它。 +`options` +: 具有以下属性的附加可选对象: + - `once`:如果为 `true`,那么会在被触发后自动删除监听器。 + - `capture`:事件处理的阶段,我们稍后将在 一章中介绍。由于历史原因,`options` 也可以是 `false/true`,它与 `{capture: false/true}` 相同。 + - `passive`:如果为 `true`,那么处理程序将不会 `preventDefault()`,我们稍后将在 一章中介绍。 -使用 `removeEventListener` 移除处理器: +要移除处理程序,可以使用 `removeEventListener`: ```js -// exactly the same arguments as addEventListener -element.removeEventListener(event, handler[, phase]); +element.removeEventListener(event, handler, [options]); ``` -````warn header="Removal requires the same function" -要移除处理器,我们需要传入与分发函数完全相同的函数。 +````warn header="移除需要相同的函数" +要移除处理程序,我们需要传入与分配的函数完全相同的函数。 这不起作用: @@ -247,9 +249,9 @@ elem.addEventListener( "click" , () => alert('Thanks!')); elem.removeEventListener( "click", () => alert('Thanks!')); ``` -处理器不会被移除,因为 `removeEventListener` 将获取另一个函数 —— 相同的代码,但这并不起作用。 +处理程序不会被移除,因为 `removeEventListener` 获取了另一个函数 —— 使用相同的代码,但这并不起作用。 -以下是正确方法: +下面是正确方法: ```js function handler() { @@ -261,10 +263,10 @@ input.addEventListener("click", handler); input.removeEventListener("click", handler); ``` -请注意 —— 如果我们不将函数存储在一个变量中,那么我们就无法移除它。由 `addEventListener` 分发的处理器将无法“读回”。 +请注意 —— 如果我们不将函数存储在一个变量中,那么我们就无法移除它。由 `addEventListener` 分配的处理程序将无法被“读回”。 ```` -多次调用 `addEventListener` 允许添加多个处理器,就像这样: +多次调用 `addEventListener` 允许添加多个处理程序,如下所示: ```html run no-beautify @@ -286,57 +288,41 @@ input.removeEventListener("click", handler); ``` -正如我们在以上所看到的那样,我们可以使用 DOM 属性**和** `addEventListener` 来设置处理器。但通常我们只使用其中一种方法。 +正如我们在上面这个例子中所看到的,我们可以 **同时** 使用 DOM 属性和 `addEventListener` 来设置处理程序。但通常我们只使用其中一种方式。 -````warn header="有些事件处理器只能通过 `addEventListener` 设置" -有些事件不能通过 DOM 属性分配。必须使用 `addEventListener`。 +````warn header="对于某些事件,只能通过 `addEventListener` 设置处理程序" +有些事件无法通过 DOM 属性进行分配。必须使用 `addEventListener`。 -事件 `transitionend`(CSS 动画完成)就是如此。 +例如,`DOMContentLoaded` 事件,该事件在文档加载完成并且 DOM 构建完成时触发。 -尝试以下代码,大多数浏览器中只有第二个处理器正常运行,而不是第一个。 - -```html run - - - - - +```js +document.addEventListener("DOMContentLoaded", function() { + alert("DOM built"); // 这种方式可以运行 +}); ``` +所以 `addEventListener` 更通用。虽然这样的事件是特例而不是规则。 ```` ## 事件对象 -为了正确处理事件,我们需要知道更多关于事件内容的细节。不仅仅是 "click" 或者 "keypress",而是指针坐标在哪而?哪个键被按了?等等。 +为了正确处理事件,我们需要更深入地了解发生了什么。不仅仅是 "click" 或 "keypress",还包括鼠标光标的坐标是什么?按下了哪个键?等等。 -当事件发生时,浏览器会创建一个**事件对象**,将信息放入其中,并将其作为参数传入处理器。 +当事件发生时,浏览器会创建一个 **`event` 对象**,将详细信息放入其中,并将其作为参数传递给处理程序。 -以下是从事件对象获取鼠标坐标的示例: +下面是一个从 `event` 对象获取鼠标坐标的示例: ```html run ``` -换句话说,当 `addEventListener` 接收一个对象作为处理器时候,就会调用 `object.handleEvent(event)` 来处理事件。 +正如我们所看到的,当 `addEventListener` 接收一个对象作为处理程序时,在事件发生时,它就会调用 `object.handleEvent(event)` 来处理事件。 -我们也可以使用一个类: +我们也可以对此使用一个类: ```html run @@ -416,9 +402,9 @@ input.removeEventListener("click", handler); ``` -这里的同一对象会处理两个事件。请注意,我们需要使用 `addEventListener` 来指明要监听的事件。`menu` 对象在这里只监听 `mousedown` 和 `mouseup`,而不是任意其他类型的事件。 +这里,同一个对象处理两个事件。请注意,我们需要使用 `addEventListener` 来显示设置事件,以指明要监听的事件。这里的 `menu` 对象只监听 `mousedown` 和 `mouseup`,而没有任何其他类型的事件。 -`handleEvent` 方法本身不会做所有的工作。它可以调用其他用于特定事件的方法,比如: +`handleEvent` 方法不必通过自身完成所有的工作。它可以调用其他特定于事件的方法,例如: ```html run @@ -446,22 +432,22 @@ input.removeEventListener("click", handler); ``` -现在事件处理器是完全独立的,这样会更容易被支持。 +现在事件处理程序已经明确地分离了出来,这样更容易进行代码编写和后续维护。 ## 总结 -有 3 种方法可以分发事件处理器: +这里有 3 种分配事件处理程序的方式: -1. HTML 属性:`onclick="..."`。 -2. DOM 属性 `elem.onclick = function`。 -3. 方法:添加 `elem.addEventListener(event, handler[, phase])`,移除 `removeEventListener`。 +1. HTML 特性(attribute):`onclick="..."`。 +2. DOM 属性(property):`elem.onclick = function`。 +3. 方法(method):`elem.addEventListener(event, handler, [phase])` 用于添加,`removeEventListener` 用于移除。 -HTML 属性很少使用,因为 HTML 标签中的 JavaScript 看起来奇怪又陌生。而且也不能在里面写太多的代码。 +HTML 特性很少使用,因为 HTML 标签中的 JavaScript 看起来有些奇怪且陌生。而且也不能在里面写太多代码。 -DOM 属性可以使用,但我们不能为特定事件分发多个处理器。在许多场景中,这种限制并不严重。 +DOM 属性用起来还可以,但我们无法为特定事件分配多个处理程序。在许多场景中,这种限制并不严重。 -最后一种方法是最灵活的,但也是编写内容最多的。有少数事件只能使用这种方式。例如 `transtionend` 和 `DOMContentLoaded`(有待讨论)。当然 `addEventListener` 也支持对象作为事件处理器。在这种场景下,事件发生时就需要调用 `handleEvent` 方法。 +最后一种方式是最灵活的,但也是写起来最长的。有少数事件只能使用这种方式。例如 `transtionend` 和 `DOMContentLoaded`(上文中讲到了)。`addEventListener` 也支持对象作为事件处理程序。在这种情况下,如果发生事件,则会调用 `handleEvent` 方法。 -无论你如何分发处理器 —— 它都会将事件对象作为第一个参数。该对象包含事件发生的细节。 +无论你如何分类处理程序 —— 它都会将获得一个事件对象作为第一个参数。该对象包含有关所发生事件的详细信息。 -我们将在下一章了解更多关于一般事件和不同类型事件的内容。 +在下一章中,我们将学习更多关于一般事件和不同类型事件的内容。