pull/1208/merge
Zander 3 weeks ago committed by GitHub
commit 926e76f537
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,217 @@
# 项目 Terrarium 第 3 部分DOM 操作和闭包
![DOM 和闭包](../../sketchnotes/webdev101-js.png)
> Sketchnote 由 [Tomomi Imura](https://twitter.com/girlie_mac) 创建
## 课前测验
[课前测验](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/19)
### 介绍
操作 DOM或者称为 "文档对象模型",是 Web 开发的一个关键方面。根据 [MDN](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) 的说法,"文档对象模型DOM是 web 上的文档的结构和内容组成的对象的数据表示。" 在 Web 上进行 DOM 操作的挑战通常是使用 JavaScript 框架而不是纯 JavaScript 来管理 DOM 的动力,但我们将自己来处理!
此外,本课程还将介绍 [JavaScript 闭包](https://developer.mozilla.org/docs/Web/JavaScript/Closures) 的概念,您可以将其视为一个由另一个函数封闭的函数,以便内部函数可以访问外部函数的范围。
> JavaScript 闭包是一个广泛而复杂的主题。本课程只涉及最基本的概念,即在这个 terrarium 代码中,您会发现一个闭包:一个内部函数和一个外部函数以一种允许内部函数访问外部函数范围的方式构造。有关更多详细信息,请访问[广泛的文档](https://developer.mozilla.org/docs/Web/JavaScript/Closures)。
我们将使用闭包来操作 DOM。
将 DOM 想象成一棵树,代表了可以操作 Web 页面文档的所有方式。各种 API应用程序接口已经编写以便程序员可以使用他们选择的编程语言访问 DOM 并进行编辑、更改、重新排列和管理它。
![DOM 树表示](./images/dom-tree.png)
> DOM 和引用它的 HTML 标记的表示。来自 [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
在本课程中,我们将通过创建 JavaScript 完成我们的交互式温室项目,使用户能够操作页面上的植物。
### 先决条件
您应该已经构建了温室的 HTML 和 CSS。在本课程结束时您将能够通过拖动将植物放入和移出温室。
### 任务
在您的温室文件夹中,创建一个名为 `script.js` 的新文件。在 `<head>` 部分导入该文件:
```html
<script src="./script.js" defer></script>
```
> 注意:当将外部 JavaScript 文件导入到 HTML 文件中时,请使用 `defer`,以便允许 JavaScript 仅在 HTML 文件完全加载后执行。您还可以使用 `async` 属性,它允许脚本在 HTML 文件解析时执行,但在我们的情况下,在允许拖动脚本执行之前,完整的 HTML 元素对于拖动是重要的。
---
## DOM 元素
首先,您需要创建对您要在 DOM 中操作的元素的引用。在我们的情况下,它们是当前等待在侧边栏中的 14 个植物。
### 任务
```html
dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
dragElement(document.getElementById('plant4'));
dragElement(document.getElementById('plant5'));
dragElement(document.getElementById('plant6'));
dragElement(document.getElementById('plant7'));
dragElement(document.getElementById('plant8'));
dragElement(document.getElementById('plant9'));
dragElement(document.getElementById('plant10'));
dragElement(document.getElementById('plant11'));
dragElement(document.getElementById('plant12'));
dragElement(document.getElementById('plant13'));
dragElement(document.getElementById('plant14'));
```
这里正在进行的操作是引用文档并查找其 DOM以找到具有特定 ID 的元素。在第一课关于 HTML 中,您为每个植物图像分配了单独的 ID`id="plant1"`),您现在将利用这个工作。在识别每个元素之后,您将该项目传递给一个名为 `dragElement` 的函数您将在接下来的一分钟内构建该函数。因此HTML 中的元素现在是可拖动的,或者很快将变得可拖动。
✅ 为什么我们通过 ID 引用元素?为什么不通过它们的 CSS 类来引用?您可以参考以前关于 CSS 的课程来回答这个问题。
---
## 闭包
现在您已经准备好创建 `dragElement` 闭包了,它是一个包含内部函数或函数的外部函数(在我们的情况下,我们将有三个内部函数)。
当一个或多个函数需要访问外部函数的范围时,闭包非常有用。下面是一个示例:
```javascript
function displayCandy(){
let candy = ['jellybeans'];
function addCandy(candyType) {
candy.push(candyType)
}
addCandy('gumdrops');
}
displayCandy();
console.log(candy)
```
在这个示例中,`displayCandy` 函数包围了一个函数,该函数将一个新的糖果类型推入已经存在于函数中的数组。如果运行这段代码,`candy` 数组将是未定义的,因为它是一个局部变量(局部于闭包)。
✅ 您如何使 `candy` 数组可访问?尝试将它移到闭包外部。这样,数组就变成了全局的,而不仅仅是在闭包的局部范围内可用。
### Task
`script.js` 中的元素声明下面,创建一个函数:
```javascript
function dragElement(terrariumElement) {
//set 4 positions for positioning on the screen
let pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
terrariumElement.onpointerdown = pointerDrag;
}
```
`dragElement` 函数从脚本顶部的声明获取它的 `terrariumElement` 对象。然后,您将传入函数的对象的一些本地位置设置为 `0`。这些是在闭包内为每个元素添加拖放功能时将被操作的本地变量。温室将由这些被拖动的元素填充,因此应用程序需要跟踪它们被放置的位置。
此外,传递给这个函数的 `terrariumElement` 被分配了一个 `pointerdown` 事件,这是用于帮助管理 DOM 的 [Web API](https://developer.mozilla.org/docs/Web/API) 的一部分。当按钮被按下时,或在我们的情况下,可拖动元素被触摸时,`onpointerdown` 事件处理程序将触发。这个事件处理程序在 [Web 和移动浏览器](https://caniuse.com/?search=onpointerdown) 上都可以使用,但有一些例外情况。
✅ [事件处理程序 `onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) 在跨浏览器上有更多的支持;为什么在这里不使用它?考虑一下您试图在这里创建的屏幕交互的确切类型。
---
## The Pointerdrag function
`terrariumElement` 已经准备好可以拖动了;当触发 `onpointerdown` 事件时,将调用 `pointerDrag` 函数。在这一行下面添加该函数:`terrariumElement.onpointerdown = pointerDrag;`
### Task
```javascript
function pointerDrag(e) {
e.preventDefault();
console.log(e);
pos3 = e.clientX;
pos4 = e.clientY;
}
```
发生了几件事情。首先,使用 `e.preventDefault();` 阻止了通常在 `pointerdown` 上发生的默认事件,这样您就可以更好地控制界面的行为。
> 在完全构建脚本文件后,尝试将此行删除并看看会发生什么。
其次,打开浏览器窗口中的 `index.html` 并检查界面。当您点击一个植物时,您可以看到如何捕获 'e' 事件。深入研究事件,看看一次 `pointerdown` 事件可以收集多少信息!
接下来,注意本地变量 `pos3``pos4` 如何设置为 `e.clientX`。您可以在检查面板中找到 `e` 的值。这些值捕获了您点击或触摸植物时的 x 和 y 坐标。您需要对植物的行为进行细粒度的控制,因此要跟踪它们的坐标。
✅ 是不是越来越清楚为什么整个应用程序都是用一个大的闭包构建的?如果不是,您将如何维护每个可拖动植物的作用域?
通过在 `pos4 = e.clientY` 下添加另外两个指针事件操作来完成初始函数:
```html
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
```
现在,您表示希望随着指针的移动,植物将被拖动,并且在取消选择植物时拖动手势会停止。`onpointermove` 和 `onpointerup` 都是与 `onpointerdown` 相同的 API 的一部分。由于您尚未定义 `elementDrag``stopElementDrag` 函数,因此界面现在将抛出错误,所以接下来要构建这两个函数。
## elementDrag 和 stopElementDrag 函数
您将通过添加另外两个内部函数来完成闭包,这些函数将处理在拖动植物和停止拖动植物时发生的情况。您想要的行为是,您可以随时拖动任何植物并将其放置在屏幕上的任何位置。这个界面相当不偏袒(例如,没有投放区域),可以通过添加、删除和重新定位植物来完全按照您的喜好设计您的温室。
### 任务
`pointerDrag` 的右大括号的后面添加 `elementDrag` 函数:
```javascript
function elementDrag(e) {
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
console.log(pos1, pos2, pos3, pos4);
terrariumElement.style.top = terrariumElement.offsetTop - pos2 + 'px';
terrariumElement.style.left = terrariumElement.offsetLeft - pos1 + 'px';
}
```
在这个函数中,您对外部函数中设置为本地变量的初始位置 1-4 进行了大量编辑。这里发生了什么?
在拖动过程中,通过将 `pos1` 重新赋值为 `pos3`(您之前设置为 `e.clientX` 的值)减去当前的 `e.clientX` 值来重新赋值 `pos1`。对 `pos2` 进行类似的操作。然后,您将 `pos3``pos4` 重置为元素的新 X 和 Y 坐标。在拖动时,您可以在控制台中查看这些更改。然后,您通过根据 `pos1``pos2` 的新位置来设置植物的新位置来操作植物的 CSS 样式,计算植物的顶部和左侧 X 和 Y 坐标,基于与这些新位置的比较来确定其偏移。
> `offsetTop``offsetLeft` 是基于父元素的位置设置 CSS 属性;其父元素可以是任何不是 `static` 定位的元素。
所有这些重新计算位置的操作都允许您对温室及其植物的行为进行微调。
### 任务
完成界面的最后一个任务是在 `elementDrag` 的右大括号之后添加 `stopElementDrag` 函数:
```javascript
function stopElementDrag() {
document.onpointerup = null;
document.onpointermove = null;
}
```
这个小函数重置了 `onpointerup``onpointermove` 事件,以便您可以重新开始拖动植物的进程,或者开始拖动新的植物。
✅ 如果不将这些事件设置为 null 会发生什么?
现在,您已经完成了您的项目!
🥇恭喜!您完成了美丽的温室。 ![finished terrarium](./images/terrarium-final.png)
---
## 🚀挑战
向您的闭包添加新的事件处理程序,以对植物进行更多操作;例如,双击植物将其移到最前面。发挥创造力!
## 讲座后测验
[讲座后测验](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/20)
## 复习与自学
虽然在屏幕上拖动元素似乎很简单,但根据您所寻求的效果,有许多方法可以实现,也有许多陷阱。实际上,有一个完整的[拖放 API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API),您可以尝试使用。在这个模块中,我们没有使用它,因为我们想要的效果略有不同,但您可以在自己的项目中尝试使用这个 API看看您能实现什么。
在[W3C文档](https://www.w3.org/TR/pointerevents1/)和[MDN web文档](https://developer.mozilla.org/docs/Web/API/Pointer_events)上找到有关指针事件的更多信息。
始终使用[CanIUse.com](https://caniuse.com/)检查浏览器的兼容性。
## 作业
[深入了解DOM](assignment.md)

@ -69,30 +69,30 @@ Gif by [Mohit Jaisal](https://linkedin.com/in/mohitjaisal)
| | 项目名称 | 教授概念 | 学习目标 | 课程链接 | 课程作者 |
| :-: | :------------------------------------------------------: | :--------------------------------------------------------------------: | ----------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------: | :---------------------: |
| 01 | 入门篇 | 编程语言和行业工具介绍 | 了解大多数编程语言背后的基本支撑,以及帮助专业开发人员完成工作的软件。 | [编程语言和工具介绍](/1-getting-started-lessons/1-intro-to-programming-languages/translations/README.zh-cn.md) | Jasmine |
| 02 | 入门篇 | GitHub的基础知识包括与一个团队合作 | 如何在你的项目中使用GitHub如何在代码库中与他人合作 | [GitHub介绍](/1-getting-started-lessons/2-github-basics/translations/README.zh-cn.md) | Floor |
| 03 | 入门篇 | 无障碍性 | 学习网络无障碍的基本知识 | [无障碍基础知识](/1-getting-started-lessons/3-accessibility/translations/README.zh-cn.md) | Christopher |
| 04 | JS基础 | JavaScript数据类型 | JavaScript数据类型的基础知识 | [数据类型](/2-js-basics/1-data-types/translations/README.zh-cn.md) | Jasmine |
| 05 | JS基础 | 函数和方法 | 了解管理应用程序逻辑流的函数和方法 | [函数和方法](/2-js-basics/2-functions-methods/translations/README.zh-cn.md) | Jasmine and Christopher |
| 06 | JS基础 | 用JS做出决定 | 学习如何使用决策方法在你的代码中创建条件 | [做出决定](/2-js-basics/3-making-decisions/translations/README.zh-cn.md) | Jasmine |
| 07 | JS基础 | 数组和循环 | 在JavaScript中使用数组和循环来处理数据 | [数组和循环](/2-js-basics/4-arrays-loops/translations/README.zh-cn.md) | Jasmine |
| 08 | [花艺瓶](/3-terrarium/solution/README.md) | 实践中的HTML | 构建HTML以创建一个在线花艺瓶重点是构建一个布局 | [HTML简介](/3-terrarium/1-intro-to-html/translations/README.zh-cn.md) | Jen |
| 09 | [花艺瓶](/3-terrarium/solution/README.md) | 实践中的CSS | 构建CSS为在线花艺瓶设计样式重点是CSS的基础知识包括使页面具有响应性 | [CSS简介](/3-terrarium/2-intro-to-css/translations/README.zh-cn.md) | Jen |
| 10 | [花艺瓶](/3-terrarium/solution/README.md) | JavaScript闭包DOM操作 | 构建JavaScript使水族馆成为一个拖/放界面重点是闭包和DOM操作 | [JavaScript闭包DOM操作](/3-terrarium/3-intro-to-DOM-and-closures/README.md) | Jen |
| 11 | [打字游戏](/4-typing-game/solution/README.md) | 建立一个打字游戏 | 学习如何使用键盘事件来驱动你的JavaScript应用程序的逻辑 | [事件驱动编程](/4-typing-game/typing-game/README.md) | Christopher |
| 12 | [绿色浏览器扩展](/5-browser-extension/solution/README.md) | 与浏览器协作 | 了解浏览器是如何工作的,它们的历史,以及如何为浏览器扩展的第一批元素提供支架 | [关于浏览器](/5-browser-extension/1-about-browsers/README.md) | Jen |
| 13 | [绿色浏览器扩展](/5-browser-extension/solution/README.md) | 构建一个表单调用一个API并在本地存储中存储变量 | 构建你的浏览器扩展的JavaScript元素使用存储在本地存储中的变量调用API | [API、表单和本地存储](/5-browser-extension/2-forms-browsers-local-storage/README.md) | Jen |
| 14 | [绿色浏览器扩展](/5-browser-extension/solution/README.md) | 浏览器中的后台进程,网络性能 | 使用浏览器的后台进程来管理扩展的图标;了解网络性能和一些优化 | [后台任务和性能](/5-browser-extension/3-background-tasks-and-performance/README.md) | Jen |
| 15 | [太空游戏](/6-space-game/solution/README.md) | 用JavaScript进行更高级的游戏开发 | 学习使用类和组合的继承以及Pub/Sub模式为建立游戏做准备。 | [高级游戏开发简介](/6-space-game/1-introduction/README.md) | Chris |
| 16 | [太空游戏](/6-space-game/solution/README.md) | 在Canvas上绘图 | 了解用于在屏幕上绘制元素的Canvas API | [在Canvas上绘图](/6-space-game/2-drawing-to-canvas/README.md) | Chris |
| 17 | [太空游戏](/6-space-game/solution/README.md) | 在屏幕上移动元素 | 了解元素如何利用软轴坐标和Canvas API获得运动效果 | [移动元素](/6-space-game/3-moving-elements-around/README.md) | Chris |
| 18 | [太空游戏](/6-space-game/solution/README.md) | 碰撞检测 | 使用按键使元素相互碰撞和反应,并提供冷却功能以确保游戏的性能 | [碰撞检测](/6-space-game/4-collision-detection/README.md) | Chris |
| 19 | [太空游戏](/6-space-game/solution/README.md) | 记分 | 根据游戏的状态和性能进行数学计算 | [记分](/6-space-game/5-keeping-score/README.md) | Chris |
| 20 | [太空游戏](/6-space-game/solution/README.md) | 结束和重启游戏 | 学习结束和重启游戏的方法,包括清理资产和重设变量值 | [结束的条件](/6-space-game/6-end-condition/README.md) | Chris |
| 21 | [银行应用](/7-bank-project/solution/README.md) | 网络应用程序中的HTML模板和路线 | 学习如何使用路由和HTML模板创建多页面网站架构的支架 | [HTML模板和路线](/7-bank-project/1-template-route/README.md) | Yohan |
| 22 | [银行应用](/7-bank-project/solution/README.md) | 建立一个登录和注册表格 | 学习构建表单和交接验证程序的知识 | [表格](/7-bank-project/2-forms/README.md) | Yohan |
| 23 | [银行应用](/7-bank-project/solution/README.md) | 获取和使用数据的方法 | 数据如何流入和流出你的应用程序,如何获取它,存储它,并处理它 | [数据](/7-bank-project/3-data/README.md) | Yohan |
| 24 | [银行应用](/7-bank-project/solution/README.md) | 国家管理的概念 | 了解你的应用程序如何保留状态以及如何以编程方式管理它 | [国家管理](/7-bank-project/4-state-management/README.md) | Yohan |
| 01 | 入门篇 | 编程语言和行业工具介绍 | 了解大多数编程语言背后的基本支撑,以及帮助专业开发人员完成工作的软件。 | [编程语言和工具介绍](/1-getting-started-lessons/1-intro-to-programming-languages/README.zh-cn.md) | Jasmine |
| 02 | 入门篇 | GitHub的基础知识包括与一个团队合作 | 如何在你的项目中使用GitHub如何在代码库中与他人合作 | [GitHub介绍](/1-getting-started-lessons/2-github-basics/README.zh-cn.md) | Floor |
| 03 | 入门篇 | 无障碍性 | 学习网络无障碍的基本知识 | [无障碍基础知识](/1-getting-started-lessons/3-accessibility/README.zh-cn.md) | Christopher |
| 04 | JS基础 | JavaScript数据类型 | JavaScript数据类型的基础知识 | [数据类型](/2-js-basics/1-data-types/README.zh-cn.md) | Jasmine |
| 05 | JS基础 | 函数和方法 | 了解管理应用程序逻辑流的函数和方法 | [函数和方法](/2-js-basics/2-functions-methods/README.zh-cn.md) | Jasmine and Christopher |
| 06 | JS基础 | 用JS做出决定 | 学习如何使用决策方法在你的代码中创建条件 | [做出决定](/2-js-basics/3-making-decisions/README.zh-cn.md) | Jasmine |
| 07 | JS基础 | 数组和循环 | 在JavaScript中使用数组和循环来处理数据 | [数组和循环](/2-js-basics/4-arrays-loops/README.zh-cn.md) | Jasmine |
| 08 | [花艺瓶](/3-terrarium/solution/README.zh-cn.md) | 实践中的HTML | 构建HTML以创建一个在线花艺瓶重点是构建一个布局 | [HTML简介](/3-terrarium/1-intro-to-html/README.zh-cn.md) | Jen |
| 09 | [花艺瓶](/3-terrarium/solution/README.zh-cn.md) | 实践中的CSS | 构建CSS为在线花艺瓶设计样式重点是CSS的基础知识包括使页面具有响应性 | [CSS简介](/3-terrarium/2-intro-to-css/README.zh-cn.md) | Jen |
| 10 | [花艺瓶](/3-terrarium/solution/README.zh-cn.md) | JavaScript闭包DOM操作 | 构建JavaScript使水族馆成为一个拖/放界面重点是闭包和DOM操作 | [JavaScript闭包DOM操作](/3-terrarium/3-intro-to-DOM-and-closures/README.zh-cn.md) | Jen |
| 11 | [打字游戏](/4-typing-game/solution/README.md) | 建立一个打字游戏 | 学习如何使用键盘事件来驱动你的JavaScript应用程序的逻辑 | [事件驱动编程](/4-typing-game/typing-game/README.zh-tw.md) | Christopher |
| 12 | [绿色浏览器扩展](/5-browser-extension/solution/README.md) | 与浏览器协作 | 了解浏览器是如何工作的,它们的历史,以及如何为浏览器扩展的第一批元素提供支架 | [关于浏览器](/5-browser-extension/1-about-browsers/README.zh-tw.md) | Jen |
| 13 | [绿色浏览器扩展](/5-browser-extension/solution/README.md) | 构建一个表单调用一个API并在本地存储中存储变量 | 构建你的浏览器扩展的JavaScript元素使用存储在本地存储中的变量调用API | [API、表单和本地存储](/5-browser-extension/2-forms-browsers-local-storage/README.zh-tw.md) | Jen |
| 14 | [绿色浏览器扩展](/5-browser-extension/solution/README.md) | 浏览器中的后台进程,网络性能 | 使用浏览器的后台进程来管理扩展的图标;了解网络性能和一些优化 | [后台任务和性能](/5-browser-extension/3-background-tasks-and-performance/README.zh-tw.md) | Jen |
| 15 | [太空游戏](/6-space-game/solution/README.md) | 用JavaScript进行更高级的游戏开发 | 学习使用类和组合的继承以及Pub/Sub模式为建立游戏做准备。 | [高级游戏开发简介](/6-space-game/1-introduction/README.zh-tw.md) | Chris |
| 16 | [太空游戏](/6-space-game/solution/README.md) | 在Canvas上绘图 | 了解用于在屏幕上绘制元素的Canvas API | [在Canvas上绘图](/6-space-game/2-drawing-to-canvas/README.zh-tw.md) | Chris |
| 17 | [太空游戏](/6-space-game/solution/README.md) | 在屏幕上移动元素 | 了解元素如何利用软轴坐标和Canvas API获得运动效果 | [移动元素](/6-space-game/3-moving-elements-around/README.zh-tw.md) | Chris |
| 18 | [太空游戏](/6-space-game/solution/README.md) | 碰撞检测 | 使用按键使元素相互碰撞和反应,并提供冷却功能以确保游戏的性能 | [碰撞检测](/6-space-game/4-collision-detection/README.zh-tw.md) | Chris |
| 19 | [太空游戏](/6-space-game/solution/README.md) | 记分 | 根据游戏的状态和性能进行数学计算 | [记分](/6-space-game/5-keeping-score/README.zh-tw.md) | Chris |
| 20 | [太空游戏](/6-space-game/solution/README.md) | 结束和重启游戏 | 学习结束和重启游戏的方法,包括清理资产和重设变量值 | [结束的条件](/6-space-game/6-end-condition/README.zh-tw.md) | Chris |
| 21 | [银行应用](/7-bank-project/solution/README.md) | 网络应用程序中的HTML模板和路线 | 学习如何使用路由和HTML模板创建多页面网站架构的支架 | [HTML模板和路线](/7-bank-project/1-template-route/README.zh-tw.md) | Yohan |
| 22 | [银行应用](/7-bank-project/solution/README.md) | 建立一个登录和注册表格 | 学习构建表单和交接验证程序的知识 | [表格](/7-bank-project/2-forms/README.zh-tw.md) | Yohan |
| 23 | [银行应用](/7-bank-project/solution/README.md) | 获取和使用数据的方法 | 数据如何流入和流出你的应用程序,如何获取它,存储它,并处理它 | [数据](/7-bank-project/3-data/README.zh-tw.md) | Yohan |
| 24 | [银行应用](/7-bank-project/solution/README.md) | 国家管理的概念 | 了解你的应用程序如何保留状态以及如何以编程方式管理它 | [国家管理](/7-bank-project/4-state-management/README.zh-tw.md) | Yohan |
## 离线访问

Loading…
Cancel
Save