` 作为参数。
+Add a callback argument: `showCircle(cx, cy, radius, callback)` to be called when the animation is complete. The `callback` should receive the circle `
` as an argument.
-这是示例:
+Here's the example:
```js
showCircle(150, 150, 100, div => {
@@ -18,8 +18,8 @@ showCircle(150, 150, 100, div => {
});
```
-案例:
+Demo:
[iframe src="solution" height=260]
-以 任务作为解决问题的基础。
+Take the solution of the task as the base.
diff --git a/1-js/11-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md
index 817ec74a28..ee0bb3caaf 100644
--- a/1-js/11-async/01-callbacks/article.md
+++ b/1-js/11-async/01-callbacks/article.md
@@ -1,10 +1,10 @@
-# 简介:回调
+# Introduction: callbacks
-JavaScipt 中的许多动作都是**异步**的。
+Many actions in JavaScript are *asynchronous*.
-比如,这个 `loadScript(src)` 函数:
+For instance, take a look at the function `loadScript(src)`:
```js
function loadScript(src) {
@@ -14,40 +14,40 @@ function loadScript(src) {
}
```
-这个函数的作用是加载一个新的脚本。当使用 `
diff --git a/1-js/11-async/05-promise-api/iliakan.json b/1-js/11-async/05-promise-api/iliakan.json
new file mode 100644
index 0000000000..32f89971a8
--- /dev/null
+++ b/1-js/11-async/05-promise-api/iliakan.json
@@ -0,0 +1,4 @@
+{
+ "name": "iliakan",
+ "isAdmin": true
+}
diff --git a/1-js/11-async/05-promise-api/one.js b/1-js/11-async/05-promise-api/one.js
new file mode 100644
index 0000000000..948a60e075
--- /dev/null
+++ b/1-js/11-async/05-promise-api/one.js
@@ -0,0 +1,3 @@
+function one() {
+ alert(1);
+}
diff --git a/1-js/11-async/05-promise-api/two.js b/1-js/11-async/05-promise-api/two.js
new file mode 100644
index 0000000000..b04795b86c
--- /dev/null
+++ b/1-js/11-async/05-promise-api/two.js
@@ -0,0 +1,3 @@
+function two() {
+ alert(2);
+}
diff --git a/1-js/11-async/06-promisify/article.md b/1-js/11-async/06-promisify/article.md
new file mode 100644
index 0000000000..7c84912b57
--- /dev/null
+++ b/1-js/11-async/06-promisify/article.md
@@ -0,0 +1,118 @@
+# Promisification
+
+Promisification -- is a long word for a simple transform. It's conversion of a function that accepts a callback into a function returning a promise.
+
+To be more precise, we create a wrapper-function that does the same, internally calling the original one, but returns a promise.
+
+Such transforms are often needed in real-life, as many functions and libraries are callback-based. But promises are more convenient. So it makes sense to promisify those.
+
+For instance, we have `loadScript(src, callback)` from the chapter .
+
+```js run
+function loadScript(src, callback) {
+ let script = document.createElement('script');
+ script.src = src;
+
+ script.onload = () => callback(null, script);
+ script.onerror = () => callback(new Error(`Script load error for ${src}`));
+
+ document.head.append(script);
+}
+
+// usage:
+// loadScript('path/script.js', (err, script) => {...})
+```
+
+Let's promisify it. The new `loadScriptPromise(src)` function will do the same, but accept only `src` (no callback) and return a promise.
+
+```js
+let loadScriptPromise = function(src) {
+ return new Promise((resolve, reject) => {
+ loadScript(src, (err, script) => {
+ if (err) reject(err)
+ else resolve(script);
+ });
+ })
+}
+
+// usage:
+// loadScriptPromise('path/script.js').then(...)
+```
+
+Now `loadScriptPromise` fits well in our promise-based code.
+
+As we can see, it delegates all the work to the original `loadScript`, providing its own callback that translates to promise `resolve/reject`.
+
+As we may need to promisify many functions, it makes sense to use a helper.
+
+That's actually very simple -- `promisify(f)` below takes a to-promisify function `f` and returns a wrapper function.
+
+That wrapper does the same as in the code above: returns a promise and passes the call to the original `f`, tracking the result in a custom callback:
+
+```js
+function promisify(f) {
+ return function (...args) { // return a wrapper-function
+ return new Promise((resolve, reject) => {
+ function callback(err, result) { // our custom callback for f
+ if (err) {
+ return reject(err);
+ } else {
+ resolve(result);
+ }
+ }
+
+ args.push(callback); // append our custom callback to the end of arguments
+
+ f.call(this, ...args); // call the original function
+ });
+ };
+};
+
+// usage:
+let loadScriptPromise = promisify(loadScript);
+loadScriptPromise(...).then(...);
+```
+
+Here we assume that the original function expects a callback with two arguments `(err, result)`. That's what we encounter most often. Then our custom callback is in exactly the right format, and `promisify` works great for such a case.
+
+But what if the original `f` expects a callback with more arguments `callback(err, res1, res2)`?
+
+Here's a modification of `promisify` that returns an array of multiple callback results:
+
+```js
+// promisify(f, true) to get array of results
+function promisify(f, manyArgs = false) {
+ return function (...args) {
+ return new Promise((resolve, reject) => {
+ function *!*callback(err, ...results*/!*) { // our custom callback for f
+ if (err) {
+ return reject(err);
+ } else {
+ // resolve with all callback results if manyArgs is specified
+ *!*resolve(manyArgs ? results : results[0]);*/!*
+ }
+ }
+
+ args.push(callback);
+
+ f.call(this, ...args);
+ });
+ };
+};
+
+// usage:
+f = promisify(f, true);
+f(...).then(arrayOfResults => ..., err => ...)
+```
+
+In some cases, `err` may be absent at all: `callback(result)`, or there's something exotic in the callback format, then we can promisify such functions without using the helper, manually.
+
+There are also modules with a bit more flexible promisification functions, e.g. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). In Node.js, there's a built-in `util.promisify` function for that.
+
+```smart
+Promisification is a great approach, especially when you use `async/await` (see the next chapter), but not a total replacement for callbacks.
+
+Remember, a promise may have only one result, but a callback may technically be called many times.
+
+So promisification is only meant for functions that call the callback once. Further calls will be ignored.
+```
diff --git a/1-js/11-async/07-microtask-queue/article.md b/1-js/11-async/07-microtask-queue/article.md
index 6faf08e085..b2897a56f0 100644
--- a/1-js/11-async/07-microtask-queue/article.md
+++ b/1-js/11-async/07-microtask-queue/article.md
@@ -1,4 +1,5 @@
+<<<<<<< HEAD
# Microtasks 和事件循环
Promise 的处理程序(handlers)`.then`、`.catch` 和 `.finally` 都是异步的。
@@ -6,12 +7,22 @@ Promise 的处理程序(handlers)`.then`、`.catch` 和 `.finally` 都是异
即便一个 promise 立即被 resolve,`.then`、`.catch` 和 `.finally` **下面**的代码也会在这些处理程序之前被执行。
示例代码如下:
+=======
+# Microtasks
+
+Promise handlers `.then`/`.catch`/`.finally` are always asynchronous.
+
+Even when a Promise is immediately resolved, the code on the lines *below* `.then`/`.catch`/`.finally` will still execute before these handlers .
+
+Here's the demo:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js run
let promise = Promise.resolve();
promise.then(() => alert("promise done"));
+<<<<<<< HEAD
alert("code finished"); // 该警告框会首先弹出
```
@@ -43,6 +54,39 @@ Promise 处理程序总是被放入这个内部队列中。
**如果返回值的顺序对我们很重要该怎么办?我们怎么才能让 `code finished` 在 `promise done` 之后出现呢?**
很简单,只需要像下面这样把返回 `code finished` 的 `.then` 处理程序放入队列中:
+=======
+alert("code finished"); // this alert shows first
+```
+
+If you run it, you see `code finished` first, and then `promise done`.
+
+That's strange, because the promise is definitely done from the beginning.
+
+Why did the `.then` trigger afterwards? What's going on?
+
+## Microtasks queue
+
+Asynchronous tasks need proper management. For that, the standard specifies an internal queue `PromiseJobs`, more often referred to as "microtask queue" (v8 term).
+
+As said in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues):
+
+- The queue is first-in-first-out: tasks enqueued first are run first.
+- Execution of a task is initiated only when nothing else is running.
+
+Or, to say that simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue. They are not executed yet. JavaScript engine takes a task from the queue and executes it, when it becomes free from the current code.
+
+That's why "code finished" in the example above shows first.
+
+
+
+Promise handlers always go through that internal queue.
+
+If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. That is, it first gets queued, and executed when the current code is complete and previously queued handlers are finished.
+
+**What if the order matters for us? How can we make `code finished` work after `promise done`?**
+
+Easy, just put it into the queue with `.then`:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js run
Promise.resolve()
@@ -50,6 +94,7 @@ Promise.resolve()
.then(() => alert("code finished"));
```
+<<<<<<< HEAD
现在代码就是按照预期执行的。
## 事件循环
@@ -157,10 +202,45 @@ window.addEventListener('unhandledrejection', event => alert(event.reason));
```
如下所示,现在我们假设会在 `setTimeout` 之后抓住这个错误:
+=======
+Now the order is as intended.
+
+## Unhandled rejection
+
+Remember "unhandled rejection" event from the chapter ?
+
+Now we can see exactly how JavaScript finds out that there was an unhandled rejection
+
+**"Unhandled rejection" occurs when a promise error is not handled at the end of the microtask queue.**
+
+Normally, if we expect an error, we add `.catch` to the promise chain to handle it:
+
+```js run
+let promise = Promise.reject(new Error("Promise Failed!"));
+*!*
+promise.catch(err => alert('caught'));
+*/!*
+
+// doesn't run: error handled
+window.addEventListener('unhandledrejection', event => alert(event.reason));
+```
+
+...But if we forget to add `.catch`, then, after the microtask queue is empty, the engine triggers the event:
+
+```js run
+let promise = Promise.reject(new Error("Promise Failed!"));
+
+// Promise Failed!
+window.addEventListener('unhandledrejection', event => alert(event.reason));
+```
+
+What if we handle the error later? Like this:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js run
let promise = Promise.reject(new Error("Promise Failed!"));
*!*
+<<<<<<< HEAD
setTimeout(() => promise.catch(err => alert('caught')));
*/!*
@@ -189,3 +269,29 @@ window.addEventListener('unhandledrejection', event => alert(event.reason));
换句话说,它们的优先级较低。
所以顺序是:常规代码,然后是 promise 处理程序,然后是其他的一切,比如事件等等。
+=======
+setTimeout(() => promise.catch(err => alert('caught')), 1000);
+*/!*
+
+// Error: Promise Failed!
+window.addEventListener('unhandledrejection', event => alert(event.reason));
+```
+
+Now, if you run it, we'll see `Promise Failed!` message first, and then `caught`.
+
+If we didn't know about microtasks queue, we could wonder: "Why did `unhandledrejection` handler run? We did catch the error!".
+
+But now we understand that `unhandledrejection` is generated when the microtask queue is complete: the engine examines promises and, if any of them is in "rejected" state, then the event triggers.
+
+In the example above, `.catch` added by `setTimeout` also triggers, but later, after `unhandledrejection` has already occurred, so that doesn't change anything.
+
+## Summary
+
+Promise handling is always asynchronous, as all promise actions pass through the internal "promise jobs" queue, also called "microtask queue" (v8 term).
+
+So, `.then/catch/finally` handlers are always called after the current code is finished.
+
+If we need to guarantee that a piece of code is executed after `.then/catch/finally`, we can add it into a chained `.then` call.
+
+In most Javascript engines, including browsers and Node.js, the concept of microtasks is closely tied with "event loop" and "macrotasks". As these have no direct relation to promises, they are covered in another part of the tutorial, in the chapter .
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
diff --git a/1-js/11-async/08-async-await/01-rewrite-async/solution.md b/1-js/11-async/08-async-await/01-rewrite-async/solution.md
index 5b4b3bc3e3..6d9b339f65 100644
--- a/1-js/11-async/08-async-await/01-rewrite-async/solution.md
+++ b/1-js/11-async/08-async-await/01-rewrite-async/solution.md
@@ -1,5 +1,5 @@
-解析在代码下面:
+The notes are below the code:
```js run
async function loadJson(url) { // (1)
@@ -17,11 +17,11 @@ loadJson('no-such-user.json')
.catch(alert); // Error: 404 (4)
```
-解析:
+Notes:
-1. 将函数 `loadJson` 变为 `async`。
-2. 将所有的 `.then` 替换为 `await`。
-3. 我们也可以不等待,直接 `return response.json()`,像这样:
+1. The function `loadJson` becomes `async`.
+2. All `.then` inside are replaced with `await`.
+3. We can `return response.json()` instead of awaiting for it, like this:
```js
if (response.status == 200) {
@@ -29,6 +29,5 @@ loadJson('no-such-user.json')
}
```
- 然后外部的代码就可以用 `await` 来等待这个 promise 被决议。在本例中可忽略。
-4. `loadJson` 抛出的错误被 `.catch` 处理了。并且我们不能用 `await loadJson(…)`,因为不是在 `async` 函数中。
-
+ Then the outer code would have to `await` for that promise to resolve. In our case it doesn't matter.
+4. The error thrown from `loadJson` is handled by `.catch`. We can't use `await loadJson(…)` there, because we're not in an `async` function.
diff --git a/1-js/11-async/08-async-await/01-rewrite-async/task.md b/1-js/11-async/08-async-await/01-rewrite-async/task.md
deleted file mode 100644
index 018d916340..0000000000
--- a/1-js/11-async/08-async-await/01-rewrite-async/task.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-# 用 async/await 来重写
-
-将 章节一个例子中的 `.then/catch` 重写为 `async/await`:
-
-```js run
-function loadJson(url) {
- return fetch(url)
- .then(response => {
- if (response.status == 200) {
- return response.json();
- } else {
- throw new Error(response.status);
- }
- })
-}
-
-loadJson('no-such-user.json') // (3)
- .catch(alert); // Error: 404
-```
diff --git a/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md b/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md
deleted file mode 100644
index f28f07a4aa..0000000000
--- a/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md
+++ /dev/null
@@ -1,49 +0,0 @@
-
-这里没有什么技巧,只需要将 `demoGithubUser` 中的 `.catch` 替换为 `try...catch`,然后在需要的地方加上 `async/await` 即可:
-
-```js run
-class HttpError extends Error {
- constructor(response) {
- super(`${response.status} for ${response.url}`);
- this.name = 'HttpError';
- this.response = response;
- }
-}
-
-async function loadJson(url) {
- let response = await fetch(url);
- if (response.status == 200) {
- return response.json();
- } else {
- throw new HttpError(response);
- }
-}
-
-// 查询用户名直到 github 返回一个合法的用户
-async function demoGithubUser() {
-
- let user;
- while(true) {
- let name = prompt("Enter a name?", "iliakan");
-
- try {
- user = await loadJson(`https://api.github.com/users/${name}`);
- break; // 没有错误,退出循环
- } catch(err) {
- if (err instanceof HttpError && err.response.status == 404) {
- // 循环将在警告后继续
- alert("No such user, please reenter.");
- } else {
- // 未知错误,rethrow
- throw err;
- }
- }
- }
-
-
- alert(`Full name: ${user.name}.`);
- return user;
-}
-
-demoGithubUser();
-```
diff --git a/1-js/11-async/08-async-await/02-rewrite-async-2/task.md b/1-js/11-async/08-async-await/02-rewrite-async-2/task.md
deleted file mode 100644
index 0de77b6837..0000000000
--- a/1-js/11-async/08-async-await/02-rewrite-async-2/task.md
+++ /dev/null
@@ -1,48 +0,0 @@
-
-# 用 async/await 来重写「rethrow」
-
-下面你可以看到 章节中的「rethrow」例子。让我们来用 `async/await` 来替换 `.then/catch`。
-
-同时我们可以在 `demoGithubUser` 中用循环代替递归:`async/await` 让这将变得更加容易。
-
-```js run
-class HttpError extends Error {
- constructor(response) {
- super(`${response.status} for ${response.url}`);
- this.name = 'HttpError';
- this.response = response;
- }
-}
-
-function loadJson(url) {
- return fetch(url)
- .then(response => {
- if (response.status == 200) {
- return response.json();
- } else {
- throw new HttpError(response);
- }
- })
-}
-
-// 查询用户名直到 github 返回一个合法的用户
-function demoGithubUser() {
- let name = prompt("Enter a name?", "iliakan");
-
- return loadJson(`https://api.github.com/users/${name}`)
- .then(user => {
- alert(`Full name: ${user.name}.`);
- return user;
- })
- .catch(err => {
- if (err instanceof HttpError && err.response.status == 404) {
- alert("No such user, please reenter.");
- return demoGithubUser();
- } else {
- throw err;
- }
- });
-}
-
-demoGithubUser();
-```
diff --git a/1-js/11-async/08-async-await/03-async-from-regular/solution.md b/1-js/11-async/08-async-await/03-async-from-regular/solution.md
index 3da31ca88c..4041dad083 100644
--- a/1-js/11-async/08-async-await/03-async-from-regular/solution.md
+++ b/1-js/11-async/08-async-await/03-async-from-regular/solution.md
@@ -1,7 +1,13 @@
+<<<<<<< HEAD
这个例子告诉我们知道内部是如何运行的会很有帮助。
只需要把 `async` 函数返回值当成 promise,并且在后面加上 `.then` 即可:
+=======
+That's the case when knowing how it works inside is helpful.
+
+Just treat `async` call as promise and attach `.then` to it:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js run
async function wait() {
await new Promise(resolve => setTimeout(resolve, 1000));
@@ -10,7 +16,11 @@ async function wait() {
}
function f() {
+<<<<<<< HEAD
// 一秒后显示 10
+=======
+ // shows 10 after 1 second
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
*!*
wait().then(result => alert(result));
*/!*
diff --git a/1-js/11-async/08-async-await/03-async-from-regular/task.md b/1-js/11-async/08-async-await/03-async-from-regular/task.md
index a1a496f125..2068402c0b 100644
--- a/1-js/11-async/08-async-await/03-async-from-regular/task.md
+++ b/1-js/11-async/08-async-await/03-async-from-regular/task.md
@@ -1,7 +1,11 @@
# Call async from non-async
+<<<<<<< HEAD
我们在一个「普通的」函数中,如何调用另一个 `async` 函数并且拿到返回值?
+=======
+We have a "regular" function. How to call `async` from it and use its result?
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
async function wait() {
@@ -11,6 +15,7 @@ async function wait() {
}
function f() {
+<<<<<<< HEAD
// ...这里怎么写?
// 我们需要调用 async wait() 等待并拿到结果 10
// 记住, 我们不能使用 「await」
@@ -18,3 +23,12 @@ function f() {
```
P.S. 这个任务很简单,但是对于 async/await 新手来说却很常见。
+=======
+ // ...what to write here?
+ // we need to call async wait() and wait to get 10
+ // remember, we can't use "await"
+}
+```
+
+P.S. The task is technically very simple, but the question is quite common for developers new to async/await.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
diff --git a/1-js/11-async/08-async-await/article.md b/1-js/11-async/08-async-await/article.md
index 4bcca25428..5bf244496c 100644
--- a/1-js/11-async/08-async-await/article.md
+++ b/1-js/11-async/08-async-await/article.md
@@ -1,10 +1,10 @@
# Async/await
-「async/await」是一种以更舒适的方式使用 promises 的特殊语法,同时它也更易于理解和使用。
+There's a special syntax to work with promises in a more comfortable fashion, called "async/await". It's surprisingly easy to understand and use.
## Async functions
-让我们以 `async` 这个关键字开始。它可以被放置在一个函数前面,像下面这样:
+Let's start with the `async` keyword. It can be placed before a function, like this:
```js
async function f() {
@@ -12,9 +12,9 @@ async function f() {
}
```
-在函数前面的「async」这个单词表达了一个简单的事情:即这个函数总是返回一个 promise。即使这个函数实际上会返回一个非 promise 的值,函数定义前加上了「async」关键字会指示 JavaScript 引擎自动将返回值包装在一个已决议(resolved)的 promise 内。
+The word "async" before a function means one simple thing: a function always returns a promise. Even If a function actually returns a non-promise value, prepending the function definition with the "async" keyword directs JavaScript to automatically wrap that value in a resolved promise.
-例如,以下的代码就返回了一个以 `1` 为结果的决议后的 promise, 让我们试一下:
+For instance, the code above returns a resolved promise with the result of `1`, let's test it:
```js run
async function f() {
@@ -24,7 +24,7 @@ async function f() {
f().then(alert); // 1
```
-...我们也可以显式返回一个 promise,结果是一样的:
+...We could explicitly return a promise, that would be the same as:
```js run
async function f() {
@@ -34,20 +34,20 @@ async function f() {
f().then(alert); // 1
```
-所以说,`async` 确保了函数的返回值是一个 promise,也会包装非 promise 的值。很简单是吧?但是还没完。还有一个关键字叫 `await`,它只在 `async` 函数中有效,也非常酷。
+So, `async` ensures that the function returns a promise, and wraps non-promises in it. Simple enough, right? But not only that. There's another keyword, `await`, that works only inside `async` functions, and it's pretty cool.
## Await
-语法如下:
+The syntax:
```js
-// 只在 async 函数中有效
+// works only inside async functions
let value = await promise;
```
-关键字 `await` 让 JavaScript 引擎等待直到 promise 完成并返回结果。
+The keyword `await` makes JavaScript wait until that promise settles and returns its result.
-这里的例子就是一个 1 秒后决议的 promise:
+Here's an example with a promise that resolves in 1 second:
```js run
async function f() {
@@ -56,7 +56,7 @@ async function f() {
});
*!*
- let result = await promise; // 等待直到 promise 决议 (*)
+ let result = await promise; // wait till the promise resolves (*)
*/!*
alert(result); // "done!"
@@ -65,14 +65,14 @@ async function f() {
f();
```
-这个函数在执行的时候,「暂停」在了 `(*)` 那一行,并且当 promise 完成后,拿到 `result` 作为结果继续往下执行。所以「done!」是在一秒后显示的。
+The function execution "pauses" at the line `(*)` and resumes when the promise settles, with `result` becoming its result. So the code above shows "done!" in one second.
-划重点:`await` 字面的意思就是让 JavaScript 引擎等待直到 promise 状态完成,然后以完成的结果继续执行。这个行为不会耗费 CPU 资源,因为引擎可以同时处理其他任务:执行其他脚本,处理事件等。
+Let's emphasize: `await` literally makes JavaScript wait until the promise settles, and then go on with the result. That doesn't cost any CPU resources, because the engine can do other jobs meanwhile: execute other scripts, handle events etc.
-相比 `promise.then` 来获取 promise 结果,这只是一个更优雅的语法,同时也更可读和更易书写。
+It's just a more elegant syntax of getting the promise result than `promise.then`, easier to read and write.
-````warn header="不能在普通函数中使用 `await`"
-如果我们尝试在非 async 函数中使用 `await` 的话,就会报语法错误:
+````warn header="Can't use `await` in regular functions"
+If we try to use `await` in non-async function, there would be a syntax error:
```js run
function f() {
@@ -83,32 +83,32 @@ function f() {
}
```
-如果函数前面没有 `async` 关键字,我们就会得到一个语法错误。就像前面说的,`await` 只在 `async 函数` 中有效。
+We will get this error if we do not put `async` before a function. As said, `await` only works inside an `async function`.
````
-让我们拿 那一章的 `showAvatar()` 例子改写成 `async/await` 的形式:
+Let's take the `showAvatar()` example from the chapter and rewrite it using `async/await`:
-1. 用 `await` 替换掉 `.then` 的调用。
-2. 在函数前面加上 `async` 关键字。
+1. We'll need to replace `.then` calls with `await`.
+2. Also we should make the function `async` for them to work.
```js run
async function showAvatar() {
- // 读取 JSON
+ // read our JSON
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
- // 读取 github 用户信息
+ // read github user
let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
let githubUser = await githubResponse.json();
- // 显示头像
+ // show the avatar
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
- // 等待 3 秒
+ // wait 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
@@ -119,18 +119,18 @@ async function showAvatar() {
showAvatar();
```
-简洁明了,是吧?比之前可强多了。
+Pretty clean and easy to read, right? Much better than before.
-````smart header="`await` 不能在顶层代码运行"
-刚开始使用 `await` 的人常常会忘记 `await` 不能用在顶层代码中。如,下面这样就不行:
+````smart header="`await` won't work in the top-level code"
+People who are just starting to use `await` tend to forget the fact that we can't use `await` in top-level code. For example, this will not work:
```js run
-// 用在顶层代码中会报语法错误
+// syntax error in top-level code
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
```
-我们可以将其包裹在一个匿名 async 函数中,如:
+We can wrap it into an anonymous async function, like this:
```js run
(async () => {
@@ -142,10 +142,10 @@ let user = await response.json();
````
-````smart header="`await` 可以接收「thenables」"
-像 `promise.then` 那样,`await` 被允许接收 thenable 对象(具有 `then` 方法的对象)。第三方对象虽然不是 promise,但是却兼容 promise,如果这些对象支持 `.then`,那么就可以对它们使用 `await`。
+````smart header="`await` accepts \"thenables\""
+Like `promise.then`, `await` allows to use thenable objects (those with a callable `then` method). The idea is that a 3rd-party object may not be a promise, but promise-compatible: if it supports `.then`, that's enough to use with `await`.
-下面是一个 `Thenable` 类,`await` 接收了该类的实例:
+Here's a demo `Thenable` class, the `await` below accepts its instances:
```js run
class Thenable {
@@ -154,13 +154,13 @@ class Thenable {
}
then(resolve, reject) {
alert(resolve);
- // 1 秒后决议为 this.num*2
+ // resolve with this.num*2 after 1000ms
setTimeout(() => resolve(this.num * 2), 1000); // (*)
}
};
async function f() {
- // 等待 1 秒, result 变为 2
+ // waits for 1 second, then result becomes 2
let result = await new Thenable(1);
alert(result);
}
@@ -168,11 +168,11 @@ async function f() {
f();
```
-如果 `await` 接收了一个非 promise 的但是提供了 `.then` 方法的对象,它就会调用这个 then 方法,并将原生函数 `resolve`,`reject` 作为参数传入。然后 `await` 等到这两个方法中的某个被调用(在例子中发生在(\*)的那一行),再处理得到的结果。
+If `await` gets a non-promise object with `.then`, it calls that method providing native functions `resolve`, `reject` as arguments. Then `await` waits until one of them is called (in the example above it happens in the line `(*)`) and then proceeds with the result.
````
````smart header="Async methods"
-如果想定义一个 async 的类方法,在方法前面添加 `async` 就可以了:
+To declare an async class method, just prepend it with `async`:
```js run
class Waiter {
@@ -187,14 +187,14 @@ new Waiter()
.wait()
.then(alert); // 1
```
-这里的含义是一样的:它确保了方法的返回值是一个 promise 并且可以在方法中使用 `await`。
+The meaning is the same: it ensures that the returned value is a promise and enables `await`.
````
## Error handling
-如果一个 promise 正常决议,`await promise` 返回的就是其结果。但是如果 promise 被拒绝(rejected),就会抛出一个错误,就像在那一行有个 `throw` 语句那样。
+If a promise resolves normally, then `await promise` returns the result. But in case of a rejection, it throws the error, just as if there were a `throw` statement at that line.
-这里的代码:
+This code:
```js
async function f() {
@@ -204,7 +204,7 @@ async function f() {
}
```
-...和下面是一样的:
+...Is the same as this:
```js
async function f() {
@@ -214,9 +214,9 @@ async function f() {
}
```
-在真实的环境下,promise 被拒绝前通常会等待一段时间。所以 `await` 会等待,然后抛出一个错误。
+In real situations, the promise may take some time before it rejects. So `await` will wait, and then throw an error.
-我们可以用 `try..catch` 来捕获上面的错误,就像对一般的 `throw` 语句那样:
+We can catch that error using `try..catch`, the same way as a regular `throw`:
```js run
async function f() {
@@ -233,7 +233,7 @@ async function f() {
f();
```
-如果有错误发生,代码就会跳到 `catch` 块中。当然也可以用 try 包裹多行 await 代码:
+In case of an error, the control jumps to the `catch` block. We can also wrap multiple lines:
```js run
async function f() {
@@ -242,7 +242,7 @@ async function f() {
let response = await fetch('/no-user-here');
let user = await response.json();
} catch(err) {
- // 捕获到 fetch 和 response.json 中的错误
+ // catches errors both in fetch and response.json
alert(err);
}
}
@@ -250,35 +250,35 @@ async function f() {
f();
```
-如果我们不使用 `try..catch`,由`f()` 产生的 promise 就会被拒绝。我们可以在函数调用后添加 `.catch` 来处理错误:
+If we don't have `try..catch`, then the promise generated by the call of the async function `f()` becomes rejected. We can append `.catch` to handle it:
```js run
async function f() {
let response = await fetch('http://no-such-url');
}
-// f() 变为一个被拒绝的 promise
+// f() becomes a rejected promise
*!*
f().catch(alert); // TypeError: failed to fetch // (*)
*/!*
```
-如果我们忘了添加 `.catch`,我们就会得到一个未处理的 promise 错误(显示在控制台)。我们可以通过在 章节讲的全局事件处理器来捕获这些。
+If we forget to add `.catch` there, then we get an unhandled promise error (viewable in the console). We can catch such errors using a global event handler as described in the chapter .
-```smart header="`async/await` 和 `promise.then/catch`"
-当我们使用 `async/await` 时,几乎就不会用到 `.then` 了,因为为我们 `await` 处理了异步等待。并且我们可以用 `try..catch` 来替代 `.catch`。这通常更加方便(当然不是绝对的)。
+```smart header="`async/await` and `promise.then/catch`"
+When we use `async/await`, we rarely need `.then`, because `await` handles the waiting for us. And we can use a regular `try..catch` instead of `.catch`. That's usually (not always) more convenient.
-但是当我们在顶层代码,外面并没有任何 `async` 函数,我们在语法上就不能使用 `await` 了,所以这时候就可以用 `.then/catch` 来处理结果和异常。
+But at the top level of the code, when we're outside of any `async` function, we're syntactically unable to use `await`, so it's a normal practice to add `.then/catch` to handle the final result or falling-through errors.
-就像上面代码的 `(*)` 那行一样。
+Like in the line `(*)` of the example above.
```
-````smart header="`async/await` 可以和 `Promise.all` 一起使用"
-当我们需要同时等待多个 promise 时,我们可以用 `Promise.all` 来包裹他们,然后使用 `await`:
+````smart header="`async/await` works well with `Promise.all`"
+When we need to wait for multiple promises, we can wrap them in `Promise.all` and then `await`:
```js
-// 等待多个 promise 结果
+// wait for the array of results
let results = await Promise.all([
fetch(url1),
fetch(url2),
@@ -286,50 +286,22 @@ let results = await Promise.all([
]);
```
-如果发生错误,也会正常传递:先从失败的 promise 传到 `Promise.all`,然后变成我们能用 `try..catch` 处理的异常。
+In case of an error, it propagates as usual: from the failed promise to `Promise.all`, and then becomes an exception that we can catch using `try..catch` around the call.
````
-## Microtask queue [#microtask-queue]
+## Summary
-我们在 章节讲过,promise 回调是异步执行的。每个 `.then/catch/finally` 回调首先被放入「微任务队列」然后在当前代码执行完成后被执行。
+The `async` keyword before a function has two effects:
-`Async/await` 是基于 promise 的,所以它内部使用相同的微任务队列,并且相对宏任务来说具有更高的优先级。
+1. Makes it always return a promise.
+2. Allows to use `await` in it.
-例如,看代码:
-- `setTimeout(handler, 0)`,应该以零延迟运行 `handler` 函数。
-- `let x = await f()`,函数 `f()` 是异步的,但是会立即运行。
+The `await` keyword before a promise makes JavaScript wait until that promise settles, and then:
-那么如果 `await` 在 `setTimeout` 下面,哪一个先执行呢?
+1. If it's an error, the exception is generated, same as if `throw error` were called at that very place.
+2. Otherwise, it returns the result, so we can assign it to a value.
-```js run
-async function f() {
- return 1;
-}
-
-(async () => {
- setTimeout(() => alert('timeout'), 0);
-
- await f();
-
- alert('await');
-})();
-```
-
-这里很确定:`await` 总是先完成,因为(作为微任务)它相比 `setTimeout` 具有更高的优先级。
-
-## 总结
-
-函数前面的关键字 `async` 有两个作用:
-
-1. 让这个函数返回一个 promise。
-2. 允许在函数内部使用 `await`。
-
-这个 `await` 关键字又让 JavaScript 引擎等待直到 promise 完成,然后:
-
-1. 如果有错误,就会抛出异常,就像那里有一个 `throw error` 语句一样。
-2. 否则,就返回结果,并赋值。
-
-这两个关键字一起用就提供了一个很棒的方式来控制异步代码,并且易于读写。
+Together they provide a great framework to write asynchronous code that is easy both to read and write.
-有了 `async/await` 我们就几乎不需要使用 `promise.then/catch`,但是不要忘了它们是基于 promise 的,所以在有些时候(如在最外层代码)我们就不得不使用这些方法。再有就是 `Promise.all` 可以帮助我们同时处理多个异步任务。
+With `async/await` we rarely need to write `promise.then/catch`, but we still shouldn't forget that they are based on promises, because sometimes (e.g. in the outermost scope) we have to use these methods. Also `Promise.all` is a nice thing to wait for many tasks simultaneously.
diff --git a/1-js/11-async/08-async-await/head.html b/1-js/11-async/08-async-await/head.html
deleted file mode 100644
index f3457236ef..0000000000
--- a/1-js/11-async/08-async-await/head.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md
index 93a53777d9..702d8b56c0 100644
--- a/1-js/12-generators-iterators/1-generators/article.md
+++ b/1-js/12-generators-iterators/1-generators/article.md
@@ -458,7 +458,11 @@ If we don't catch the error there, then, as usual, it falls through to the outer
## Summary
+<<<<<<< HEAD
- Generators are created by generator functions `function*(…) {…}`.
+=======
+- Generators are created by generator functions `function* f(…) {…}`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
- Inside generators (only) there exists a `yield` operator.
- The outer code and the generator may exchange results via `next/yield` calls.
diff --git a/1-js/12-generators-iterators/2-async-iterators-generators/article.md b/1-js/12-generators-iterators/2-async-iterators-generators/article.md
index 0245a5da1e..c9dcb33492 100644
--- a/1-js/12-generators-iterators/2-async-iterators-generators/article.md
+++ b/1-js/12-generators-iterators/2-async-iterators-generators/article.md
@@ -112,7 +112,11 @@ Here's a small cheatsheet:
| | Iterators | Async iterators |
|-------|-----------|-----------------|
+<<<<<<< HEAD
| Object method to provide iteraterable | `Symbol.iterator` | `Symbol.asyncIterator` |
+=======
+| Object method to provide iterator | `Symbol.iterator` | `Symbol.asyncIterator` |
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
| `next()` return value is | any value | `Promise` |
| to loop, use | `for..of` | `for await..of` |
@@ -130,7 +134,11 @@ That's natural, as it expects to find `Symbol.iterator`, same as `for..of` witho
## Async generators
+<<<<<<< HEAD
As we already know, JavaScript also supprots generators, and they are iterable.
+=======
+As we already know, JavaScript also supports generators, and they are iterable.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
Let's recall a sequence generator from the chapter [](info:generators). It generates a sequence of values from `start` to `end`:
@@ -178,7 +186,11 @@ No problem, just prepend it with `async`, like this:
})();
```
+<<<<<<< HEAD
Now we have an the async generator, iteratable with `for await...of`.
+=======
+Now we have the async generator, iterable with `for await...of`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
It's indeed very simple. We add the `async` keyword, and the generator now can use `await` inside of it, rely on promises and other async functions.
@@ -344,7 +356,11 @@ Syntax differences between async and regular iterators:
| | Iterators | Async iterators |
|-------|-----------|-----------------|
+<<<<<<< HEAD
| Object method to provide iteraterable | `Symbol.iterator` | `Symbol.asyncIterator` |
+=======
+| Object method to provide iterator | `Symbol.iterator` | `Symbol.asyncIterator` |
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
| `next()` return value is | any value | `Promise` |
Syntax differences between async and regular generators:
@@ -356,6 +372,10 @@ Syntax differences between async and regular generators:
In web-development we often meet streams of data, when it flows chunk-by-chunk. For instance, downloading or uploading a big file.
+<<<<<<< HEAD
We can use async generators to process such data, but it's worth to mention that there's also another API called Streams, that provides special interfaces to transform the data and to pass it from one stream to another (e.g. download from one place and immediately send elsewhere).
+=======
+We can use async generators to process such data, but it's worth to mention that there's also another API called Streams, that provides special interfaces to transform the data and to pass it from one stream to another (e.g. download from one place and immediately send elsewhere).
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
Streams API not a part of JavaScript language standard. Streams and async generators complement each other, both are great ways to handle async data flows.
diff --git a/1-js/13-modules/01-modules-intro/article.md b/1-js/13-modules/01-modules-intro/article.md
index c42e74e99a..78151477ff 100755
--- a/1-js/13-modules/01-modules-intro/article.md
+++ b/1-js/13-modules/01-modules-intro/article.md
@@ -1,4 +1,5 @@
+<<<<<<< HEAD
# 模块 (Modules) 简介
当我们的应用日益增大时,我们想要将应用分割成多个文件,即我们所说的“模块”。
@@ -26,6 +27,35 @@
- `import` 关键字允许从其他模块中导入一些诸如函数之类的功能等等。
例如,我们有一个名为 `sayHi.js` 的文件导出一个函数:
+=======
+# Modules, introduction
+
+As our application grows bigger, we want to split it into multiple files, so called 'modules'.
+A module usually contains a class or a library of useful functions.
+
+For a long time, JavaScript existed without a language-level module syntax. That wasn't a problem, because initially scripts were small and simple, so there was no need.
+
+But eventually scripts became more and more complex, so the community invented a variety of ways to organize code into modules, special libraries to load modules on demand.
+
+For instance:
+
+- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- one of the most ancient module systems, initially implemented by the library [require.js](http://requirejs.org/).
+- [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) -- the module system created for Node.js server.
+- [UMD](https://github.com/umdjs/umd) -- one more module system, suggested as a universal one, compatible with AMD and CommonJS.
+
+Now all these slowly become a part of history, but we still can find them in old scripts. The language-level module system appeared in the standard in 2015, gradually evolved since then, and is now supported by all major browsers and in Node.js.
+
+## What is a module?
+
+A module is just a file, a single script, as simple as that.
+
+There are directives `export` and `import` to interchange functionality between modules, call functions of one module from another one:
+
+- `export` keyword labels variables and functions that should be accessible from outside the current module.
+- `import` allows to import functionality from other modules.
+
+For instance, if we have a file `sayHi.js` exporting a function:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
// 📁 sayHi.js
@@ -34,7 +64,11 @@ export function sayHi(user) {
}
```
+<<<<<<< HEAD
然后在其他的文件里导入并使用它:
+=======
+...Then another file may import and use it:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
// 📁 main.js
@@ -44,6 +78,7 @@ alert(sayHi); // function...
sayHi('John'); // Hello, John!
```
+<<<<<<< HEAD
在这个章节里,我们专注于语言本身,但是我们使用浏览器作为演示环境,那么就让我们开始来看看怎么在浏览器中使用模块的。
由于模块使用特殊的关键词和功能,所以我们必须通过使用属性 `
```
+<<<<<<< HEAD
在这里我们可以在浏览器里看到它,但是对于任何模块来说都是一样的。
### 模块级作用域(Module-level scope)
@@ -93,6 +150,31 @@ sayHi('John'); // Hello, John!
```html run
@@ -103,6 +185,7 @@ sayHi('John'); // Hello, John!
```
+<<<<<<< HEAD
如果我们真的需要创建一个窗口级别(window-level)的全局变量,我们可以显式地将它分配给 `window` 并以 `window.user` 来访问它。但是这样做需要你有足够充分的理由,否则就不要这样。
### 模块代码仅在第一次导入时解析
@@ -112,6 +195,17 @@ sayHi('John'); // Hello, John!
这具有很重要的后果。我们来看一下下面的例子:
首先,如果执行一个模块中的代码带来一些副作用,比如显示一个消息,然后多次导入它但是只会显示一次,即第一次:
+=======
+If we really need to make a window-level global variable, we can explicitly assign it to `window` and access as `window.user`. But that's an exception requiring a good reason.
+
+### A module code is evaluated only the first time when imported
+
+If the same module is imported into multiple other places, its code is executed only the first time, then exports are given to all importers.
+
+That has important consequences. Let's see that on examples.
+
+First, if executing a module code brings side-effects, like showing a message, then importing it multiple times will trigger it only once -- the first time:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
// 📁 alert.js
@@ -119,7 +213,11 @@ alert("Module is evaluated!");
```
```js
+<<<<<<< HEAD
// 从不同的文件导入相同模块
+=======
+// Import the same module from different files
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
// 📁 1.js
import `./alert.js`; // Module is evaluated!
@@ -128,11 +226,19 @@ import `./alert.js`; // Module is evaluated!
import `./alert.js`; // (nothing)
```
+<<<<<<< HEAD
在日常开发中,顶级模块主要是用于初始化使用的。我们创建数据结构,预填充它们,如果我们想要可重用某些东西,只要导出即可。
下面是一个高级点的例子:
我们假设一个模块导出了一个对象:
+=======
+In practice, top-level module code is mostly used for initialization. We create data structures, pre-fill them, and if we want something to be reusable -- export it.
+
+Now, a more advanced example.
+
+Let's say, a module exports an object:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
// 📁 admin.js
@@ -141,9 +247,15 @@ export let admin = {
};
```
+<<<<<<< HEAD
如果这个模块被导入到多个文件中,模块仅仅在第一次导入的时候解析创建 `admin` 对象。然后将其传入所有导入的位置。
所有导入位置都得到了唯一的 `admin` 对象。
+=======
+If this module is imported from multiple files, the module is only evaluated the first time, `admin` object is created, and then passed to all further importers.
+
+All importers get exactly the one and only `admin` object:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
// 📁 1.js
@@ -155,6 +267,7 @@ import {admin} from './admin.js';
alert(admin.name); // Pete
*!*
+<<<<<<< HEAD
// 1.js 和 2.js 导入相同的对象
// 1.js 中对对象的修改,在 2.js 中是可访问的
*/!*
@@ -165,6 +278,18 @@ alert(admin.name); // Pete
这种行为对于需要配置的模块来说是非常棒的。我们可以在第一次导入时设置所需要的属性,然后在后面的导入中就可以直接使用了。
例如,下面的 `admin.js` 模块可能提供特定的功能,但是希望在外部可访问 `admin` 对象:
+=======
+// Both 1.js and 2.js imported the same object
+// Changes made in 1.js are visible in 2.js
+*/!*
+```
+
+So, let's reiterate -- the module is executed only once. Exports are generated, and then they are shared between importers, so if something changes the `admin` object, other modules will see that .
+
+Such behavior is great for modules that require configuration. We can set required properties on the first import, and then in further imports it's ready.
+
+For instance, `admin.js` module may provide certain functionality, but expect the credentials to come into the `admin` object from outside:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
// 📁 admin.js
@@ -175,7 +300,11 @@ export function sayHi() {
}
```
+<<<<<<< HEAD
现在,在 `init.js`——我们 app 的第一个脚本中,设置了 `admin.name`。现在每个位置都能看到它了,包括来自 `admin.js` 本身的调用。
+=======
+Now, in `init.js`, the first script of our app, we set `admin.name`. Then everyone will see it, including calls made from inside `admin.js` itself:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
// 📁 init.js
@@ -194,6 +323,7 @@ sayHi(); // Ready to serve, *!*Pete*/!*!
### import.meta
+<<<<<<< HEAD
`import.meta` 对象包含当前模块的一些信息。
它的内容取决于其所在环境,比如说在浏览器环境中,它包含脚本的链接,如果是在 HTML 中的话就是当前页面的链接。
@@ -209,6 +339,23 @@ sayHi(); // Ready to serve, *!*Pete*/!*!
这是一个小功能,但为了完整性,我们应该提到它。
在一个模块中,顶级 `this` 是未定义的,而不是像非模块脚本中的全局变量。
+=======
+The object `import.meta` contains the information about the current module.
+
+Its content depends on the environment. In the browser, it contains the url of the script, or a current webpage url if inside HTML:
+
+```html run height=0
+
+```
+
+### Top-level "this" is undefined
+
+That's kind of a minor feature, but for completeness we should mention it.
+
+In a module, top-level `this` is undefined, as opposed to a global object in non-module scripts:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```html run height=0
```
+<<<<<<< HEAD
## 特定于浏览器的功能
与常规脚本相比,拥有 `type="module"` 标识的脚本有几个特定于浏览器的差异。
@@ -238,10 +386,31 @@ sayHi(); // Ready to serve, *!*Pete*/!*!
它的一个副作用是,模块脚本总是“看见”完全加载的 HTML 页面,包括在它们后面的 HTML 元素。
例如:
+=======
+## Browser-specific features
+
+There are also several browser-specific differences of scripts with `type="module"` compared to regular ones.
+
+You may want skip those for now if you're reading for the first time, or if you don't use JavaScript in a browser.
+
+### Module scripts are deferred
+
+Module scripts are *always* deferred, same effect as `defer` attribute (described in the chapter [](info:script-async-defer)), for both external and inline scripts.
+
+In other words:
+- external module scripts `
+
+Compare to regular script below:
+
+
```
+<<<<<<< HEAD
注意:上面的第二个脚本要先于前一个脚本执行,所以我们先会看到 `undefined`,然后才是 `object`。
这是因为模块脚本被延迟执行了,所以要等到页面加载结束才执行。而普通脚本就没有这个限制了,它会马上执行,所以我们先看到它的输出。
@@ -278,6 +462,27 @@ sayHi(); // Ready to serve, *!*Pete*/!*!
```html
+=======
+Please note: the second script actually works before the first! So we'll see `undefined` first, and then `object`.
+
+That's because modules are deferred, so way wait for the document to be processed. The regular scripts runs immediately, so we saw its output first.
+
+When using modules, we should be aware that HTML-page shows up as it loads, and JavaScript modules run after that, so the user may see the page before the JavaScript application is ready. Some functionality may not work yet. We should put transparent overlays or "loading indicators", or otherwise ensure that the visitor won't be confused by that.
+
+### Async works on inline scripts
+
+Async attribute `
```
+<<<<<<< HEAD
### 外部脚本
外部脚本相较于其他脚本有两个显著的差异:
@@ -292,10 +498,20 @@ sayHi(); // Ready to serve, *!*Pete*/!*!
1. 具有相同 `src` 属性值的外部脚本仅运行一次:
```html
+=======
+### External scripts
+
+There are two notable differences of external module scripts:
+
+1. External scripts with same `src` run only once:
+ ```html
+
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```
+<<<<<<< HEAD
2. 从其他域名获取的外部脚本需要加上 [CORS](mdn:Web/HTTP/CORS) 头。换句话说,如果一个模块脚本是从其他域名获取的,那么它所在的远端服务器必须提供 `Access-Control-Allow-Origin: *`(可能使用加载的域名代替 `*`)响应头以指明当前请求是被允许的。
```html
@@ -320,6 +536,32 @@ import {sayHi} from 'sayHi'; // Error,“裸”模块
### 兼容性,"nomodule"
旧时的浏览器不理解 `type="module"` 值。对于位置类型的脚本会被忽略掉。对于它们来说可以使用 `nomodule` 属性来提供后备:
+=======
+2. External scripts that are fetched from another origin (e.g. another site) require [CORS](mdn:Web/HTTP/CORS) headers, as described in the chapter . In other words, if a module script is fetched from another origin, the remote server must supply a header `Access-Control-Allow-Origin: *` (may use site domain instead of `*`) to indicate that the fetch is allowed.
+ ```html
+
+
+
+ ```
+
+ That ensures better security by default.
+
+### No "bare" modules allowed
+
+In the browser, `import` must get either a relative or absolute URL. Modules without any path are called "bare" modules. Such modules are not allowed in `import`.
+
+For instance, this `import` is invalid:
+```js
+import {sayHi} from 'sayHi'; // Error, "bare" module
+// the module must have a path, e.g. './sayHi.js' or wherever the module is
+```
+
+Certain environments, like Node.js or bundle tools allow bare modules, without any path, as they have own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet.
+
+### Compatibility, "nomodule"
+
+Old browsers do not understand `type="module"`. Scripts of the unknown type are just ignored. For them, it's possible to provide a fallback using `nomodule` attribute:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```html run
```
+<<<<<<< HEAD
如果我们使用打包工具,当脚本被打包进一个单一文件(或者几个文件),在这些脚本中,`import/export` 语句被特殊的打包函数处理后替代。因此最终打包好的脚本不包含任何 `import/export` 语句,它也不需要 `type="module"` 属性,我们仅像普通脚本一样使用就好了:
```html
@@ -377,3 +620,50 @@ import {sayHi} from 'sayHi'; // Error,“裸”模块
在生产环境中,开发者经常基于性能或者其他原因而使用诸如 [Webpack](https://webpack.js.org) 这类的打包工具。
在下一章里,我们将会看到更多关于模块以及如何导入/导出的例子。
+=======
+If we use bundle tools, then as scripts are bundled together into a single file (or few files), `import/export` statements inside those scripts are replaced by special bundler functions. So the resulting "bundled" script does not contain any `import/export`, it doesn't require `type="module"`, and we can put it into a regular script:
+
+```html
+
+
+```
+
+## Build tools
+
+In real-life, browser modules are rarely used in their "raw" form. Usually, we bundle them together with a special tool such as [Webpack](https://webpack.js.org/) and deploy to the production server.
+
+One of the benefits of using bundlers -- they give more control over how modules are resolved, allowing bare modules and much more, like CSS/HTML modules.
+
+Build tools do the following:
+
+1. Take a "main" module, the one intended to be put in `
```
+<<<<<<< HEAD
除非我们自己声明同名变量:
+=======
+The behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), but it is supported mainly for compatibility. The browser tries to help us by mixing namespaces of JS and DOM. Good for very simple scripts, but there may be name conflicts. Also, when we look in JS and don't have HTML in view, it's not obvious where the variable comes from.
+
+If we declare a variable with the same name, it takes precedence:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```html run untrusted height=0
@@ -32,13 +52,17 @@
```
+<<<<<<< HEAD
[在规范中](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem)描述了这种行为,主要是考虑到兼容性才对它进行了支持。为了帮助我们,浏览器尝试了混合 JS 和 DOM 的命名空间。但这仅仅对简单脚本有效,因为它们可能会产生命名冲突。同时,当我们在 JS 中查看时,因为无法在视图中查看 HTML,所以变量的来源可能会很模糊。
选择特殊的方法,才是最好的选择:`document.getElementById(id)`。
+=======
+The better alternative is to use a special method `document.getElementById(id)`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
例如:
@@ -68,6 +92,7 @@
`getElementById` 只能在 `document` 对象上调用。它会在整个文档中查找给定的 `id`。
```
+<<<<<<< HEAD
## getElementsBy*
也有其他的方法来搜索节点:
@@ -166,6 +191,11 @@ document.getElementsByTagName('input')[0].value = 5;
现在将进行重要的内容
`elem.querySelectorAll(css)` 的调用将返回与给定 CSS 选择器匹配 `elem` 中的所有元素。这是最常用和最有力的方法。
+=======
+## querySelectorAll [#querySelectorAll]
+
+By far, the most versatile method, `elem.querySelectorAll(css)` returns all elements inside `elem` matching the given CSS selector.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
我们将查找所有为最后一个子元素的 `
` 元素:
@@ -195,7 +225,6 @@ document.getElementsByTagName('input')[0].value = 5;
CSS 选择器的伪类,如 `:hover` 和 `:active` 都是被支持的。例如,`document.querySelectorAll(':hover')` 将会返回指针现在已经结束的集合(按嵌套顺序:从最外层 `` 到嵌套最多的元素)。
```
-
## querySelector [#querySelector]
调用 `elem.querySelector(css)` 后,它会返回给定 CSS 选择器的第一个元素。
@@ -230,9 +259,13 @@ CSS 选择器的伪类,如 `:hover` 和 `:active` 都是被支持的。例如
## closest
+<<<<<<< HEAD
所有直接在给定元素之上的元素都被称为它的“祖先”。
换句话说,祖先是:父类,父类的父类,它的父类等。祖先们一起组成了从元素到顶端的父类链。
+=======
+*Ancestors* of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
`elem.closest(css)` 方法会查找与 CSS 选择器匹配的最接近的祖先。`elem` 自己也会被搜索。
@@ -260,14 +293,115 @@ CSS 选择器的伪类,如 `:hover` 和 `:active` 都是被支持的。例如
```
+<<<<<<< HEAD
## Live 集合
+=======
+## getElementsBy*
+
+There are also other methods to look for nodes by a tag, class, etc.
+
+Today, they are mostly history, as `querySelector` is more powerful and shorter to write.
+
+So here we cover them mainly for completeness, while you can still find them in the old scripts.
+
+- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags".
+- `elem.getElementsByClassName(className)` returns elements that have the given CSS class.
+- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. very rarely used.
+
+For instance:
+```js
+// get all divs in the document
+let divs = document.getElementsByTagName('div');
+```
+
+Let's find all `input` tags inside the table:
+
+```html run height=50
+
+
+
Your age:
+
+
+
+
+
+
+
+
+
+
+```
+
+```warn header="Don't forget the `\"s\"` letter!"
+Novice developers sometimes forget the letter `"s"`. That is, they try to call `getElementByTagName` instead of getElementsByTagName.
+
+The `"s"` letter is absent in `getElementById`, because it returns a single element. But `getElementsByTagName` returns a collection of elements, so there's `"s"` inside.
+```
+
+````warn header="It returns a collection, not an element!"
+Another widespread novice mistake is to write:
+
+```js
+// doesn't work
+document.getElementsByTagName('input').value = 5;
+```
+
+That won't work, because it takes a *collection* of inputs and assigns the value to it rather than to elements inside it.
+
+We should either iterate over the collection or get an element by its index, and then assign, like this:
+
+```js
+// should work (if there's an input)
+document.getElementsByTagName('input')[0].value = 5;
+```
+````
+
+Looking for `.article` elements:
+
+```html run height=50
+
+
+
+```
+
+## Live collections
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
所有的 `"getElementsBy*"` 方法都会返回 **live** 集合。这类集合总是可以反映出文档的当前状态而且在文档变化时,可以自动更新。
下面的实例中,有两个脚本。
+<<<<<<< HEAD
1. 第一个方法创建了对集合 `
` 的引用。到目前为止,它的长度是 `1`。
2. 第二个脚本在浏览器再遇到一个 `
` 时,它的长度会变成 `2`。
+=======
+1. The first one creates a reference to the collection of `
`. As of now, its length is `1`.
+2. The second scripts runs after the browser meets one more `
`, so its length is `2`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```html run
+<<<<<<< HEAD
请注意,只有在文档 `document.getElementById(...)` 的上下文中才能调用 `getElementById` 和 `getElementsByName`。但元素中没有 `elem.getElementById(...)` 会报错。
也可以在元素上调用其他方法,例如 `elem.querySelectorAll(...)` 将会在 `elem`(在 DOM 子树中)内部进行搜素。
+=======
+By far the most used are `querySelector` and `querySelectorAll`, but `getElementBy*` can be sporadically helpful or found in the old scripts.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
除此以外:
- `elem.matches(css)` 用于检查 `elem` 与给定的 CSS 选择器是否匹配。
- `elem.closest(css)` 用于查找与给定 CSS 选择器相匹配的最近的祖先。`elem` 本身也会被检查。
+<<<<<<< HEAD
最后我们在提一种检查父子关系的方法:
- 如果 `elemB` 在 `elemA`(`elemA` 的后代)中或者当 `elemA==elemB` 时 `elemA.contains(elemB)` 将返回 true。
+=======
+And let's mention one more method here to check for the child-parent relationship, as it's sometimes useful:
+- `elemA.contains(elemB)` returns true if `elemB` is inside `elemA` (a descendant of `elemA`) or when `elemA==elemB`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md
new file mode 100644
index 0000000000..c40a614dfa
--- /dev/null
+++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md
@@ -0,0 +1,29 @@
+我们在 `
` 中使用循环:
+
+```js
+for (let li of document.querySelectorAll('li')) {
+ ...
+}
+```
+
+<<<<<<< HEAD:2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.md
+循环时,我们需要获取每个 `li` 的文本下标。我们可以直接从第一个节点开始读取,这就是文本节点:
+=======
+In the loop we need to get the text inside every `li`.
+
+We can read the text from the first child node of `li`, that is the text node:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7:2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md
+
+```js
+for (let li of document.querySelectorAll('li')) {
+ let title = li.firstChild.data;
+
+ // title is the text in
@@ -51,7 +51,7 @@
"oak": {}
},
"Flowering": {
- "redbud": {},
+ "apple tree": {},
"magnolia": {}
}
}
diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md b/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md
index f6e51da03d..ea0770a787 100644
--- a/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md
+++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md
@@ -21,7 +21,7 @@ let data = {
"oak": {}
},
"Flowering": {
- "redbud": {},
+ "apple tree": {},
"magnolia": {}
}
}
diff --git a/2-ui/1-document/07-modifying-document/article.md b/2-ui/1-document/07-modifying-document/article.md
index e95023075c..5e92172554 100644
--- a/2-ui/1-document/07-modifying-document/article.md
+++ b/2-ui/1-document/07-modifying-document/article.md
@@ -38,7 +38,11 @@ DOM(document object model 文档对象模型,此文中全部以缩写 DOM
这两种方法都可以创建 DOM 节点:
`document.createElement(tag)`
+<<<<<<< HEAD
: 用给定的标签创建一个新元素:
+=======
+: Creates a new *element node* with the given tag:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
let div = document.createElement('div');
@@ -61,13 +65,21 @@ div.className = "alert alert-success";
div.innerHTML = "Hi there! You've read an important message.";
```
+<<<<<<< HEAD
之后,我们就有拥有一个 DOM 元素。现在这个元素已经有一个保存类名的变量和一个保存文字信息的变量,但是在页面上依然看不到我们想要的内容,因为它还没有被插入到页面中。
+=======
+After that, we have our DOM element ready. Right now it is just in a variable and we cannot see it. That is because it's not yet inserted into the page.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
## 插值方法
为了让 `div` 显示我们想要的内容,我们需要在 `document` 中找个合适的位置插值,这里我们选择 `document.body`。
+<<<<<<< HEAD
这里有一个特定的插值方法:`document.body.appendChild(div)`。
+=======
+There's a special method `appendChild` for that: `document.body.appendChild(div)`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
这里是完整代码:
@@ -135,8 +147,13 @@ div.innerHTML = "Hi there! You've read an important message.";
*/!*
```
+<<<<<<< HEAD
如果需要把 `newLi` 插入成为第一个子元素,我们可以这样做:
+=======
+ To insert `newLi` as the first element, we can do it like this:
+
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
list.insertBefore(newLi, list.firstChild);
```
@@ -146,9 +163,15 @@ div.innerHTML = "Hi there! You've read an important message.";
所有这些插入节点的操作都会返回节点。换句话说,`parentElem.appendChild(node)` 返回 `node`。但是通常返回的节点都没有用,只是插入方法的默认返回值。
+<<<<<<< HEAD
以上方法都是“旧三板斧”:它们从很早就存在,我们在老的脚本里能看到它们的影子。很不幸,它们已经没法很好的处理现在的需求了。
例如,我们怎样在 **html** 插入字符串呢?又或者,给定你一个节点,你怎样插入到**节点**之前?虽然也能完成需求开发,总归不是那么优雅的解决方式。
+=======
+These methods are "old school": they exist from the ancient times and we can meet them in many old scripts. Unfortunately, they are not flexible enough.
+
+For instance, how to insert *html* if we have it as a string? Or, given a node, without reference to its parent, how to remove it? Of course, that's doable, but not in an elegant way.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
所以诞生了两种优雅插入方法来代替这些繁琐的插入操作。
@@ -162,7 +185,13 @@ This set of methods provides more flexible insertions:
- `node.after(...nodes or strings)` —— 在 `node` 后面插入节点或者字符串,
- `node.replaceWith(...nodes or strings)` —— 将 `node` 替换为节点或者字符串。
+<<<<<<< HEAD
下面例子是使用以上提到的方法在列表项前面或后面插入文本:
+=======
+All of them accept a list of DOM nodes and/or text strings. If a string is given it's inserted as a text node.
+
+Here's an example of using these methods to add more items to a list and the text before/after it:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```html autorun
@@ -236,6 +265,7 @@ after
接下来登场的这个方法就可以做到:`elem.insertAdjacentHTML(where, html)`。
+<<<<<<< HEAD
该方法第一个参数是字符串,指定插值的位置,必须是以下四个值之一:
- `"beforebegin"` —— 在 `elem` 开头位置前插入 `html`,
@@ -244,6 +274,16 @@ after
- `"afterend"` —— 在 `elem` 结束位置后插入 `html`。
第二个参数是 HTML 字符串,会作为标签插入到页面中。
+=======
+The first parameter is a code word, specifying where to insert relative to `elem`. Must be one of the following:
+
+- `"beforebegin"` -- insert `html` immediately before `elem`,
+- `"afterbegin"` -- insert `html` into `elem`, at the beginning,
+- `"beforeend"` -- insert `html` into `elem`, at the end,
+- `"afterend"` -- insert `html` immediately after `elem`.
+
+The second parameter is an HTML string, that is inserted "as HTML".
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
例如:
@@ -273,10 +313,17 @@ after
这个方法还有两个变种:
+<<<<<<< HEAD
- `elem.insertAdjacentText(where, text)` —— 一样的语法,只不过把 `text` 作为“文本”直接插入到 HTML 中,
- `elem.insertAdjacentElement(where, elem)` —— 一样的语法,只不过插入的是一个元素。
他们存在的意义更多是为了使语法“整齐划一”,在实践中,通常只使用 `insertAdjacentHTML`,因为插入文本和元素的方法可以使用 `append/prepend/before/after` —— 同样的效果这样写起来更简洁。
+=======
+- `elem.insertAdjacentText(where, text)` -- the same syntax, but a string of `text` is inserted "as text" instead of HTML,
+- `elem.insertAdjacentElement(where, elem)` -- the same syntax, but inserts an element.
+
+They exist mainly to make the syntax "uniform". In practice, only `insertAdjacentHTML` is used most of the time. Because for elements and text, we have methods `append/prepend/before/after` -- they are shorter to write and can insert nodes/text pieces.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
这里有一个展示一条信息的变种写法:
@@ -335,13 +382,89 @@ after
```
+<<<<<<< HEAD
## 移除
+=======
+
+## DocumentFragment [#document-fragment]
+
+`DocumentFragment` is a special DOM node that serves as a wrapper to pass around lists of nodes.
+
+We can append other nodes to it, but when we insert it somewhere, then its content is inserted instead.
+
+For example, `getListContent` below generates a fragment with `
` items, that are later inserted into `
`:
+
+```html run
+
+
+
+```
+
+Please note, at the last line `(*)` we append `DocumentFragment`, but it "blends in", so the resulting structure will be:
+
+```html
+
+
1
+
2
+
3
+
+```
+
+`DocumentFragment` is rarely used explicitly. Why append to a special kind of node, if we can return an array of nodes instead? Rewritten example:
+
+```html run
+
+
+
+```
+
+We mention `DocumentFragment` mainly because there are some concepts on top of it, like [template](info:template-element) element, that we'll cover much later.
+
+
+## Removal methods
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
想要移除节点,可以通过以下方法:
`parentElem.removeChild(node)`
+<<<<<<< HEAD
: 从 `parentElem` 中移除 `elem`(假设它是元素中的子元素)。
+=======
+: Removes `node` from `parentElem` (assuming it's a child).
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
`node.remove()`
: 从当前位置移除 `node`。
@@ -409,7 +532,11 @@ after
调动 `document.write(html)` 时意味着将 `html` “就地并马上”放入到页面中。`html` 字符串会动态的创建,所以它以自动伸缩的方式放入到页面中。我们可以通过 JavaScript 创建一个完整的 HTML 页面并写入浏览器窗口中。
+<<<<<<< HEAD
这个方法的起源于没有 DOM,没有 Web 标准的上古时期……,但是这个方法依旧保留了下来,因为很多的脚本使用它来实现一些功能。
+=======
+The method comes from times when there was no DOM, no standards... Really old times. It still lives, because there are scripts using it.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
现代的脚本已经很少再看到这个方法,因为使用它有一个很重要的局限性:
@@ -434,7 +561,11 @@ after
这是它的缺陷。
+<<<<<<< HEAD
从技术上讲,当调用 `document.write`,如果浏览器仍然在解析 HTML,该方法会添加一些内容,浏览器会把添加进来的内容替换掉原来接收到内容,解析后展示在窗口中。
+=======
+Technically, when `document.write` is called while the browser is reading ("parsing") incoming HTML, and it writes something, the browser consumes it just as it were initially there, in the HTML text.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
反过来说这也是一个优势 —— 它性能出奇的快,因为它不用**修改 DOM 结构**。它直接在 DOM 结构构建之前,对整个页面直接进行重写,再交给浏览器去构建 DOM 结构。
diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after.png b/2-ui/1-document/07-modifying-document/before-prepend-append-after.png
index 5bff84d850..858056f72e 100644
Binary files a/2-ui/1-document/07-modifying-document/before-prepend-append-after.png and b/2-ui/1-document/07-modifying-document/before-prepend-append-after.png differ
diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png b/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png
index 44c369ee66..9d68943147 100644
Binary files a/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png and b/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png differ
diff --git a/2-ui/1-document/07-modifying-document/insert-adjacent.png b/2-ui/1-document/07-modifying-document/insert-adjacent.png
index 08063bc517..7cf2f5973c 100644
Binary files a/2-ui/1-document/07-modifying-document/insert-adjacent.png and b/2-ui/1-document/07-modifying-document/insert-adjacent.png differ
diff --git a/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png b/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png
index 60333ad1bb..fcb14017ae 100644
Binary files a/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png and b/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png differ
diff --git a/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md b/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md
index ccc7a01dbf..8cf3fbbb77 100644
--- a/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md
+++ b/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md
@@ -4,7 +4,11 @@ importance: 5
# 创建通知
+<<<<<<< HEAD
编写 `showNotification(options)` 通知函数:`
` 包含给定内容。通知应该在 1.5 秒后自动消失。
+=======
+Write a function `showNotification(options)` that creates a notification: `
` with the given content. The notification should automatically disappear after 1.5 seconds.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
参数:
diff --git a/2-ui/1-document/08-styles-and-classes/article.md b/2-ui/1-document/08-styles-and-classes/article.md
index ee8d93109b..abb1f9cd0c 100644
--- a/2-ui/1-document/08-styles-and-classes/article.md
+++ b/2-ui/1-document/08-styles-and-classes/article.md
@@ -1,6 +1,10 @@
# 样式和类
+<<<<<<< HEAD
在我们讨论 JavaScript 处理样式和类的方法之前 —— 有一个重要的规则。尽管这足够明显的,但我们还是要提到这一点。
+=======
+Before we get into JavaScript's ways of dealing with styles and classes -- here's an important rule. Hopefully it's obvious enough, but we still have to mention it.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
通常有两种方式来设计元素样式:
@@ -65,11 +69,19 @@ elem.style.top = top; // e.g '456px'
`classList` 方法:
+<<<<<<< HEAD
- `elem.classList.add/remove("class")` —— 添加/移除类。
- `elem.classList.toggle("class")` —— 如果类存在就移除,否则添加。
- `elem.classList.contains("class")` —— 返回 `true/false`,检查给定类。
此外,`classList` 是可迭代的,因此我们可以像下述方法一样列出所有类:
+=======
+- `elem.classList.add/remove("class")` -- adds/removes the class.
+- `elem.classList.toggle("class")` -- adds the class if it doesn't exist, otherwise removes it.
+- `elem.classList.contains("class")` -- returns `true/false`, checks for the given class.
+
+Besides, `classList` is iterable, so we can list all classes with `for..of`, like this:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```html run
@@ -116,7 +128,11 @@ button.style.WebkitBorderRadius = '5px';
例如,为了隐藏一个元素,我们可以设置 `elem.style.display = "none"`。
+<<<<<<< HEAD
然后,我们可能要移除 `style.display`,就像它没有被设置一样。这里不应该使用 `delete elem.style.display`,而应该使用 `elem.style.display = ""` 将其赋值为空。
+=======
+Then later we may want to remove the `style.display` as if it were not set. Instead of `delete elem.style.display` we should assign an empty string to it: `elem.style.display = ""`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js run
// if we run this code, the "blinks"
@@ -125,7 +141,11 @@ document.body.style.display = "none"; // hide
setTimeout(() => document.body.style.display = "", 1000); // 恢复正常
```
+<<<<<<< HEAD
如果我们设置 `display` 为空字符串,那么浏览器一般会应用 CSS 类以及内置样式,就像根本没有这样的 `style` 属性。
+=======
+If we set `display` to an empty string, then the browser applies CSS classes and its built-in styles normally, as if there were no such `display` property at all.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
````smart header="用 `style.cssText` 进行重写"
通常,我们使用 `style.*` 来分配单独的样式属性。我们不能将完整的样式设置为 `div.style="color: red; width: 100px"`,因为 `div.style` 是一个对象,而且它是只读的。
@@ -147,7 +167,11 @@ setTimeout(() => document.body.style.display = "", 1000); // 恢复正常
```
+<<<<<<< HEAD
我们很少使用它,因为这样的赋值会删除所有现有样式:它不会添加,而是替换它们。偶尔会移出所需的东西。但是当我们知道我们不移出一些重要的内容时,仍然可以对新元素进行处理。
+=======
+This property is rarely used, because such assignment removes all existing styles: it does not add, but replaces them. May occasionally delete something needed. But we can safely use it for new elements, when we know we won't delete an existing style.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
通过设置属性:`div.setAttribute('style', 'color: red...')` 也可以实现同样的目的。
````
@@ -207,7 +231,11 @@ setTimeout(() => document.body.style.display = "", 1000); // 恢复正常
```
+<<<<<<< HEAD
...但如果我们需要,比如说,把边距增加 20px 呢?那么需要当前值作为开始。
+=======
+...But what if we need, say, to increase the margin by 20px? We would want the current value of it.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
还有另一种方法:`getComputedStyle`。
@@ -281,7 +309,11 @@ pseudo
但 `getComputedStyle` 不允许访问该颜色,否则任意页面都可以通过在页面上创建连接并通过检查样式来确定用户是否访问了连接。
+<<<<<<< HEAD
JavaScript 中我们看不到 `:visited` 应用的样式。此外,CSS 中也有一个限制,禁止在 `:visited` 中应用更改几何的样式。这是为了保证一个不好的页面没有办法来测试是否访问了链接,从而窥探隐私。
+=======
+JavaScript may not see the styles applied by `:visited`. And also, there's a limitation in CSS that forbids to apply geometry-changing styles in `:visited`. That's to guarantee that there's no sideway for an evil page to test if a link was visited and hence to break the privacy.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```
## 总结
diff --git a/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md b/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md
index 82ba162df7..b0259c8482 100644
--- a/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md
+++ b/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md
@@ -6,6 +6,10 @@ importance: 3
编写返回标准滚动条宽度的代码。
+<<<<<<< HEAD
对于Windows,它通常在 `12px` 和 `20px` 之间变化。如果浏览器不为它保留任何空间,那么它可能是 `0px`。
+=======
+For Windows it usually varies between `12px` and `20px`. If the browser doesn't reserve any space for it (the scrollbar is half-translucent over the text, also happens), then it may be `0px`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
P.S. 代码应该适用于任何HTML文档,且不依赖于它的内容元素。
diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png
index 098f9eb6d5..67b9019a04 100644
Binary files a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png and b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png
index 3ecb293753..cdd679855e 100644
Binary files a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png and b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md
index 87a501baac..f22880b8ec 100644
--- a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md
+++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md
@@ -34,9 +34,13 @@ ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'p
当浏览器还不知道图片的宽/高(图片的尺寸可能来自标签属性或 CSS)的时候它会假设它们的尺寸为 `0`直到图片加载完成。
+<<<<<<< HEAD
实际使用过程中,浏览器会在图片第一次加载完成后缓存该图片,方便下次再次访问时立即显示图片。
但是在第一次加载时 `ball.offsetWidth` 的值为 `0`,这会导致错误的坐标出现。
+=======
+After the first load browser usually caches the image, and on next loads it will have the size immediately. But on the first load the value of `ball.offsetWidth` is `0`. That leads to wrong coordinates.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
此时我们应该为 `` 添加 `width/height` 属性:
diff --git a/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md b/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md
index 2e3860fc85..df5daf4ec2 100644
--- a/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md
+++ b/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md
@@ -1,6 +1,13 @@
不同点:
+<<<<<<< HEAD
1. `clientWidth` 值是数值,然而 `getComputedStyle(elem).width` 返回一个包含 `px` 的字符串。
2. `getComputedStyle` 可能返回非数值的结果,例如内联元素的 `"auto"`。
3. `clientWidth` 是元素的内部内容区域加上内间距,而 CSS 宽度(具有标准的 `box-sizing`)是内部**不包括内间距**的空间区域。
4. 如果有一个滚动条,一般浏览器保留它的空间,有的浏览器从 CSS 宽度中减去这个空间(因为它不再用于内容),而有些则不这样做。`clientWidth` 属性总是相同的:如果保留了滚动条,那么它的宽度将被删去。
+=======
+1. `clientWidth` is numeric, while `getComputedStyle(elem).width` returns a string with `px` at the end.
+2. `getComputedStyle` may return non-numeric width like `"auto"` for an inline element.
+3. `clientWidth` is the inner content area of the element plus paddings, while CSS width (with standard `box-sizing`) is the inner content area *without paddings*.
+4. If there's a scrollbar and the browser reserves the space for it, some browser substract that space from CSS width (cause it's not available for content any more), and some do not. The `clientWidth` property is always the same: scrollbar size is substracted if reserved.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
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 79683bbbba..b01b1ce4bf 100644
--- a/2-ui/1-document/09-size-and-scroll/article.md
+++ b/2-ui/1-document/09-size-and-scroll/article.md
@@ -38,12 +38,20 @@ JavaScript 中存在许多属性让我们能够读取元素的宽度、高度或
因此,没有滚动条时,内容宽度将是 `300 px`,但是如果滚动条宽度是 `16px`(不同的设备和浏览器,宽度可能会不一样),那么还剩下 `300 - 16=284px`,这是我们必须考虑到的事。这就是为什么本章的例子总是假设有滚动条的原因。如果没有滚动条,那么事情就会更简单一些。
```
+<<<<<<< HEAD
```smart header="文本可能会溢出到 `padding-bottom` 中"
内填充通常在插图中显示的是空的,但是如果元素中有很多文本,并且溢出,那么浏览器在 `padding-bottom` 中显示“溢出”文本,这可以在示例中看到。但是填充物仍然存在,除非另有说明。
+=======
+```smart header="The `padding-bottom` area may be filled with text"
+Usually paddings are shown empty on illustrations, but if there's a lot of text in the element and it overflows, then browsers show the "overflowing" text at `padding-bottom`.
+
+That's a note to avoid confusion, as `padding-bottom` is set in further examples, unless explicitly specified otherwise.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```
## 几何学
+<<<<<<< HEAD
提供宽度、高度和其他几何形状的元素属性通过数值来计算。它们被假定为像素。
以下是显示总体情况的图片:
@@ -51,6 +59,15 @@ JavaScript 中存在许多属性让我们能够读取元素的宽度、高度或

它们有很多属性,很难将它们全部放在单个图片中,但是它们的值很简单,容易理解。
+=======
+Here's the overall picture:
+
+
+
+Values of these properties are technically numbers, but these numbers are "of pixels", so these are pixel measurements.
+
+They are many properties, it's difficult to fit them all in the single picture, but their values are simple and easy to understand.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
让我们从元素的外部开始探索它们。
@@ -58,6 +75,7 @@ JavaScript 中存在许多属性让我们能够读取元素的宽度、高度或
这些属性很少出现,但它们仍然是“最外层”的几何属性,所以我们将从它们开始。
+<<<<<<< HEAD
`offsetParent` 是最近的祖先元素:
1. CSS 定位(`position` 为 `absolute`、`relative` 或 `fixed`),
@@ -67,6 +85,19 @@ JavaScript 中存在许多属性让我们能够读取元素的宽度、高度或
在大多数实际情况下,我们可以使用 `offsetParent` 来获得最近的 CSS 定位祖先。`offsetLeft/offsetTop` 提供相对于元素左上角的 x/y 坐标。
在下面的例子中,内部 `
` 有 `` 作为 `offsetParent`,并且 `offsetLeft/offsetTop` 让它从左上角位移(`180`):
+=======
+The `offsetParent` is the nearest ancestor, that browser uses for calculating coordinates during rendering.
+
+That's the nearest ancestor, that satisfies following conditions:
+
+1. CSS-positioned (`position` is `absolute`, `relative`, `fixed` or `sticky`),
+2. or `
`, `
`, `
`,
+2. or ``.
+
+Properties `offsetLeft/offsetTop` provide x/y coordinates relative to its upper-left corner.
+
+In the example below the inner `
` has `` as `offsetParent` and `offsetLeft/offsetTop` shifts from its upper-left corner (`180`):
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```html run height=10
@@ -103,12 +134,21 @@ JavaScript 中存在许多属性让我们能够读取元素的宽度、高度或
- `offsetWidth = 390` — 外部宽度,计算方法是内部 css 宽度(`300px`)加上内填充(`2 * 20px`)和边框宽度(`2 * 25px`)。
- `offsetHeight = 290` — 外部高度。
+<<<<<<< HEAD
````smart header="未显示的几何元素的属性值为 0/null"
几何属性仅为显示出来的元素计算。
如果元素(或其任何祖先)在文档中显示为 `display:none` 或本身不在文档中,则所有几何属性都是 0 或者值为 `null`,这取决于它是什么。
例如,`offsetParent` 为 `null`,并且 `offsetWidth`,`offsetHeight` 为 `0`。
+=======
+````smart header="Geometry properties are zero/null for elements that are not displayed"
+Geometry properties are calculated only for displayed elements.
+
+If an element (or any of its ancestors) has `display:none` or is not in the document, then all geometry properties are zero (or `null` if that's `offsetParent`).
+
+For example, `offsetParent` is `null`, and `offsetWidth`, `offsetHeight` are `0` when we created an element, but haven't inserted it into the document yet, or it (or it's ancestor) has `display:none`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
我们可以用它来检查一个元素是否被隐藏,像这样:
@@ -134,7 +174,11 @@ function isHidden(elem) {

+<<<<<<< HEAD
...但确切地说,它们不是边框,而是内侧与外侧的相对坐标。
+=======
+...But to be precise -- these properties are not border width/height, but rather relative coordinates of the inner side from the outer side.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
有什么区别?
@@ -201,8 +245,13 @@ element.style.height = `${element.scrollHeight}px`;
换种说法,`scrollTop` 就是 “滚动了多少” 的意思。
+<<<<<<< HEAD
````smart header="`scrollLeft/scrollTop` 可修改 "
大多数几何属性是只读的,但是 `scrollLeft/scrollTop` 可以改变,浏览器将会直接滚动元素。
+=======
+````smart header="`scrollLeft/scrollTop` can be modified"
+Most of the geometry properties here are read-only, but `scrollLeft/scrollTop` can be changed, and the browser will scroll the element.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```online
如果单击下面的元素,代码 `elem.scrollTop += 10` 将会执行,这使得元素向下滚动 `10px`。
@@ -215,11 +264,19 @@ element.style.height = `${element.scrollHeight}px`;
## 不要从 CSS 中获取宽高
+<<<<<<< HEAD
我们刚刚介绍了 DOM 元素的几何属性。它们通常用于获得宽度、高度和计算距离。
+=======
+We've just covered geometry properties of DOM elements, that can be used to get widths, heights and calculate distances.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
但是,正如我们从《信息:样式和类》一章所知道的,我们可以使用 `getComputedStyle` 来读取CSS的高度和宽度。
+<<<<<<< HEAD
那么为什么不像这样读取一个元素的高度呢?
+=======
+So why not to read the width of an element with `getComputedStyle`, like this?
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js run
let elem = document.body;
@@ -264,6 +321,7 @@ alert( getComputedStyle(elem).width ); // show CSS width for elem
元素具有以下几何属性:
+<<<<<<< HEAD
- `offsetParent` — 是最近的有定位属性的祖先元素,或者是 `td`、`th`、`table`、`body`。
- `offsetLeft/offsetTop` — 是相对于 `offsetParent` 的左上角边缘坐标。
- `offsetWidth/offsetHeight` — 元素的“外部”宽/高 ,边框尺寸计算在内。
@@ -271,5 +329,14 @@ alert( getComputedStyle(elem).width ); // show CSS width for elem
- `clientWidth/clientHeight` — 内容的宽度/高度,包括内间距,但没有滚动条。
- `scrollWidth/scrollHeight` — 内容的宽度/高度,包括可滚动的可视区域外的尺寸,也包括内间距,但不包括滚动条。
- `scrollLeft/scrollTop` — 从左上角开始的元素的滚动部分的宽度/高度。
+=======
+- `offsetParent` -- is the nearest positioned ancestor or `td`, `th`, `table`, `body`.
+- `offsetLeft/offsetTop` -- coordinates relative to the upper-left edge of `offsetParent`.
+- `offsetWidth/offsetHeight` -- "outer" width/height of an element including borders.
+- `clientLeft/clientTop` -- the distance from the upper-left outer corner to its upper-left inner corner. For left-to-right OS they are always the widths of left/top borders. For right-to-left OS the vertical scrollbar is on the left so `clientLeft` includes its width too.
+- `clientWidth/clientHeight` -- the width/height of the content including paddings, but without the scrollbar.
+- `scrollWidth/scrollHeight` -- the width/height of the content, just like `clientWidth/clientHeight`, but also include scrolled-out, invisible part of the element.
+- `scrollLeft/scrollTop` -- width/height of the scrolled out upper part of the element, starting from its upper-left corner.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
除了 `scrollLeft/scrollTop` 之外,所有属性都是只读的。如果更改,浏览器会使元素滚动。
diff --git a/2-ui/1-document/09-size-and-scroll/metric-all.png b/2-ui/1-document/09-size-and-scroll/metric-all.png
index 6deec90821..6060b9890b 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-all.png and b/2-ui/1-document/09-size-and-scroll/metric-all.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-all@2x.png b/2-ui/1-document/09-size-and-scroll/metric-all@2x.png
index e084c257b1..3c53be1dd6 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-all@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-all@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png
index ffb10aa1bd..efcc9440b5 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png and b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png
index 48510e6e09..9b7edcf572 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png
index 33faf9be7d..787557205a 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png and b/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png
index a8e170eabf..8034c7aff9 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png
index ed44652d89..d50d1186ff 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png and b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png
index f31f483e46..76017b9ee6 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png
index a183c6b384..173bf1529b 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png and b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png
index 5a73f87a97..2d145d77fc 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-css.png b/2-ui/1-document/09-size-and-scroll/metric-css.png
index 564c210a99..5579b4abdb 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-css.png and b/2-ui/1-document/09-size-and-scroll/metric-css.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-css@2x.png b/2-ui/1-document/09-size-and-scroll/metric-css@2x.png
index e4fd7bb878..c4df7706b2 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-css@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-css@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png
index 01d2d00087..059ef13efa 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png
index 125a91e153..0f86b4f858 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png
index a13075f03a..82551b8e1c 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png
index 7bfb24c221..805687cd6b 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png
index 4bc795aa0e..83f410837c 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png and b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png
index bf0cf711a9..1ef15502e7 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png
index 581c214437..54e6b96344 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png and b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png differ
diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png
index 1e2a20f546..d57c5c198f 100644
Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png differ
diff --git a/2-ui/1-document/10-size-and-scroll-window/article.md b/2-ui/1-document/10-size-and-scroll-window/article.md
index c3902788a8..7233df9302 100644
--- a/2-ui/1-document/10-size-and-scroll-window/article.md
+++ b/2-ui/1-document/10-size-and-scroll-window/article.md
@@ -1,6 +1,10 @@
# Window 的尺寸和滚动
+<<<<<<< HEAD
怎么找到浏览器窗口的宽度呢?如何获得包括滚动窗口外部分的文档总高度呢?如何通过 JavaScript 滚动页面呢?
+=======
+How to find out the width and height of the browser window? How to get the full width and height of the document, including the scrolled out part? How to scroll the page using JavaScript?
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
在 DOM 里,文档的根元素是 `document.documentElement`。与之相对应的标签是 ``,同时它具备了[前一章](info:size-and-scroll)的几何属性。只有在某些情景下,我们可以使用到它,但还有其他方法和特性需要我们去特别考虑的。
@@ -17,11 +21,19 @@
```
````warn header="Not `window.innerWidth/Height`"
+<<<<<<< HEAD
浏览器也支持属性 `window.innerWidth/innerHeight`。它们看起来都是我们想要的。那么它们有什么不同呢?
在滚动条占用一部分空间的情况下,`clientWidth/clientHeight` 提供了它内部空间的宽度/高度。换言之,它们返回文档可见部分(可用内容)的宽高。
而 `window.innerWidth/innerHeight` 是忽略滚动条。
+=======
+Browsers also support properties `window.innerWidth/innerHeight`. They look like what we want. So why not to use them instead?
+
+If there exists a scrollbar, and it occupies some space, `clientWidth/clientHeight` provide the width/height without it (subtract it). In other words, they return width/height of the visible part of the document, available for the content.
+
+...And `window.innerWidth/innerHeight` ignore the scrollbar.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
如果有一条滚动条,它占用了一些空间,那么这两行代码会得到不同的值:
```js run
@@ -43,9 +55,15 @@ alert( document.documentElement.clientWidth ); // 窗口减去滚动条的宽度
理论上,文档元素的宽高是 `documentElement.clientWidth/Height`,并且包含了全部内容,我们可以依据 `documentElement.scrollWidth/scrollHeight` 测量它的最大值。
+<<<<<<< HEAD
这几个属性对常规元素能起作用,但面对整个页面它们会失去它们应有的作用了。在 Chrome/Safari/Opera 浏览器,如果这里没有滚动条,`documentElement.scrollHeight` 甚至比 `documentElement.clientHeight` 小!这对常规元素来讲是不可能出现的情况。
要获得可靠的窗口大小,我们应该采用这些属性的最大值:
+=======
+These properties work well for regular elements. But for the whole page these properties do not work as intended. In Chrome/Safari/Opera if there's no scroll, then `documentElement.scrollHeight` may be even less than `documentElement.clientHeight`! Sounds like a nonsense, weird, right?
+
+To reliably obtain the full document height, we should take the maximum of these properties:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js run
let scrollHeight = Math.max(
@@ -61,11 +79,19 @@ alert('Full document height, with scrolled out part: ' + scrollHeight);
## 得到当前滚动 [#page-scroll]
+<<<<<<< HEAD
常规元素有它们自己的滚动状态 `elem.scrollLeft/scrollTop`。
那页面的呢?差不多所有浏览器为文档滚动提供了 `documentElement.scrollLeft/Top`,但是 Chrome/Safari/Opera 有 bugs(像 [157855](https://code.google.com/p/chromium/issues/detail?id=157855),[106133](https://bugs.webkit.org/show_bug.cgi?id=106133))我们应该用`document.body` 代替`document.documentElement`
幸运的是,由于特殊属性 `window.pageXOffset/pageYOffset`,我们根本不必记住这些特性:
+=======
+DOM elements have their current scroll state in `elem.scrollLeft/scrollTop`.
+
+For document scroll `document.documentElement.scrollLeft/Top` works in most browsers, except oldler WebKit-based ones, like Safari (bug [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), where we should use `document.body` instead of `document.documentElement` there.
+
+Luckily, we don't have to remember these peculiarities at all, because the scroll is available in the special properties `window.pageXOffset/pageYOffset`:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js run
alert('Current scroll from the top: ' + window.pageYOffset);
@@ -84,11 +110,19 @@ alert('Current scroll from the left: ' + window.pageXOffset);
可以通过更改 `scrollTop/scrollLeft` 来滚动常规元素。
+<<<<<<< HEAD
我们可以对页面执行相同的操作:
- 在 Chrome/Safari/Opera 外的所有浏览器:修改 `document.documentElement.scrollTop/Left`。
- 在 Chrome/Safari/Opera 浏览器:使用 `document.body.scrollTop/Left`。
不好!这个操作虽然能工作了,但似乎在跨浏览器上不兼容。幸运的是,有一个更简单,更通用的解决方案 [window.scrollBy(x,y)](mdn:api/Window/scrollBy) 和 [window.scrollTo(pageX,pageY)](mdn:api/Window/scrollTo)。
+=======
+We can do the same for the page, but as explained above:
+- For most browsers (except older Webkit-based) `document.documentElement.scrollTop/Left` is the right property.
+- Otherwise, `document.body.scrollTop/Left`.
+
+These cross-browser incompatibilities are not good. Fortunately, there's a simpler, universal solution: special methods [window.scrollBy(x,y)](mdn:api/Window/scrollBy) and [window.scrollTo(pageX,pageY)](mdn:api/Window/scrollTo).
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
- 方法 `scrollBy(x,y)` 滚动页面至相对于现在位置的 (x, y) 位置。例如,`scrollBy(0,10)` 页面向下滚动 `10px`。
@@ -97,7 +131,11 @@ alert('Current scroll from the left: ' + window.pageXOffset);
```
+<<<<<<< HEAD
- 方法 `scrollTo(pageX,pageY)` 滚动页面至相对于文档的左上角的 (pageX, pageY) 位置。就好像设置 `scrollLeft/scrollTop`。
+=======
+- The method `scrollTo(pageX,pageY)` scrolls the page to absolute coordinates, so that the top-left corner of the visible part has coordinates `(pageX, pageY)` relative to the document's top-left corner. It's like setting `scrollLeft/scrollTop`.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
回到顶部, 我们可以用 `scrollTo(0,0)`。
@@ -130,7 +168,11 @@ alert('Current scroll from the left: ' + window.pageXOffset);
有时候我们需要让文档禁止滚动。例如,当我们需要用一个被立即关注的消息框覆盖这个文档时,我们希望访问者与这个消息进行交互,而不是与文档进行交互。
+<<<<<<< HEAD
想让这个文档禁止滚动,设置`document.body.style.overflow = "hidden"`就够了。该页面将“冻结”其当前滚动。
+=======
+To make the document unscrollable, it's enough to set `document.body.style.overflow = "hidden"`. The page will freeze on its current scroll.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```online
试一试:
@@ -146,7 +188,11 @@ alert('Current scroll from the left: ' + window.pageXOffset);
这个方法的缺点是会使滚动条消失。如果它占用了一些空间,它原本占用的空间就会空出来,使元素本身的内容“跳”出来填满空出来的空间里。
+<<<<<<< HEAD
这看起来就有点怪怪的。但我们可以解决这个问题:通过对比冻结前后的`clientWidth`如果在 `clientWidth` 增加(滚动条消失)时,将 `padding` 添加到 `document.body` 取代滚动条来保持内容宽度不变。
+=======
+That looks a bit odd, but can be worked around if we compare `clientWidth` before and after the freeze, and if it increased (the scrollbar disappeared) then add `padding` to `document.body` in place of the scrollbar, to keep the content width the same.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
## 总结
diff --git a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png
index 76a45a7a7f..a3cf95f118 100644
Binary files a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png and b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png differ
diff --git a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png
index 249db0edbe..868dc8a266 100644
Binary files a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png and b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png differ
diff --git a/2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md b/2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md
index 66209a3a0d..075a574d36 100644
--- a/2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md
+++ b/2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md
@@ -14,10 +14,17 @@ importance: 5
你的代码应该使用 DOM 来获取到以下窗口坐标:
+<<<<<<< HEAD
1. 左上的外角(这很简单)。
2. 右下的外角(这也挺简单)。
3. 左上的内角(这有点难)。
4. 右下的内角(有几种方式,选择其中一种)。
+=======
+1. Upper-left, outer corner (that's simple).
+2. Bottom-right, outer corner (simple too).
+3. Upper-left, inner corner (a bit harder).
+4. Bottom-right, inner corner (there are several ways, choose one).
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
你计算得到的坐标应该和鼠标单击返回的坐标相同。
diff --git a/2-ui/1-document/11-coordinates/2-position-at/solution.md b/2-ui/1-document/11-coordinates/2-position-at/solution.md
index 0ebe4ccc35..c7ea79fa2e 100644
--- a/2-ui/1-document/11-coordinates/2-position-at/solution.md
+++ b/2-ui/1-document/11-coordinates/2-position-at/solution.md
@@ -1,4 +1,8 @@
+<<<<<<< HEAD
在这个任务中我们只需要精确计算坐标。具体细节可以在代码中查看。
+=======
+In this task we only need to accurately calculate the coordinates. See the code for details.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
请注意:元素必须位于在文档中才能读取 `offsetHeight` 和其它属性。
一个元素如果有隐藏的(`display:none`)样式或者坐标范围超出文档是得不到大小的。
\ No newline at end of file
diff --git a/2-ui/1-document/11-coordinates/2-position-at/task.md b/2-ui/1-document/11-coordinates/2-position-at/task.md
index bbecc21d1f..affe254fea 100644
--- a/2-ui/1-document/11-coordinates/2-position-at/task.md
+++ b/2-ui/1-document/11-coordinates/2-position-at/task.md
@@ -6,7 +6,11 @@ importance: 5
基于 `anchor` 元素的上边(`"top"`),右边(`"right"`)或者底部(`"bottom"`)的 `position`,创建一个 `positionAt(anchor, position, elem)` 函数来定位 `elem` 元素。
+<<<<<<< HEAD
使用这个函数构建一个 `showNote(anchor, position, html)` 函数,它使用类 `"note"` 和文本 `html` 标签在 anchor 位置旁边显示一个元素。
+=======
+Call it inside the function `showNote(anchor, position, html)` that shows an element with the class `"note"` and the text `html` at the given position near the anchor.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
注释像下面这样显示:
diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md
index 07aa70ff2a..3d4fa1c76f 100644
--- a/2-ui/1-document/11-coordinates/article.md
+++ b/2-ui/1-document/11-coordinates/article.md
@@ -11,7 +11,11 @@
## 窗口坐标:getBoundingClientRect
+<<<<<<< HEAD
窗口的坐标是从窗口的左上角开始计算的。
+=======
+Window coordinates start at the upper-left corner of the window.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
`elem.getBoundingClientRect()` 方法返回一个 `elem` 的窗口坐标对象,这个对象有以下这些属性:
@@ -25,7 +29,11 @@

+<<<<<<< HEAD
窗口坐标并不会考虑到文档滚动,它们就是基于窗口的左上角计算出来的。
+=======
+Window coordinates do not take the scrolled out part of the document into account, they are calculated from the window's upper-left corner.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
换句话说,当我们滚动这个页面,这个元素就会上升或者下降,**它的窗口坐标改变了**。这很重要。
@@ -46,6 +54,7 @@ function showRect(elem) {
额外说明:
+<<<<<<< HEAD
- 坐标可以是十进制的分数。这很正常,浏览器内部也是使用十进制分数来计算坐标。当设置元素的 `style.position.left/top` 时我们不需要舍入它们,浏览器可以支持十进制分数。
- 坐标也可以是负数的。例如当我们滚动页面向下在 `elem` 的顶部超过窗口的时候,这时候我们调用 `elem.getBoundingClientRect().top` 返回的就是负数。
- 一些浏览器(像 Chrome)还会在 `getBoundingClientRect` 的返回中增加 `width` 和 `height` 属性。我们可以通过减法计算 `height=bottom-top`,`width=right-left` 来得到这两个属性。
@@ -57,6 +66,18 @@ function showRect(elem) {
但是在 CSS 中 `right` 属性表示的是到右边界的距离,而且 `bottom` 是到底部边界的距离。
如果我们只看下面的图片,我们可以看到在 JavaScript 中并非如此。所有窗口坐标都是从左上角开始计算的,包括这些坐标。
+=======
+- Coordinates may be decimal fractions. That's normal, internally browser uses them for calculations. We don't have to round them when setting to `style.position.left/top`, the browser is fine with fractions.
+- Coordinates may be negative. For instance, if the page is scrolled down and the top `elem` is now above the window. Then, `elem.getBoundingClientRect().top` is negative.
+- Some browsers (like Chrome) provide additional properties, `width` and `height` of the element that invoked the method to `getBoundingClientRect` as the result. We can also get them by subtraction: `height=bottom-top`, `width=right-left`.
+
+```warn header="Coordinates right/bottom are different from CSS properties"
+If we compare window coordinates versus CSS positioning, then there are obvious similarities to `position:fixed`. The positioning of an element is also relative to the viewport.
+
+But in CSS, the `right` property means the distance from the right edge, and the `bottom` property means the distance from the bottom edge.
+
+If we just look at the picture above, we can see that in JavaScript it is not so. All window coordinates are counted from the upper-left corner, including these ones.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```
## elementFromPoint(x, y) [#elementFromPoint]
@@ -154,7 +175,11 @@ setTimeout(() => message.remove(), 5000);
## 文档坐标
+<<<<<<< HEAD
文档相对坐标是从文档的左上角开始计算,而不是窗口。
+=======
+Document-relative coordinates start from the upper-left corner of the document, not the window.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
在 CSS 中,窗口坐标对应的是 `position:fixed`,而文档坐标则类似顶部的 `position:absolute`。
@@ -179,7 +204,11 @@ setTimeout(() => message.remove(), 5000);
## 获取文档坐标 [#getCoords]
+<<<<<<< HEAD
现在 Javascript 中并没有获取一个元素文档坐标的标准方法。但是这个方法写起来很容易。
+=======
+There's no standard method to get the document coordinates of an element. But it's easy to write it.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
两个坐标系可以通过由公式相连接:
- `pageY` = `clientY` + 文档垂直部分滚动的高度。
diff --git a/2-ui/1-document/11-coordinates/coords.png b/2-ui/1-document/11-coordinates/coords.png
index 58fd533fba..79d1fdbb65 100644
Binary files a/2-ui/1-document/11-coordinates/coords.png and b/2-ui/1-document/11-coordinates/coords.png differ
diff --git a/2-ui/1-document/11-coordinates/coords@2x.png b/2-ui/1-document/11-coordinates/coords@2x.png
index c3469f4cde..d3e340f50b 100644
Binary files a/2-ui/1-document/11-coordinates/coords@2x.png and b/2-ui/1-document/11-coordinates/coords@2x.png differ
diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png
index 39ddc31c3c..b4c102b640 100644
Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png and b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png differ
diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png
index 52e555c490..4310522f0b 100644
Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png and b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png differ
diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png b/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png
index 706b42c110..c5102df552 100644
Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png and b/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png differ
diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png b/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png
index 83f31950c0..831ec4a187 100644
Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png and b/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png differ
diff --git a/2-ui/1-document/11-coordinates/head.html b/2-ui/1-document/11-coordinates/head.html
index bba7bec068..bcc06fd926 100644
--- a/2-ui/1-document/11-coordinates/head.html
+++ b/2-ui/1-document/11-coordinates/head.html
@@ -1,9 +1,17 @@
```
@@ -234,7 +234,7 @@ alert(event.clientX); // undefined, the unknown property is ignored!
alert(2);
};
- document.addEventListener('menu-open', () => alert('nested'))
+ document.addEventListener('menu-open', () => alert('nested'));
```
@@ -242,35 +242,51 @@ alert(event.clientX); // undefined, the unknown property is ignored!
这不仅仅是 `dispatchEvent`,还有其他案例。JavaScript 在事件处理时可以调用导致其他事件的方法 —— 它们也是被同步处理的。
+<<<<<<< HEAD
如果我们不喜欢,可以将 `dispatchEvent`(或者其他事件触发器调用)放在 `onclick` 结束,或者如果不方便,可以将其包装在 `setTimeout(...,0)` 中:
+=======
+If we don't like it, we can either put the `dispatchEvent` (or other event-triggering call) at the end of `onclick` or wrap it in zero-delay `setTimeout`:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```html run
```
+<<<<<<< HEAD
## 总结
+=======
+Now `dispatchEvent` runs asynchronously after the current code execution is finished, including `mouse.onclick`, so event handlers are totally separate.
+
+## Summary
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
要生成一个事件,我们首先需要创建一个事件对象。
+<<<<<<< HEAD
泛型 `Event(name, options)` 构造器接受任意事件名,`options` 对象具有两个属性:
- `bubbles: true` ,如果事件应该冒泡的话。
- `cancelable: true` 则 `event.preventDefault()` 应该有效。
+=======
+The generic `Event(name, options)` constructor accepts an arbitrary event name and the `options` object with two properties:
+ - `bubbles: true` if the event should bubble.
+ - `cancelable: true` if the `event.preventDefault()` should work.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
其他像 `MouseEvent`、`KeyboardEvent` 这样的原生事件构造器,接受特定于该事件类型的属性。例如,鼠标事件的 `clientX`。
diff --git a/2-ui/2-events/index.md b/2-ui/2-events/index.md
index 4fd273eaf8..eb10130b84 100644
--- a/2-ui/2-events/index.md
+++ b/2-ui/2-events/index.md
@@ -1,3 +1,7 @@
+<<<<<<< HEAD
# 事件简介
+=======
+# Introduction to Events
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
浏览器事件、事件属性和处理模式简介。
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 6f9ebe1f32..250583361f 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
@@ -175,7 +175,11 @@ Before...
...但有一个潜在的问题!文本变得无法被选中。即使用户从“之前”开始选择,“之后”结束,选择也会跳过“不可选择”部分。我们真的想让文本不可选么?
+<<<<<<< HEAD
大部分时间,我们不会那么做。用户可能有合理的理由来选择文本,以便进行复制或其他需要。如果我们不让他那么做那么可能不太方便。所以这个解决方案没那么好。
+=======
+Most of time, we don't. A user may have valid reasons to select the text, for copying or other needs. That may be inconvenient if we don't allow them to do it. So this solution is not that good.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
我们只是想阻止双击进行选择,仅此而已。
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
index 8fbae4c790..e998165fd2 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
@@ -18,7 +18,7 @@
border: 1px solid #b3c9ce;
border-radius: 4px;
text-align: center;
- font: italic 14px/1.3 arial, sans-serif;
+ font: italic 14px/1.3 sans-serif;
color: #333;
background: #fff;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);
@@ -78,12 +78,13 @@
}
document.onmouseout = function() {
- // it is possible that mouseout triggered, but we're still inside the element (cause of bubbling)
+ // it is possible that mouseout triggered, but we're still inside the element
+ // (its target was inside, and it bubbled)
// but in this case we'll have an immediate mouseover,
// so the tooltip will be destroyed and shown again
//
- // that's an overhead, but here it's not visible
- // can be fixed with additional checks
+ // luckily, the "blinking" won't be visible,
+ // as both events happen almost at the same time
if (tooltip) {
tooltip.remove();
tooltip = false;
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html
index 8abb923d5e..2dc4394e74 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html
@@ -18,7 +18,7 @@
border: 1px solid #b3c9ce;
border-radius: 4px;
text-align: center;
- font: italic 14px/1.3 arial, sans-serif;
+ font: italic 14px/1.3 sans-serif;
color: #333;
background: #fff;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
index a78c7c5554..4e6e2a3e95 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
@@ -101,6 +101,6 @@ class HoverIntent {
elem.removeEventListener('mousemove', this.onMouseMove);
elem.removeEventListener('mouseover', this.onMouseOver);
elem.removeEventListener('mouseout', this.onMouseOut);
- };
+ }
}
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css
index f0736f3e35..fa2f09eba8 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css
@@ -30,7 +30,7 @@ body {
border: 1px solid #b3c9ce;
border-radius: 4px;
text-align: center;
- font: italic 14px/1.3 arial, sans-serif;
+ font: italic 14px/1.3 sans-serif;
color: #333;
background: #fff;
z-index: 100000;
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js
index 29198d8eac..caca940b1d 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js
@@ -45,6 +45,6 @@ class HoverIntent {
destroy() {
/* your code to "disable" the functionality, remove all handlers */
- };
+ }
}
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html
index 702b79f968..856daf3a8f 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html
@@ -1,5 +1,5 @@
-
+
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md
index 0dd55b7ea2..659d069d4a 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md
@@ -6,15 +6,25 @@ importance: 5
编写一个函数,仅在访问者将鼠标**移动**到它上面而非**通过**它时,才会显示元素上的工具提示。
+<<<<<<< HEAD
换句话说,如果访问者把鼠标移动到元素上,然后停下 —— 显示工具提示。如果访问者将鼠标快速移过元素,那就不需要显示,谁想要多余的内容呢?
+=======
+In other words, if the visitor moves the mouse on the element and stopped -- show the tooltip. And if they just moved the mouse through fast, then no need, who wants extra blinking?
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
从技术上说,我们可以测量鼠标在元素上的经过速度,如果速度很慢,那么我们认为它**在元素上**并显示工具提示,如果速度太快 —— 那么我们就忽略它。
为它创建一个通用对象 `new HoverIntent(options)`,加上 `options`:
+<<<<<<< HEAD
- `elem` —— 要跟踪的元素。
- `over` —— 如果鼠标缓慢地移动元素,调用该函数。
- `out` —— 当鼠标离开元素时,调用函数(如果 `over` 被调用过了)。
+=======
+- `elem` -- element to track.
+- `over` -- a function to call if the mouse is slowly moving over the element.
+- `out` -- a function to call when the mouse leaves the element (if `over` was called).
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
在工具提示中使用此类对象的示例:
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md
index ec2edbb6b2..e536ab9ef0 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md
@@ -10,15 +10,29 @@
这些事件很特别,因为它们有 `relatedTarget`。
+<<<<<<< HEAD
对于 `mouseover`:
- `event.target` —— 是鼠标经过的那个元素。
- `event.relatedTarget` —— 是鼠标上一次经过的元素。
+=======
+This property complements `target`. When a mouse leaves one element for another, one of them becomes `target`, and the other one `relatedTarget`.
+
+For `mouseover`:
+
+- `event.target` -- is the element where the mouse came over.
+- `event.relatedTarget` -- is the element from which the mouse came (`relatedTarget` -> `target`).
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
`mouseout` 则与之相反:
+<<<<<<< HEAD
- `event.target` —— 是鼠标离开的元素。
- `event.relatedTarget` —— 是当前指针位置下的(鼠标进入的)元素。
+=======
+- `event.target` -- is the element that mouse left.
+- `event.relatedTarget` -- is the new under-the-pointer element, that mouse left for (`target` -> `relatedTarget`).
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```online
在下面示例中,每个特性都是一个元素。当你移动鼠标时你可以看到文本区域的鼠标事件。
@@ -50,7 +64,11 @@
这在实践中是有用的,因为可能会有许多中间元素。我们并不是真的想要处理每一个进入离开的过程。
+<<<<<<< HEAD
另一方面,我们应该记住,我们不能假设鼠标会缓慢地从一个事件移动到另一个事件。是的,它可以“跳”。
+=======
+On the other hand, we should keep in mind that we can't assume that the mouse slowly moves from one event to another. No, it can "jump".
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
特别是,光标可能从窗口外跳进页面的中间。此时 `relatedTarget=null`,这是因为鼠标来自“窗口外(nowhere)”:
@@ -96,7 +114,11 @@
因此,对于不考虑 `target` 的处理器,这看起来就像是在 `mouseout` 事件中,鼠标离开了父元素(第 `(2)` 步),然后在第 `(3)` 步的 `mouseover` 事件中鼠标又回到了父元素上。
+<<<<<<< HEAD
如果我们在进入/离开元素时执行一些动作,就会多执行很多“错误”操作。对于简单的事情可能不引人注目。但对于复杂的事情来说,会带来不必要的副作用。
+=======
+If we perform some actions on entering/leaving the element, then we'll get a lot of extra "false" runs. For simple stuff that may be unnoticeable. For complex things that may bring unwanted side-effects.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
我们可以通过使用 `mouseenter/mouseleave` 事件来解决这个问题。
@@ -169,7 +191,11 @@ table.onmouseout = function(event) {
[codetabs height=380 src="mouseenter-mouseleave-delegation-2"]
+<<<<<<< HEAD
尝试在表格单元之间或内部移动光标,太快或太慢都有问题。与之前不同的是只有 `
` 作为一个整体被高亮显示。
+=======
+Try to move the cursor in and out of table cells and inside them. Fast or slow -- doesn't matter. Only `
` as a whole is highlighted unlike the example before.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.png
index ea57233073..a9d76bebfd 100644
Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.png differ
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside@2x.png
index 6a17ca99f7..8ef27b83a4 100644
Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside@2x.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside@2x.png differ
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.png
index babbea5027..b421342bff 100644
Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.png differ
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems@2x.png
index e61891a5af..8a4c8672f2 100644
Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems@2x.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems@2x.png differ
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png
index cf9fa448b1..688dcb48b6 100644
Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png differ
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png
index a2749d68a5..6cb3c78a34 100644
Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png differ
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.png
index 627f9584a7..975acf4aff 100644
Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.png differ
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child@2x.png
index 51a917d86e..50725f39cd 100644
Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child@2x.png and b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child@2x.png differ
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/index.html
index 1e38d693c0..17239b026f 100755
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/index.html
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/index.html
@@ -1,5 +1,5 @@
-
+
diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js
index 6c3daa5b23..1c1443a7e7 100644
--- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js
+++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js
@@ -1,3 +1,5 @@
+let isDragging = false;
+
document.addEventListener('mousedown', function(event) {
let dragElement = event.target.closest('.draggable');
@@ -5,16 +7,19 @@ document.addEventListener('mousedown', function(event) {
if (!dragElement) return;
event.preventDefault();
- let coords, shiftX, shiftY;
+
+ dragElement.ondragstart = function() {
+ return false;
+ };
- startDrag(event.clientX, event.clientY);
+ let coords, shiftX, shiftY;
- document.addEventListener('mousemove', onMouseMove);
+ startDrag(dragElement, event.clientX, event.clientY);
- dragElement.onmouseup = function() {
+ function onMouseUp(event) {
finishDrag();
};
-
+
function onMouseMove(event) {
moveAt(event.clientX, event.clientY);
}
@@ -22,25 +27,37 @@ document.addEventListener('mousedown', function(event) {
// on drag start:
// remember the initial shift
// move the element position:fixed and a direct child of body
- function startDrag(clientX, clientY) {
-
- shiftX = clientX - dragElement.getBoundingClientRect().left;
- shiftY = clientY - dragElement.getBoundingClientRect().top;
+ function startDrag(element, clientX, clientY) {
+ if(isDragging) {
+ return;
+ }
+
+ isDragging = true;
+
+ document.addEventListener('mousemove', onMouseMove);
+ element.addEventListener('mouseup', onMouseUp);
- dragElement.style.position = 'fixed';
+ shiftX = clientX - element.getBoundingClientRect().left;
+ shiftY = clientY - element.getBoundingClientRect().top;
- document.body.append(dragElement);
+ element.style.position = 'fixed';
moveAt(clientX, clientY);
};
// switch to absolute coordinates at the end, to fix the element in the document
function finishDrag() {
+ if(!isDragging) {
+ return;
+ }
+
+ isDragging = false;
+
dragElement.style.top = parseInt(dragElement.style.top) + pageYOffset + 'px';
dragElement.style.position = 'absolute';
document.removeEventListener('mousemove', onMouseMove);
- dragElement.onmouseup = null;
+ dragElement.removeEventListener('mouseup', onMouseUp);
}
function moveAt(clientX, clientY) {
@@ -96,4 +113,4 @@ document.addEventListener('mousedown', function(event) {
dragElement.style.top = newY + 'px';
}
-});
+});
\ No newline at end of file
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 a435079ecf..7607576962 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
@@ -2,11 +2,19 @@
拖放是一个很好的界面解决方案。从复制和移动(参考文件管理)到排序(放入购物车),拖放是一种简洁明了的方法。
+<<<<<<< HEAD
在现代 HTML 标准中,有一个[拖动事件的部分](https://html.spec.whatwg.org/multipage/interaction.html#dnd)。
这很有趣,因为它们允许轻松地解决一些简单的任务,而且允许处理“外部”文件拖放到浏览器中的事件。因此我们可以在 OS 文件管理中获取文件,并将其拖动到浏览器窗口。然后 JavaScript 获取对其内容的访问权限。
但是本地的拖动事件总是有局限性。比如,我们可以把拖动范围限制在某个区域内。而且我们也可以把它变成 "horizontal" 或 "vertical"。还有其他的拖放任务无法通过使用 API 实现。
+=======
+In the modern HTML standard there's a [section about Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd) with special events such as `dragstart`, `dragend` and so on.
+
+They are interesting because they allow to solve simple tasks easily, and also allow to handle drag'n'drop of "external" files into the browser. So we can take a file in the OS file-manager and drop it into the browser window. Then JavaScript gains access to its contents.
+
+But native Drag Events also have limitations. For instance, we can't limit dragging by a certain area. Also we can't make it "horizontal" or "vertical" only. There are other drag'n'drop tasks that can't be implemented using that API.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
在这里,我们将看到如何使用鼠标事件实现拖放。并不难。
@@ -14,10 +22,17 @@
拖放基础算法就像这样:
+<<<<<<< HEAD
1. 在可拖动元素上捕获 `mousedown` 事件。
2. 准备要移动的元素(可能创建它的副本或其他任何东西)。
3. 然后在 `mousemove` 上,通过改变 `left/top` 和 `position:absolute` 来移动它。
4. 在 `mouseup`(释放按钮)中 —— 执行所有完成拖放相关的动作。
+=======
+1. Catch `mousedown` on a draggable element.
+2. Prepare the element for moving (maybe create a copy of it or whatever).
+3. Then on `mousemove` move it by changing `left/top` and `position:absolute`.
+4. On `mouseup` (button release) -- perform all actions related to a finished Drag'n'Drop.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
这些是基础。我们可以对其进行拓展,例如,当鼠标在可拖动元素上悬停时,高亮这个元素。
@@ -58,7 +73,11 @@ ball.onmousedown = function(event) { // (1) 启动进程
};
```
+<<<<<<< HEAD
如果我们运行代码,我们会发现一些奇怪的事情。在拖放的一开始,球会 "forks":我们开始拖动它的 "clone"。
+=======
+If we run the code, we can notice something strange. On the beginning of the drag'n'drop, the ball "forks": we start dragging its "clone".
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```online
这是一个动作实例:
@@ -94,14 +113,22 @@ ball.ondragstart = function() {
## 修正定位
+<<<<<<< HEAD
在上述例子中,球总是以指针为中心的:
+=======
+In the examples above the ball is always moved so, that it's center is under the pointer:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
ball.style.left = pageX - ball.offsetWidth / 2 + 'px';
ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
```
+<<<<<<< HEAD
不错,但这存在副作用。我们可以在球的任何地方使用 `mousedown` 来开始拖放。如果在边缘那么做,那么球就会突然“跳”到以指针为中心的位置。
+=======
+Not bad, but there's a side-effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if do it at the edge, then the ball suddenly "jumps" to become centered.
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
如果我们保持元素相对指针的初始位移,情况会更好。
@@ -119,9 +146,13 @@ ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
let shiftY = event.clientY - ball.getBoundingClientRect().top;
```
+<<<<<<< HEAD
请注意,在 JavaScript 中没有获取文档坐标的方法,因此我们在这里使用相对于窗口的坐标。
2. 然后,在拖动球时,我们将球放置在相对于指针移动的位置上,就像这样:
+=======
+2. Then while dragging we position the ball on the same shift relative to the pointer, like this:
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
```js
// onmousemove
@@ -146,7 +177,12 @@ ball.onmousedown = function(event) {
moveAt(event.pageX, event.pageY);
+<<<<<<< HEAD
// 球中心在 (pageX, pageY) 坐标上
+=======
+ // moves the ball at (pageX, pageY) coordinates
+ // taking initial shifts into account
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
function moveAt(pageX, pageY) {
ball.style.left = pageX - *!*shiftX*/!* + 'px';
ball.style.top = pageY - *!*shiftY*/!* + 'px';
@@ -156,10 +192,17 @@ ball.onmousedown = function(event) {
moveAt(event.pageX, event.pageY);
}
+<<<<<<< HEAD
// (3) 用 mousemove 移动球
document.addEventListener('mousemove', onMouseMove);
// (4) 释放球,移除不需要的处理器
+=======
+ // move the ball on mousemove
+ document.addEventListener('mousemove', onMouseMove);
+
+ // drop the ball, remove unneeded handlers
+>>>>>>> be342e50e3a3140014b508437afd940cd0439ab7
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
@@ -178,19 +221,35 @@ In action (inside `