diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 78230a281a..49f882d6fa 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -1,6 +1,10 @@ # 자바스크립트란? +<<<<<<< HEAD 자바스크립트(JavaScript)가 언어로서 지닌 특징에 대해 알아보겠습니다. 이어서 자바스크립트로 무엇을 할 수 있을지, 다른 기술들이 자바스크립트를 어떻게 활용하고 있는지도 이야기해 보겠습니다. +======= +Let's see what's so special about JavaScript, what we can achieve with it, and what other technologies play well with it. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 정의 @@ -24,9 +28,15 @@ 엔진의 종류는 다양한데, 엔진마다 특유의 코드네임이 있습니다. 아래처럼 말이죠. +<<<<<<< HEAD - [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- Chrome과 Opera에서 쓰입니다. - [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- Firefox에서 쓰입니다. - IE는 버전에 따라 'Trident'나 'Chakra'라 불리는 엔진을 사용합니다. 'ChakraCore'는 Microsoft Edge에 사용되며, 'SquirrelFish'는 Safari에 사용됩니다. +======= +- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera. +- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox. +- ...There are other codenames like "Chakra" for IE, "JavaScriptCore", "Nitro" and "SquirrelFish" for Safari, etc. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 위의 코드네임은 개발 관련 글에서 종종 언급되기 때문에 기억해 두는 것이 좋습니다. 본 튜토리얼에서도 해당 코드네임을 사용할 예정입니다. "X라는 기능은 V8에서만 지원합니다."라는 식으로 말이죠. 이런 문장을 만나면 Chrome과 Opera에서만 이 기능을 지원한다고 이해하시면 됩니다. @@ -106,15 +116,30 @@ 자바스크립트로 트랜스파일이 가능한 언어 몇 가지를 소개해 드리겠습니다. +<<<<<<< HEAD - [CoffeeScript](http://coffeescript.org/)는 자바스크립트를 위한 'syntactic sugar'입니다. 짧은 문법을 도입하여 명료하고 이해하기 쉬운 코드를 작성할 수 있습니다. Ruby 개발자들이 좋아합니다. - [TypeScript](http://www.typescriptlang.org/)는 개발을 단순화 하고 복잡한 시스템을 지원하려는 목적으로 '자료형의 명시화(strict data typing)'에 집중해 만든 언어입니다. Microsoft가 개발하였습니다. - [Flow](http://flow.org/) 역시 자료형을 강제하는데, TypeScript와는 다른 방식을 사용합니다. Facebook이 개발하였습니다. - [Dart](https://www.dartlang.org/)는 모바일 앱과 같이 브라우저가 아닌 환경에서 동작하는 고유의 엔진을 가진 독자적 언어입니다. Google이 개발하였습니다. +======= +- [CoffeeScript](http://coffeescript.org/) is a "syntactic sugar" for JavaScript. It introduces shorter syntax, allowing us to write clearer and more precise code. Usually, Ruby devs like it. +- [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing" to simplify the development and support of complex systems. It is developed by Microsoft. +- [Flow](http://flow.org/) also adds data typing, but in a different way. Developed by Facebook. +- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps), but also can be transpiled to JavaScript. Developed by Google. +- [Brython](https://brython.info/) is a Python transpiler to JavaScript that enables the writing of applications in pure Python without JavaScript. +- [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) is a modern, concise and safe programming language that can target the browser or Node. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 이 외에도 자바스크립트로 트랜스파일 할 수 있는 언어는 다양합니다. 개발 언어로 이런 언어 중 하나를 택한다고 하더라도 자신이 무엇을 하고 있는지 이해하려면 결국엔 자바스크립트를 알아야 합니다. ## 요약 +<<<<<<< HEAD - 자바스크립트는 브라우저에서만 쓸 목적으로 고안된 언어이지만, 지금은 다양한 환경에서 쓰이고 있습니다. - 오늘날 자바스크립트는 브라우저 환경에서 가장 널리 사용되는 언어로 자리매김하였습니다. HTML/CSS와 완전한 통합이 가능합니다. - 자바스크립트로 '트랜스파일'할 수 있는 언어는 많습니다. 각 언어마다 고유한 기능을 제공하죠. 자바스크립트에 숙달한 뒤에 이 언어들을 살펴볼 것을 추천드립니다. +======= +- JavaScript was initially created as a browser-only language, but it is now used in many other environments as well. +- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration in HTML/CSS. +- There are many languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 diff --git a/1-js/01-getting-started/2-manuals-specifications/article.md b/1-js/01-getting-started/2-manuals-specifications/article.md index c0017e9444..b85ced157c 100644 --- a/1-js/01-getting-started/2-manuals-specifications/article.md +++ b/1-js/01-getting-started/2-manuals-specifications/article.md @@ -13,20 +13,32 @@ ECMA-262명세서는 새로운 버전이 매년 나옵니다. 공식 버전이 갓 명세서에 등록된 기능이나 '등록되기 바로 직전'에 있는 기능(스테이지(stage)3 상태의 기능), 제안 목록은 에서 확인할 수 있습니다. +<<<<<<< HEAD 본 튜토리얼의 [두 번째 대 단원](info:browser-environment)에서 브라우저와 관련된 명세서를 다룰 예정이므로, 만약 브라우저에서 돌아가는 기능을 구현하는 개발자라면 해당 내용을 확인해 보시기 바랍니다. +======= +Also, if you're developing for the browser, then there are other specifications covered in the [second part](info:browser-environment) of the tutorial. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 매뉴얼 +<<<<<<< HEAD - Mozilla 재단이 운영하는 **MDN JavaScript Reference**엔 다양한 예제와 정보가 있습니다. 특정 함수나 메서드에 대한 깊이 있는 정보를 얻고 싶다면 이 사이트가 제격입니다. +======= +- **MDN (Mozilla) JavaScript Reference** is the main manual with examples and other information. It's great to get in-depth information about individual language functions, methods etc. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 링크는 다음과 같습니다. +<<<<<<< HEAD 위 사이트에 들어가서 원하는 내용을 직접 검색하는 것도 좋지만, 가끔은 검색 엔진을 이용해 내용을 찾는 게 더 나을 때도 있습니다. Google 검색 엔진에 접속해 'MDN [원하는 용어]'를 입력해 봅시다. `parseInt` 함수에 대한 정보를 얻고 싶다면 같이 검색하는 식으로 말이죠. - Microsoft가 운영하는 **MSDN**도 자바스크립트(해당 사이트에선 JScript라고 불립니다)와 관련된 광범위한 정보를 얻기에 좋은 사이트입니다. Internet Explorer에 관련된 정보를 찾고 싶다면 에 방문해 보는 것을 추천합니다. 위에서 소개한 방법처럼 검색 엔진을 켜서 검색어에 'MSDN'을 붙이면 원하는 정보를 쉽게 찾을 수 있습니다. 'RegExp MSDN', 'RegExp MSDN jscript' 처럼 말이죠. +======= +Although, it's often best to use an internet search instead. Just use "MDN [term]" in the query, e.g. to search for `parseInt` function. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 호환성 표 diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index a242ecef2d..c0f55d499e 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -9,7 +9,11 @@ ## 'script' 태그 +<<<<<<< HEAD ` ``` +<<<<<<< HEAD 참고로 브라우저 환경에서 부득이하게 window 레벨 전역 변수를 만들어야 한다면 `window` 객체에 변수를 명시적으로 할당하고 `window.user`와 같이 접근하는 방식을 취하시면 됩니다. 그런데 이 방법은 정말 필요한 경우에만 사용하길 권유합니다. +======= +```smart +In the browser, we can make a variable window-level global by explicitly assigning it to a `window` property, e.g. `window.user = "John"`. + +Then all scripts will see it, both with `type="module"` and without it. + +That said, making such global variables is frowned upon. Please try to avoid them. +``` +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ### 단 한 번만 평가됨 +<<<<<<< HEAD 동일한 모듈이 여러 곳에서 사용되더라도 모듈은 최초 호출 시 단 한 번만 실행됩니다. 실행 후 결과는 이 모듈을 가져가려는 모든 모듈에 내보내 집니다. 이런 작동방식은 중요한 결과를 초래합니다. 예시를 통해 이에 대해 알아봅시다. +======= +If the same module is imported into multiple other modules, its code is executed only once, upon the first import. Then its exports are given to all further importers. + +The one-time evaluation has important consequences, that we should be aware of. + +Let's see a couple of examples. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 alert 함수가 있는 모듈(`alert.js`)을 여러 모듈에서 가져오기로 해봅시다. 얼럿 창은 단 한 번만 나타납니다. @@ -133,9 +179,17 @@ import `./alert.js`; // 얼럿창에 '모듈이 평가되었습니다!'가 출 import `./alert.js`; // 아무 일도 발생하지 않습니다. ``` +<<<<<<< HEAD 실무에선 최상위 레벨 모듈을 대개 초기화나 내부에서 쓰이는 데이터 구조를 만들고 이를 내보내 재사용하고 싶을 때 사용합니다. 이제 좀 더 어려운 예시를 살펴보겠습니다. +======= +The second import shows nothing, because the module has already been evaluated. + +There's a rule: top-level module code should be used for initialization, creation of module-specific internal data structures. If we need to make something callable multiple times - we should export it as a function, like we did with `sayHi` above. + +Now, let's consider a deeper example. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 객체를 내보내는 모듈을 만들어봅시다. @@ -160,6 +214,7 @@ import {admin} from './admin.js'; alert(admin.name); // Pete *!* +<<<<<<< HEAD // 1.js와 2.js 모두 같은 객체를 가져오므로 // 1.js에서 객체에 가한 조작을 2.js에서도 확인할 수 있습니다. */!* @@ -170,12 +225,34 @@ alert(admin.name); // Pete 이런 특징을 이용하면 모듈 *설정(configuration)*을 쉽게 할 수 있습니다. 최초로 실행되는 모듈의 객체 프로퍼티를 원하는 대로 설정하면 다른 모듈에서 이 설정을 그대로 사용할 수 있기 때문이죠. 예시를 통해 이에 대해 자세히 알아봅시다. 아래 `admin.js` 모듈은 어떤 특정한 기능을 제공해주는데, 이 기능을 사용하려면 외부에서 `admin` 객체와 관련된 인증 정보를 받아와야 한다고 가정해봅시다. +======= +// Both 1.js and 2.js reference the same admin object +// Changes made in 1.js are visible in 2.js +*/!* +``` + +As you can see, when `1.js` changes the `name` property in the imported `admin`, then `2.js` can see the new `admin.name`. + +That's exactly because 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 actually very convenient, because it allows us to *configure* modules.** + +In other words, a module can provide a generic functionality that needs a setup. E.g. authentication needs credentials. Then it can export a configuration object expecting the outer code to assign to it. + +Here's the classical pattern: +1. A module exports some means of configuration, e.g. a configuration object. +2. On the first import we initialize it, write to its properties. The top-level application script may do that. +3. Further imports use the module. + +For instance, the `admin.js` module may provide certain functionality (e.g. authentication), but expect the credentials to come into the `config` object from outside: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js // 📁 admin.js -export let admin = { }; +export let config = { }; export function sayHi() { +<<<<<<< HEAD alert(`${admin.name}님, 안녕하세요!`); } ``` @@ -189,25 +266,56 @@ admin.name = "보라"; ``` 또 다른 모듈에서도 `admin.name`에 저장된 정보를 볼 수 있다는 걸 확인해 봅시다. +======= + alert(`Ready to serve, ${config.user}!`); +} +``` + +Here, `admin.js` exports the `config` object (initially empty, but may have default properties too). + +Then in `init.js`, the first script of our app, we import `config` from it and set `config.user`: ```js -// 📁 other.js -import {admin, sayHi} from './admin.js'; +// 📁 init.js +import {config} from './admin.js'; +config.user = "Pete"; +``` + +...Now the module `admin.js` is configured. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 +Further importers can call it, and it correctly shows the current user: + +<<<<<<< HEAD alert(admin.name); // *!*보라*/!* +======= +```js +// 📁 another.js +import {sayHi} from './admin.js'; +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 sayHi(); // *!*보라*/!*님, 안녕하세요! ``` + ### import.meta `import.meta` 객체는 현재 모듈에 대한 정보를 제공해줍니다. +<<<<<<< HEAD 호스트 환경에 따라 제공하는 정보의 내용은 다른데, 브라우저 환경에선 스크립트의 URL 정보를 얻을 수 있습니다. HTML 안에 있는 모듈이라면, 현재 실행 중인 웹페이지의 URL 정보를 얻을 수 있습니다. ```html run height=0 ``` @@ -260,7 +368,11 @@ sayHi(); // *!*보라*/!*님, 안녕하세요! diff --git a/1-js/13-modules/02-import-export/article.md b/1-js/13-modules/02-import-export/article.md index ad88c6943d..663dd496ed 100644 --- a/1-js/13-modules/02-import-export/article.md +++ b/1-js/13-modules/02-import-export/article.md @@ -321,7 +321,11 @@ export {default as User} from './user.js'; // default export를 다시 내보내 다시 내보내기가 왜 필요한건지 의문이 드실 겁니다. 유스 케이스를 통해 다시 내보내기가 실무에서 언제 사용되는지 알아봅시다. +<<<<<<< HEAD NPM을 통해 외부에 공개할 '패키지(package)'를 만들고 있다고 가정합시다. 이 패키지는 수많은 모듈로 구성되어있는데, 몇몇 모듈은 외부에 공개할 기능을, 몇몇 모듈은 이러한 모듈을 도와주는 '헬퍼' 역할을 담당하고 있다고 합시다. +======= +Imagine, we're writing a "package": a folder with a lot of modules, with some of the functionality exported outside (tools like NPM allow us to publish and distribute such packages, but we don't have to use them), and many modules are just "helpers", for internal use in other package modules. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 패키지 구조는 아래와 같습니다. ``` @@ -337,13 +341,27 @@ auth/ ... ``` +<<<<<<< HEAD 진입점 역할을 하는 '주요 파일'인 `auth/index.js`을 통해 기능을 외부에 노출시키면 이 패키지를 사용하는 개발자들은 아래와 같은 코드로 해당 기능을 사용할 겁니다. +======= +We'd like to expose the package functionality via a single entry point. + +In other words, a person who would like to use our package, should import only from the "main file" `auth/index.js`. + +Like this: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js import {login, logout} from 'auth/index.js' ``` +<<<<<<< HEAD 이때 우리가 만든 패키지를 사용하는 외부 개발자가 패키지 안의 파일들을 뒤져 내부 구조를 건드리게 하면 안 됩니다. 그러려면 공개할 것만 `auth/index.js`에 넣어 내보내기 하고 나머는 숨기는 게 좋겠죠. +======= +The "main file", `auth/index.js` exports all the functionality that we'd like to provide in our package. + +The idea is that outsiders, other programmers who use our package, should not meddle with its internal structure, search for files inside our package folder. We export only what's necessary in `auth/index.js` and keep the rest hidden from prying eyes. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 이때 내보낼 기능을 패키지 전반에 분산하여 구현한 후, `auth/index.js`에서 이 기능들을 가져오고 이를 다시 내보내면 원하는 바를 어느 정도 달성할 수 있습니다. @@ -366,19 +384,36 @@ export {User}; ```js // 📁 auth/index.js +<<<<<<< HEAD // login과 logout을 가지고 온 후 바로 내보냅니다. export {login, logout} from './helpers.js'; // User 가져온 후 바로 내보냅니다. +======= +// re-export login/logout +export {login, logout} from './helpers.js'; + +// re-export the default export as User +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 export {default as User} from './user.js'; ... ``` +<<<<<<< HEAD ### default export 다시 내보내기 +======= +The notable difference of `export ... from` compared to `import/export` is that re-exported modules aren't available in the current file. So inside the above example of `auth/index.js` we can't use re-exported `login/logout` functions. + +### Re-exporting the default export +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 기본 내보내기를 다시 내보낼 때는 주의해야 할 점들이 있습니다. +<<<<<<< HEAD `user.js` 내의 클래스 `User`를 다시 내보내기 한다고 가정해 봅시다. +======= +Let's say we have `user.js` with the `export default class User` and would like to re-export it: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js // 📁 user.js @@ -387,7 +422,13 @@ export default class User { } ``` +<<<<<<< HEAD 1. `User`를 `export User from './user.js'`로 다시 내보내기 할 때 문법 에러가 발생합니다. 어디가 잘못된 걸까요? +======= +We can come across two problems with it: + +1. `export User from './user.js'` won't work. That would lead to a syntax error. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 default export를 다시 내보내려면 위 예시처럼 `export {default as User}`를 사용해야 합니다. @@ -399,7 +440,11 @@ export default class User { export {default} from './user.js'; // default export를 다시 내보내기 ``` +<<<<<<< HEAD default export를 다시 내보낼 땐 이런 특이한 상황도 인지하고 있다가 처리해줘야 하므로 몇몇 개발자들은 default export를 다시 내보내는것을 선호하지 않습니다. +======= +Such oddities of re-exporting a default export are one of the reasons why some developers don't like default exports and prefer named ones. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 요약 @@ -418,6 +463,7 @@ default export를 다시 내보낼 땐 이런 특이한 상황도 인지하고 가져오기 타입 역시 정리해 봅시다. +<<<<<<< HEAD - named export 가져오기: - `import {x [as y], ...} from "mod"` - default export 가져오기: @@ -427,6 +473,17 @@ default export를 다시 내보낼 땐 이런 특이한 상황도 인지하고 - `import * as obj from "mod"` - 모듈을 가져오긴 하지만(코드는 실행됨), 변수에 할당하지 않기: - `import "mod"` +======= +- Importing named exports: + - `import {x [as y], ...} from "module"` +- Importing the default export: + - `import x from "module"` + - `import {default as x} from "module"` +- Import all: + - `import * as obj from "module"` +- Import the module (its code runs), but do not assign any of its exports to variables: + - `import "module"` +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 `import/export` 문은 스크립트의 맨 위나 맨 아래에 올 수 있는데 이 둘엔 차이가 없습니다. diff --git a/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md b/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md index 357a573134..9db69cb2fa 100644 --- a/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md +++ b/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md @@ -19,5 +19,5 @@ function wrap(target) { user = wrap(user); alert(user.name); // John -alert(user.age); // ReferenceError: Property doesn't exist "age" +alert(user.age); // ReferenceError: Property doesn't exist: "age" ``` diff --git a/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md b/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md index 67e853f537..29a13bbe9f 100644 --- a/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md +++ b/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md @@ -27,6 +27,6 @@ user = wrap(user); alert(user.name); // John *!* -alert(user.age); // ReferenceError: Property doesn't exist "age" +alert(user.age); // ReferenceError: Property doesn't exist: "age" */!* ``` diff --git a/1-js/99-js-misc/01-proxy/article.md b/1-js/99-js-misc/01-proxy/article.md index 4c8d328819..c326e56b55 100644 --- a/1-js/99-js-misc/01-proxy/article.md +++ b/1-js/99-js-misc/01-proxy/article.md @@ -39,7 +39,7 @@ for(let key in proxy) alert(key); // test, 반복도 잘 동작합니다. -- (3) 그림에서 볼 수 있듯이 트랩이 없으면 `proxy`는 `target`을 둘러싸는 투명한 래퍼가 됩니다. -![](proxy.svg) +![](proxy.svg) `Proxy`는 일반 객체와는 다른 행동 양상을 보이는 '특수 객체(exotic object)'입니다. 프로퍼티가 없죠. `handler`가 비어있으면 `Proxy`에 가해지는 작업은 `target`에 곧바로 전달됩니다. @@ -55,6 +55,7 @@ for(let key in proxy) alert(key); // test, 반복도 잘 동작합니다. -- (3) | 내부 메서드 | 핸들러 메서드 | 작동 시점 | |-----------------|----------------|-------------| +<<<<<<< HEAD | `[[Get]]` | `get` | 프로퍼티를 읽을 때 | | `[[Set]]` | `set` | 프로퍼티에 쓸 때 | | `[[HasProperty]]` | `has` | `in` 연산자가 동작할 때 | @@ -68,6 +69,21 @@ for(let key in proxy) alert(key); // test, 반복도 잘 동작합니다. -- (3) | `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) | | `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` | | `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in`, `Object/keys/values/entries` | +======= +| `[[Get]]` | `get` | reading a property | +| `[[Set]]` | `set` | writing to a property | +| `[[HasProperty]]` | `has` | `in` operator | +| `[[Delete]]` | `deleteProperty` | `delete` operator | +| `[[Call]]` | `apply` | function call | +| `[[Construct]]` | `construct` | `new` operator | +| `[[GetPrototypeOf]]` | `getPrototypeOf` | [Object.getPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) | +| `[[SetPrototypeOf]]` | `setPrototypeOf` | [Object.setPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) | +| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](mdn:/JavaScript/Reference/Global_Objects/Object/isExtensible) | +| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](mdn:/JavaScript/Reference/Global_Objects/Object/preventExtensions) | +| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperties) | +| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` | +| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in`, `Object.keys/values/entries` | +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```warn header="규칙" 내부 메서드나 트랩을 쓸 땐 자바스크립트에서 정한 몇 가지 규칙(invariant)을 반드시 따라야 합니다. @@ -335,7 +351,11 @@ let user = { _password: "비밀" }; +<<<<<<< HEAD alert(user._password); // 비밀 +======= +alert(user._password); // secret +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ``` 프락시를 사용해 `_`로 시작하는 프로퍼티에 접근하지 못하도록 막아봅시다. @@ -375,8 +395,13 @@ user = new Proxy(user, { } }, *!* +<<<<<<< HEAD deleteProperty(target, prop) { // 프로퍼티 삭제를 가로챕니다. */!* +======= + deleteProperty(target, prop) { // to intercept property deletion +*/!* +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 if (prop.startsWith('_')) { throw new Error("접근이 제한되어있습니다."); } else { @@ -437,7 +462,11 @@ user = { ``` +<<<<<<< HEAD `user.checkPassword()`를 호출하면 점 앞의 객체가 `this`가 되므로 프락시로 감싼 `user`에 접근하게 되는데, `this._password`는 `get` 트랩(프로퍼티를 읽으려고 하면 동작함)을 활성화하므로 에러가 던져집니다. +======= +A call to `user.checkPassword()` gets proxied `user` as `this` (the object before dot becomes `this`), so when it tries to access `this._password`, the `get` trap activates (it triggers on any property read) and throws an error. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 `(*)`로 표시한 줄에선 객체 메서드의 컨텍스트를 원본 객체인 `target`에 바인딩시켜준 이유가 바로 여기에 있습니다. `checkPassword()`를 호출할 땐 언제든 트랩 없이 `target`이 `this`가 되게 하기 위해서이죠. @@ -963,9 +992,13 @@ revoke(); alert(proxy.data); // Error ``` -A call to `revoke()` removes all internal references to the target object from the proxy, so they are no longer connected. The target object can be garbage-collected after that. +A call to `revoke()` removes all internal references to the target object from the proxy, so they are no longer connected. + +Initially, `revoke` is separate from `proxy`, so that we can pass `proxy` around while leaving `revoke` in the current scope. + +We can also bind `revoke` method to proxy by setting `proxy.revoke = revoke`. -We can also store `revoke` in a `WeakMap`, to be able to easily find it by a proxy object: +Another option is to create a `WeakMap` that has `proxy` as the key and the corresponding `revoke` as the value, that allows to easily find `revoke` for a proxy: ```js run *!* @@ -980,21 +1013,25 @@ let {proxy, revoke} = Proxy.revocable(object, {}); revokes.set(proxy, revoke); -// ..later in our code.. +// ..somewhere else in our code.. revoke = revokes.get(proxy); revoke(); alert(proxy.data); // Error (revoked) ``` +<<<<<<< HEAD The benefit of such an approach is that we don't have to carry `revoke` around. We can get it from the map by `proxy` when needed. We use `WeakMap` instead of `Map` here because it won't block garbage collection. If a proxy object becomes 'unreachable' (e.g. no variable references it any more), `WeakMap` allows it to be wiped from memory together with its `revoke` that we won't need any more. +======= +We use `WeakMap` instead of `Map` here because it won't block garbage collection. If a proxy object becomes "unreachable" (e.g. no variable references it any more), `WeakMap` allows it to be wiped from memory together with its `revoke` that we won't need any more. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## References - Specification: [Proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). -- MDN: [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). +- MDN: [Proxy](mdn:/JavaScript/Reference/Global_Objects/Proxy). ## Summary @@ -1016,13 +1053,13 @@ We can trap: - Reading (`get`), writing (`set`), deleting (`deleteProperty`) a property (even a non-existing one). - Calling a function (`apply` trap). - The `new` operator (`construct` trap). -- Many other operations (the full list is at the beginning of the article and in the [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)). +- Many other operations (the full list is at the beginning of the article and in the [docs](mdn:/JavaScript/Reference/Global_Objects/Proxy)). That allows us to create "virtual" properties and methods, implement default values, observable objects, function decorators and so much more. We can also wrap an object multiple times in different proxies, decorating it with various aspects of functionality. -The [Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) API is designed to complement [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). For any `Proxy` trap, there's a `Reflect` call with same arguments. We should use those to forward calls to target objects. +The [Reflect](mdn:/JavaScript/Reference/Global_Objects/Reflect) API is designed to complement [Proxy](mdn:/JavaScript/Reference/Global_Objects/Proxy). For any `Proxy` trap, there's a `Reflect` call with same arguments. We should use those to forward calls to target objects. Proxies have some limitations: diff --git a/1-js/99-js-misc/03-currying-partials/article.md b/1-js/99-js-misc/03-currying-partials/article.md index e9601348e2..7fa5d77e01 100644 --- a/1-js/99-js-misc/03-currying-partials/article.md +++ b/1-js/99-js-misc/03-currying-partials/article.md @@ -155,7 +155,7 @@ function curried(...args) { if (args.length >= func.length) { // (1) return func.apply(this, args); } else { - return function pass(...args2) { // (2) + return function(...args2) { // (2) return curried.apply(this, args.concat(args2)); } } @@ -164,6 +164,7 @@ function curried(...args) { 위의 예시를 실행시키면, 두 개의 `if` 분기점이 있습니다. +<<<<<<< HEAD (1)에 해당하는 경우(함수가 호출되었을때): `args` 를 카운트한 갯수가 전달된 원래 함수 func (`func.length`)와 같거나 길다면, 그대로 `func` 호출에 전달함. (2)에 해당하는 경우(partial이 적용될때): 아직 `func`이 호출되지 않습니다. `pass`라는 래퍼가 대신 반환되고, `pass` 래퍼함수가 `curried`를 이전함수와 새로운 인수와 함께 다시 적용합니다. 그 다음 새로운 `curried` 호출에, 다시 새로운 partial (만약에 인수가 충분하지 않으면)을 반환하거나 최종적으로 `func` 결과를 반환합니다. @@ -176,6 +177,12 @@ function curried(...args) { 3. `pass` 래퍼가 다시 `(3)`과 함께 호출됩니다. 다음 호출인 `pass(3)`가 이전의 인수들인 (`1`, `2`)를 가져오고 `3`을 추가하고 `curried(1, 2, 3)` 호출을 합니다 -- 여기에 `3`인수는 마지막으로, 원래의 함수에 전달됩니다. 아직 확실하게 이해되지 않았다면, 호출 순서를 마음속이나 종이에 그려보세요. +======= +1. If passed `args` count is the same or more than the original function has in its definition (`func.length`) , then just pass the call to it using `func.apply`. +2. Otherwise, get a partial: we don't call `func` just yet. Instead, another wrapper is returned, that will re-apply `curried` providing previous arguments together with the new ones. + +Then, if we call it, again, we'll get either a new partial (if not enough arguments) or, finally, the result. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```smart header="오직 고정된 길이의 함수들만 사용 가능합니다" 커링은 해당 함수가 고정된 개수의 인수를 가지도록 요구합니다. diff --git a/1-js/99-js-misc/04-reference-type/3-why-this/solution.md b/1-js/99-js-misc/04-reference-type/3-why-this/solution.md index 9748f9c2ae..488e6746b6 100644 --- a/1-js/99-js-misc/04-reference-type/3-why-this/solution.md +++ b/1-js/99-js-misc/04-reference-type/3-why-this/solution.md @@ -5,7 +5,11 @@ 2. 역시 일반적인 호출 방법에 속합니다. 괄호가 추가되었긴 하지만 연산 우선순위를 바꾸진 않으므로 점 연산자가 먼저 실행됩니다. +<<<<<<< HEAD 3. 좀 더 복잡한 패턴의 호출(`(expression).method()`)이 등장했네요. 세 번째 호출은 아래와 같은 코드로 쪼갤 수 있습니다. +======= +3. Here we have a more complex call `(expression)()`. The call works as if it were split into two lines: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js no-beautify f = obj.go; // 표현식 계산하기 @@ -14,7 +18,11 @@ 위 코드에서 `f()`는 (메서드가 아닌) 함수로써 호출되었습니다. `this`에 대한 정보가 전혀 없는 상태에서 말이죠. +<<<<<<< HEAD 4. `(3)`과 동일한 패턴의 호출입니다. `expression`이 `obj.go || obj.stop`라는 차이점만 있습니다. +======= +4. The similar thing as `(3)`, to the left of the parentheses `()` we have an expression. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 `(3)`과 `(4)`에서 어떤 일이 일어나는지 알려면 참조 타입을 다시 상기해야 합니다. 점이나 대괄호를 통해 프로퍼티에 접근하려는 경우 참조 타입 값(`(base, name, strict)`)이 반환됩니다. diff --git a/1-js/99-js-misc/04-reference-type/article.md b/1-js/99-js-misc/04-reference-type/article.md index 2228ea9399..fcb9c8af02 100644 --- a/1-js/99-js-misc/04-reference-type/article.md +++ b/1-js/99-js-misc/04-reference-type/article.md @@ -4,7 +4,11 @@ ```warn header="심화 학습" 이번 절에선 특정 에지 케이스(edge case)를 설명하기 위한 심화 내용을 다룹니다. +<<<<<<< HEAD 숙련된 상당수의 개발자가 이 절에서 다룰 내용을 모른 채로 일하고 있지만 문제가 없고, 중요한 내용은 아니기 때문에 자바스크립트 내부에서 어떤 일이 일어나는지 알고 싶지 않다면 이번 글은 넘어가거나 미뤄도 괜찮습니다. +======= +It's not important. Many experienced developers live fine without knowing it. Read on if you want to know how things work under the hood. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ``` 복잡한 상황에서 메서드를 호출하면 `this` 값을 잃어버리는 경우가 생깁니다. @@ -93,7 +97,11 @@ hi(); // this가 undefined이기 때문에 에러가 발생합니다. 그런데 점 연산 이외의 연산(할당 연산 등)은 참조 타입을 통째로 버리고 `user.hi` 값(함수)만 받아 전달합니다. 이 때문에 점 이외의 연산에선 `this` 정보가 사라집니다. +<<<<<<< HEAD `obj.method()` 같이 점을 사용하거나, `obj[method]()` 같이 대괄호를 사용해 함수를 호출했을 때만 `this` 값이 의도한 대로 전달됩니다. 이런 문제는 [func.bind()](/bind#solution-2-bind) 등을 이용하면 해결 할 수 있는데, 이에 대해선 추후에 알아보도록 하겠습니다. +======= +So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj['method']()` syntax (they do the same here). There are various ways to solve this problem such as [func.bind()](/bind#solution-2-bind). +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 요약 diff --git a/1-js/99-js-misc/05-bigint/article.md b/1-js/99-js-misc/05-bigint/article.md index 7f59ecb55e..ff99ded971 100644 --- a/1-js/99-js-misc/05-bigint/article.md +++ b/1-js/99-js-misc/05-bigint/article.md @@ -126,5 +126,5 @@ We can use such JSBI code "as is" for engines that don't support bigints and for ## 참고 자료 -- [MDN docs on BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt). +- [MDN docs on BigInt](mdn:/JavaScript/Reference/Global_Objects/BigInt). - [Specification](https://tc39.es/ecma262/#sec-bigint-objects). diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index 2528b5cafc..9c23e1db37 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -51,7 +51,11 @@ DOM은 HTML을 아래와 같이 태그 트리 구조로 표현합니다.
@@ -142,8 +146,13 @@ let node4 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node4, 'div.domtree', 690, 360); +<<<<<<< HEAD ````warn header="테이블엔 언제나 ``가 있습니다." 테이블은 조금 흥미롭습니다. DOM 명세서에선 테이블에 반드시 ``가 있어야 한다고 못 박아 놓았지만, HTML에선 ``를 생략하곤 합니다. 이때, 브라우저는 자동으로 DOM에 ``를 만들어줍니다. +======= +````warn header="Tables always have ``" +An interesting "special case" is tables. By DOM specification they must have `` tag, but HTML text may omit it. Then the browser creates `` in the DOM automatically. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 HTML: @@ -160,7 +169,11 @@ let node5 = {"name":"TABLE","nodeType":1,"children":[{"name":"TBODY","nodeType": drawHtmlTree(node5, 'div.domtree', 600, 200); +<<<<<<< HEAD 보이시죠? ``가 어디선가 나타났습니다. 테이블을 다룰 땐 위 내용을 상기해 갑자기 나타난 ``때문에 놀라지 않도록 합시다. +======= +You see? The `` appeared out of nowhere. We should keep this in mind while working with tables to avoid surprises. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```` ## 기타 노드 타입 @@ -188,7 +201,11 @@ drawHtmlTree(node5, 'div.domtree', 600, 200);
@@ -199,7 +216,11 @@ drawHtmlTree(node6, 'div.domtree', 690, 500); **HTML 안의 모든 것은 (심지어 그것이 주석이더라도) DOM을 구성합니다.** +<<<<<<< HEAD HTML 문서 최상단에 위치하는 `` 지시자 또한 DOM 노드가 됩니다. 이 노드는 DOM 트리의 `` 바로 위에 위치합니다. 본 튜토리얼에선 이 노드를 다루지 않을 예정이라 다이어그램에도 표시는 하지 않을 것입니다. 하지만 존재하는 노드입니다. +======= +Even the `` directive at the very beginning of HTML is also a DOM node. It's in the DOM tree right before ``. Few people know about that. We are not going to touch that node, we even don't draw it on diagrams, but it's there. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 문서 전체를 나타내는 `document` 객체 또한 DOM 노드입니다. diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index 8b759337f3..3e9382c70c 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -214,7 +214,11 @@ alert( document.body.previousSibling ); // HTMLHeadElement ## 요소 간 이동 +<<<<<<< HEAD 지금까지 언급한 탐색 관련 프로퍼티는 *모든* 종류의 노드를 참조합니다. `childNodes`를 이용하면 텍스트 노드, 요소 노드, 심지어 주석 노드까지 참조할 수 있죠. +======= +Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if they exist. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 하지만 실무에서 텍스트 노드나 주석 노드는 잘 다루지 않습니다. 웹 페이지를 구성하는 태그의 분신인 요소 노드를 조작하는 작업이 대다수이죠. diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md index 1853d12da4..965da25b2b 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -70,8 +70,13 @@ 같은 `id`를 가진 요소가 여러 개 있으면 `document.getElementById`같이 `id`를 이용해 요소를 검색하는 메서드의 동작이 예측 불가능해집니다. 검색된 여러 요소 중 어떤 요소를 반환할지 판단하지 못해 임의의 요소가 반환되죠. 문서 내 동일 `id`가 없도록 해 이런 일을 방지하도록 합시다. ``` +<<<<<<< HEAD ```warn header="`anyNode.getElementById`가 아닌 `document.getElementById`" `getElementById`는 `document` 객체를 대상으로 해당 `id`를 가진 요소 노드를 찾아 줍니다. 문서 노드가 아닌 다른 노드엔 호출할 수 없습니다. +======= +```warn header="Only `document.getElementById`, not `anyElem.getElementById`" +The method `getElementById` can be called only on `document` object. It looks for the given `id` in the whole document. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ``` ## querySelectorAll [#querySelectorAll] @@ -142,7 +147,11 @@ querySelectorAll에는 `:hover`나 `:active` 같은 CSS 선택자의 가상 클 부모 요소, 부모 요소의 부모 요소 등 DOM 트리에서 특정 요소의 상위에 있는 요소들은 *조상(ancestor)* 요소라고 합니다. +<<<<<<< HEAD 메서드 `elem.closest(css)`는 `elem` 자기 자신을 포함하여 CSS 선택자와 일치하는 가장 가까운 조상 요소를 찾을 수 있게 도와줍니다. +======= +The method `elem.closest(css)` looks for the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 `closest`메서드는 해당 요소부터 시작해 DOM 트리를 한 단계씩 거슬러 올라가면서 원하는 요소를 찾습니다. CSS 선택자와 일치하는 요소를 찾으면, 검색을 중단하고 해당 요소를 반환합니다. @@ -363,7 +372,11 @@ DOM에서 원하는 노드를 검색하게 해주는 주요 메서드 6가지는 +<<<<<<< HEAD 아마 실무에선 `querySelector`나 `querySelectorAll`을 가장 많이 사용하실 겁니다. `getElementBy`로 시작하는 메서드는 대개 오래된 스크립트에서 만날 수 있는데, 일부 이 메서드가 꼭 필요한 상황에서 쓰이는 경우도 있습니다. +======= +By far the most used are `querySelector` and `querySelectorAll`, but `getElement(s)By*` can be sporadically helpful or found in the old scripts. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 이 외에 알아두면 좋을 만한 메서드는 아래와 같습니다. diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md index cf20c98378..f03fb31ecf 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/article.md +++ b/2-ui/1-document/05-basic-dom-node-properties/article.md @@ -198,7 +198,11 @@ XML 모드에선 케이스가 '그대로' 유지됩니다. XML 모드는 요즘 ## innerHTML로 내용 조작하기 +<<<<<<< HEAD [innerHTML](https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML) 프로퍼티를 사용하면 요소 안의 HTML을 문자열 형태로 받아올 수 있습니다. +======= +The [innerHTML](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) property allows to get the HTML inside the element as a string. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 요소 안 HTML을 수정하는 것도 가능합니다. innerHTML은 페이지를 수정하는 데 쓰이는 강력한 방법의 하나입니다. @@ -397,15 +401,24 @@ elem.innerHTML = elem.innerHTML + "..."
``` +<<<<<<< HEAD 1. 첫 번째 `
`엔 이름이 'HTML 형태'로 저장됩니다. 입력한 태그는 태그로 해석되어 굵은 글씨가 출력되네요. 2. 두 번째 `
`엔 이름이 '텍스트 형태'로 저장됩니다. 따라서 입력한 값 그대로 `이보라`가 출력되는 것을 확인할 수 있습니다. +======= +1. The first `
` gets the name "as HTML": all tags become tags, so we see the bold name. +2. The second `
` gets the name "as text", so we literally see `Winnie-the-Pooh!`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 개발을 하다보면 사용자의 입력값을 받아 처리해야 하는 경우가 많습니다. 이때 사용자가 입력한 값은 텍스트로 처리되어야 합니다. 예상치 못한 HTML이 사이트에 침투하는 것을 막으려면 `textContent`를 사용합시다. @@ -413,7 +426,11 @@ elem.innerHTML = elem.innerHTML + "..." hidden 속성과 hidden 프로퍼티는 요소를 보여줄지 말지 지정할 때 사용할 수 있습니다. +<<<<<<< HEAD `hidden`은 HTML 안에서 쓸 수도 있고 자바스크립트에서도 쓸 수 있습니다. +======= +We can use it in HTML or assign it using JavaScript, like this: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```html run height="80"
아래 두 div를 숨겨봅시다.
diff --git a/2-ui/1-document/06-dom-attributes-and-properties/article.md b/2-ui/1-document/06-dom-attributes-and-properties/article.md index c683859cf0..7dfa7b6d0e 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/article.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/article.md @@ -298,7 +298,11 @@ HTML을 작성할 때 우리는 대부분 표준 속성을 사용합니다. 하
``` +<<<<<<< HEAD 이렇게 커스텀 속성을 사용하는 게 `.order-state-new`, `.order-state-pending`, `order-state-canceled`같은 클래스를 사용하는 것보다 왜 선호될까요? +======= +Why would using an attribute be preferable to having classes like `.order-state-new`, `.order-state-pending`, `.order-state-canceled`? +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 이유는 속성은 클래스보다 다루기 편리하다는 점 때문입니다. 속성의 상태는 아래와 같이 쉽게 변경할 수 있습니다. diff --git a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md index eab23709c4..1983403298 100644 --- a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md +++ b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md @@ -6,7 +6,11 @@ importance: 5 빈 DOM 요소 `elem`과 `text`라는 문자열이 있습니다. +<<<<<<< HEAD 셋 중에서 같은 동작을 수행하는 명령어는 무엇일까요? +======= +Which of these 3 commands will do exactly the same? +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 1. `elem.append(document.createTextNode(text))` 2. `elem.innerHTML = text` diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md index 8ae95c4b28..51a9f8c51c 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md @@ -39,15 +39,23 @@ function update() { ```js let timerId; -function clockStart() { // run the clock - timerId = setInterval(update, 1000); +function clockStart() { // run the clock + if (!timerId) { // only set a new interval if the clock is not running + timerId = setInterval(update, 1000); + } update(); // (*) } function clockStop() { clearInterval(timerId); - timerId = null; + timerId = null; // (**) } ``` -`update()`는 `clockStart()` 에서뿐만 아니라 `(*)`로 표시한 줄에서도 호출됩니다. 양쪽 모두에서 `update()`를 호출하지 않으면 `setInterval`이 실행되기 전까지 사용자는 아무런 내용이 없는 시계를 봐야 하기 때문입니다. \ No newline at end of file +<<<<<<< HEAD +`update()`는 `clockStart()` 에서뿐만 아니라 `(*)`로 표시한 줄에서도 호출됩니다. 양쪽 모두에서 `update()`를 호출하지 않으면 `setInterval`이 실행되기 전까지 사용자는 아무런 내용이 없는 시계를 봐야 하기 때문입니다. +======= +Please note that the call to `update()` is not only scheduled in `clockStart()`, but immediately run in the line `(*)`. Otherwise the visitor would have to wait till the first execution of `setInterval`. And the clock would be empty till then. + +Also it is important to set a new interval in `clockStart()` only when the clock is not running. Otherways clicking the start button several times would set multiple concurrent intervals. Even worse - we would only keep the `timerID` of the last interval, losing references to all others. Then we wouldn't be able to stop the clock ever again! Note that we need to clear the `timerID` when the clock is stopped in the line `(**)`, so that it can be started again by running `clockStart()`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html index e017bb3783..9aa6b7fa09 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html @@ -43,12 +43,22 @@ } function clockStart() { +<<<<<<< HEAD timerId = setInterval(update, 1000); update(); // <-- 첫 번째 setInterval이 실행되기 전까지 기다릴 필요 없이 시작합니다. +======= + // set a new interval only if the clock is stopped + // otherwise we would rewrite the timerID reference to the running interval and wouldn't be able to stop the clock ever again + if (!timerId) { + timerId = setInterval(update, 1000); + } + update(); // <-- start right now, don't wait 1 second till the first setInterval works +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 } function clockStop() { clearInterval(timerId); + timerId = null; // <-- clear timerID to indicate that the clock has been stopped, so that it is possible to start it again in clockStart() } clockStart(); diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md index 6455ac18be..b53c2a1f88 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md @@ -1,9 +1,17 @@ 이 이상한 동작의 이유는 바로 주어진 HTML이 잘못되었기 때문입니다. +<<<<<<< HEAD 브라우저는 이를 자동으로 고쳐야 합니다. 그러나 명세에 따르면 `` 안에는 표와 관련된 특정 태그만이 존재할 수 있기 때문에 텍스트가 있어서는 안 됩니다. 따라서 브라우저는 `'aaa'`를 `
` *앞에* 추가합니다. +======= +The browser has to fix it automatically. But there may be no text inside the `
`: according to the spec only table-specific tags are allowed. So the browser shows `"aaa"` *before* the `
`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 이제 표를 삭제해도 텍스트가 남아있는 이유가 분명해졌습니다. +<<<<<<< HEAD 이 문제는 브라우저 도구를 사용해 DOM을 탐색해보면 쉽게 답을 찾을 수 있습니다. 브라우저 도구에서는 `
` 앞에 `'aaa'` 가 있는 것으로 표시됩니다. +======= +The question can be easily answered by exploring the DOM using the browser tools. You'll see `"aaa"` before the `
`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 HTML 표준에는 잘못된 HTML을 수정하는 방법이 구체적으로 정해져 있으므로, 이러한 브라우저의 동작은 올바른 동작입니다. \ No newline at end of file diff --git a/2-ui/1-document/07-modifying-document/6-create-list/task.md b/2-ui/1-document/07-modifying-document/6-create-list/task.md index f66b63584a..7682264b51 100644 --- a/2-ui/1-document/07-modifying-document/6-create-list/task.md +++ b/2-ui/1-document/07-modifying-document/6-create-list/task.md @@ -8,9 +8,15 @@ importance: 4 리스트의 모든 요소는 아래 방법으로 생성합니다. +<<<<<<< HEAD 1. `prompt`를 사용해 사용자로부터 리스트의 내용을 입력받습니다. 2. 1번에서 입력받은 내용을 갖는 `
  • ` 를 생성한 후 `
      ` 에 추가합니다. 3. 사용자가 입력을 취소할 때까지 계속합니다 (`ESC` 키나 프롬프트 창의 취소 버튼을 누를 때까지). +======= +1. Ask a user about its content using `prompt`. +2. Create the `
    • ` with it and add it to `
        `. +3. Continue until the user cancels the input (by pressing `key:Esc` or via an empty entry). +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 모든 요소는 동적으로 생성되어야 합니다. 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 6018859b93..20860f6d91 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 @@ -2,11 +2,19 @@ 브라우저 창이 차지하는 너비와 높이를 어떻게 구할 수 있을까요? 스크롤 때문에 보이지 않는 영역을 포함하여 문서 전체가 차지하는 너비와 높이는 어떻게 구할 수 있을까요? 자바스크립트를 사용해서 페이지를 스크롤 할 수 있을까요? +<<<<<<< HEAD 이번 챕터에선 위와 같은 물음에 답을 주는 루트 문서 요소인 `document.documentElement`를 살펴볼 예정입니다. `document.documentElement`는 `` 태그와 상응하는 요소로 다양한 메서드를 지원합니다. 유용한 메서드이긴 하지만 몇 가지 주의할 점이 있어 같이 살펴봅시다. +======= +For this type of information, we can use the root document element `document.documentElement`, that corresponds to the `` tag. But there are additional methods and peculiarities to consider. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 브라우저 창의 너비와 높이 +<<<<<<< HEAD 창이 차지하는 너비와 높이를 알려면 `document.documentElement`의 `clientWidth`와 `clientHeight`를 사용하면 됩니다. +======= +To get window width and height, we can use the `clientWidth/clientHeight` of `document.documentElement`: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ![](document-client-width-height.svg) @@ -16,12 +24,21 @@ ``` +<<<<<<< HEAD ````warn header="`window` 객체가 아닌 `document.documentElement`를 쓰는 이유" 브라우저의 `window` 객체 역시 `innerWidth`와 `innerHeight` 프로퍼티를 지원합니다. 이 프로퍼티를 써도 원하는 대로 창 크기를 구할 수 있을 것 같은데 왜 `document.documentElement`의 `clientWidth`나 `clientHeight`를 쓰는 걸까요? 스크롤바가 생기면 스크롤바 역시 공간을 차지하는데, `clientWidth`나 `clientHeight`는 스크롤바가 차지하는 공간을 제외해서 너비나 높이 값을 계산합니다. 눈에 보이는 문서에서 콘텐츠가 실제로 들어가게 될 영역의 너비와 높이 값을 반환하는 것이죠. 그런데 `window.innerWidth/innerHeight`는 스크롤바가 차지하는 영역을 포함해 값을 계산합니다. +======= +````warn header="Not `window.innerWidth/innerHeight`" +Browsers also support properties like `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 the width/height of the visible part of the document, available for the content. + +`window.innerWidth/innerHeight` includes the scrollbar. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 스크롤바가 있는 경우 스크롤 바 역시 공간을 차지하는데, 이럴 때 `window`객체와 `document.documentElement`의 해당 프로퍼티들은 다른 값을 반환합니다. ```js run @@ -29,7 +46,11 @@ alert( window.innerWidth ); // 전체 창 너비 alert( document.documentElement.clientWidth ); // 스크롤바가 차지하는 영역을 제외한 창 너비 ``` +<<<<<<< HEAD 창 사이즈가 필요한 경우는 스크롤 바 안쪽에 무언가를 그리거나 위치시킬 때가 대다수입니다. 따라서 `documentElement`의 `clientHeight/clientWidth`를 써야 합니다. +======= +In most cases, we need the *available* window width in order to draw or position something within scrollbars (if there are any), so we should use `documentElement.clientHeight/clientWidth`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```` ```warn header="`DOCTYPE`을 꼭 써주세요." @@ -40,9 +61,15 @@ alert( document.documentElement.clientWidth ); // 스크롤바가 차지하는 ## 문서의 너비와 높이 +<<<<<<< HEAD 이론상 `document.documentElement`는 문서의 루트 요소에 상응하고, 루트 요소엔 콘텐츠 전부가 들어가기 때문에 우리는 문서의 전체 크기를 `document.documentElement`의 `scrollWidth`와 `scrollHeight`를 사용해 재면 되지 않냐고 생각합니다. 그런데 전체 페이지를 대상으로 했을 때, `document.documentElement`의 프로피터들은 우리가 예상한 대로 동작하지 않습니다. Chrome이나 Safari, Opera에서 스크롤이 없는 경우 `documentElement.scrollHeight`는 `documentElement.clientHeight`보다 작을 때가 있죠. 예상하기엔 같은 값이어야 하는데도 말입니다. +======= +Theoretically, as the root document element is `document.documentElement`, and it encloses all the content, we could measure the document's full size as `document.documentElement.scrollWidth/scrollHeight`. + +But on that element, 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`! Weird, right? +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 정확한 문서 전체 높이 값을 얻으려면 아래 여섯 프로퍼티가 반환하는 값 중 최댓값을 골라야 합니다. @@ -60,11 +87,19 @@ alert('스크롤에 의해 가려진 분을 포함한 전체 문서 높이: ' + ## 스크롤 정보 얻기 [#page-scroll] +<<<<<<< HEAD DOM 요소의 현재 스크롤 상태(스크롤에 의해 가려진 영역에 대한 정보)는 `scrollLeft`와 `scrollTop` 프로퍼티를 통해 구할 수 있습니다. 대부분의 브라우저에서 문서의 스크롤 상태는 `document.documentElement`의 `scrollLeft`나 `scrollTop`을 이용해 구할 수 있습니다. 다만 구버전 WebKit을 기반으로 하는 브라우저에선 버그([5991](https://bugs.webkit.org/show_bug.cgi?id=5991)) 때문에 `document.documentElement`가 아닌 `document.body`를 사용해야 원하는 값을 구할 수 있습니다. 이쯤 되면 스크롤 포지션 정보를 구하기 위해 브라우저별 예외처리까지 다 해야 하나 라는 생각이 들 수 있을 겁니다. 다행히도 `window`객체의 `pageXOffset`과 `pageYOffset`을 사용하면 브라우저 상관없이 스크롤 정보를 구할 수 있어서 이런 예외 상황을 외워두지 않아도 됩니다. +======= +DOM elements have their current scroll state in their `scrollLeft/scrollTop` properties. + +For document scroll, `document.documentElement.scrollLeft/scrollTop` works in most browsers, except older 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`. + +Luckily, we don't have to remember these peculiarities at all, because the scroll is available in the special properties, `window.pageXOffset/pageYOffset`: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js run alert('세로 스크롤에 의해 가려진 위쪽 영역 높이: ' + window.pageYOffset); @@ -73,19 +108,40 @@ alert('가로 스크롤에 의해 가려진 왼쪽 영역 너비: ' + window.pag 참고로 이 두 프로퍼티는 읽기만 가능합니다. +<<<<<<< HEAD ## scrollTo, scrollBy로 스크롤 상태 변경하기 [#window-scroll] ```warn 자바스크립트를 사용해 스크롤을 움직이려면 DOM이 완전히 만들어진 상태이어야 합니다. ``에 있는 스크립트에서 페이지 전체의 스크롤을 움직이려 하면 잘 동작하지 않을 수 있습니다. +======= +```smart header="Also available as `window` properties `scrollX` and `scrollY`" +For historical reasons, both properties exist, but they are the same: +- `window.pageXOffset` is an alias of `window.scrollX`. +- `window.pageYOffset` is an alias of `window.scrollY`. +``` + +## Scrolling: scrollTo, scrollBy, scrollIntoView [#window-scroll] + +```warn +To scroll the page with JavaScript, its DOM must be fully built. + +For instance, if we try to scroll the page with a script in ``, it won't work. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ``` 일반 요소의 스크롤 상태는 `scrollTop`이나 `scrollLeft`로 쉽게 변경할 수 있습니다. +<<<<<<< HEAD 페이지 전체의 스크롤 상태 역시 `document.documentElement`의 `scrollTop/scrollLeft`를 사용해 변경 가능하죠(다만, Safari는 `document.body`의 `scrollTop/scrollLeft`를 써야 합니다). 그런데 이보다 더 편하고 브라우저 상관없이 쓸 수 있는 대안이 있긴합니다. 바로 [window.scrollBy(x,y)](mdn:api/Window/scrollBy)와 [window.scrollTo(pageX,pageY)](mdn:api/Window/scrollTo)입니다. +======= +We can do the same for the page using `document.documentElement.scrollTop/scrollLeft` (except Safari, where `document.body.scrollTop/Left` should be used instead). + +Alternatively, 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). +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 - `scrollBy(x,y)`메서드를 사용하면 페이지의 스크롤 상태를 현재 포지션을 기준으로 상대적으로 조정합니다. `scrollBy(0,10)`는 문서의 스크롤 상태를 현재를 기준으로 스크롤을 `10px`아래로 내린것 처럼 움직여주죠. @@ -106,10 +162,15 @@ alert('가로 스크롤에 의해 가려진 왼쪽 영역 너비: ' + window.pag ## scrollIntoView +<<<<<<< HEAD 추가 메서드 [elem.scrollIntoView(top)](mdn:api/Element/scrollIntoView)를 머릿속에 추가해 스크롤 상태를 완벽히 마스터 해봅시다. +======= +For completeness, let's cover one more method: [elem.scrollIntoView(top)](mdn:api/Element/scrollIntoView). +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 `elem.scrollIntoView(top)`를 호출하면 전체 페이지 스크롤이 움직여 `elem`이 눈에 보이는 상태로 변경됩니다. `elem.scrollIntoView`는 인수를 하나 받는데, 인수에 따라 다음과 같이 동작합니다. +<<<<<<< HEAD - `top`이 `true`(디폴트)인 경우, `elem`이 창 제일 위에 보이도록 스크롤 상태가 변경됩니다. `elem`의 위쪽 모서리가 창의 위쪽 모서리와 일치하게 되죠. - `top`이 `false`인 경우, `elem`이 창 가장 아래에 보이도록 스크롤 상태가 변경됩니다. `elem`의 아래쪽 모서리가 창의 아래쪽 모서리와 일치하게 변합니다. @@ -119,15 +180,32 @@ alert('가로 스크롤에 의해 가려진 왼쪽 영역 너비: ' + window.pag 두 번째 버튼을 누르면 버튼의 아래 모서리가 창 밑으로 붙는 것을 확인할 수 있습니다. +======= +- If `top=true` (that's the default), then the page will be scrolled to make `elem` appear on the top of the window. The upper edge of the element will be aligned with the window top. +- If `top=false`, then the page scrolls to make `elem` appear at the bottom. The bottom edge of the element will be aligned with the window bottom. + +```online +The button below scrolls the page to position itself at the window top: + + + +And this button scrolls the page to position itself at the bottom: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ``` ## 스크롤 막기 +<<<<<<< HEAD 때에 따라 문서 스크롤바를 '고정' 해야 하는 경우가 생기곤 합니다. 사용자에게 반드시 전달해야 하는 중요한 메시지가 있어서 이 메시지를 화면에 크게 띄우고, 사용자가 스크롤을 움직여 다른 콘텐츠를 보지 못하게 한 상태에서 메시지를 읽게 하려는 경우가 대표적인 예가 될 수 있습니다. 이럴 때 `document.body.style.overflow = "hidden"`를 사용할 수 있습니다. 해당 스크립트가 동작하면 페이지의 스크롤바 위치가 '고정' 됩니다. +======= +Sometimes we need to make the document "unscrollable". For instance, when we need to cover the page with a large message requiring immediate attention, and we want the visitor to interact with that message, not with the document. + +To make the document unscrollable, it's enough to set `document.body.style.overflow = "hidden"`. The page will "freeze" at its current scroll position. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```online 직접 실습해봅시다. @@ -136,6 +214,7 @@ alert('가로 스크롤에 의해 가려진 왼쪽 영역 너비: ' + window.pag +<<<<<<< HEAD 위쪽 버튼을 누르면 스크롤바가 고정되었다가, 아래 버튼을 누르면 고정이 해제되는 것을 확인할 수 있습니다. ``` @@ -144,13 +223,28 @@ alert('가로 스크롤에 의해 가려진 왼쪽 영역 너비: ' + window.pag 그런데 이 방법은 스크롤바가 사라진다는 단점이 있습니다. 스크롤바는 일정 공간을 차지하는데, 스크롤바가 사라지면 해당 공간을 채우기 위해 콘텐츠가 갑자기 '움직이는' 현상이 발생합니다. 이렇게 페이지 전체의 스크롤 상태가 갑자기 변경되면 사용자 입장에선 이상해 보일 수 있기 때문에 개발자는 스크롤바를 고정시키기 전과 후의 `clientWidth`값을 비교해서 해당 증상을 보정해야 합니다. 스크롤바가 사라질 땐 `clientWidth`값이 커지는데 이때 스크롤바가 차지했던 영역만큼 `document.body`에 `padding`을 줘서 콘텐츠 전체의 너비를 스크롤바가 사라지기 전과 같은 값으로 유지할 수 있습니다. +======= +The first button freezes the scroll, while the second one releases it. +``` + +We can use the same technique to freeze the scroll for other elements, not just for `document.body`. + +The drawback of the method is that the scrollbar disappears. If it occupied some space, then that space is now free and the content "jumps" to fill it. + +That looks a bit odd, but can be worked around if we compare `clientWidth` before and after the freeze. If it increased (the scrollbar disappeared), then add `padding` to `document.body` in place of the scrollbar to keep the content width the same. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 요약 기하 프로퍼티: +<<<<<<< HEAD - 사용자 눈에 보이는 문서(콘텐츠가 실제 보여지는 영역)의 너비와 높이: `document.documentElement.clientWidth/clientHeight` - 스크롤에 의해 가려진 영역을 포함한 문서 전체의 너비와 높이: +======= +- Width/height of the visible part of the document (content area width/height): `document.documentElement.clientWidth/clientHeight` +- Width/height of the whole document, with the scrolled out part: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js let scrollHeight = Math.max( diff --git a/2-ui/2-events/02-bubbling-and-capturing/article.md b/2-ui/2-events/02-bubbling-and-capturing/article.md index e0177b9d0b..1e31a32e7f 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/article.md +++ b/2-ui/2-events/02-bubbling-and-capturing/article.md @@ -204,9 +204,15 @@ elem.addEventListener("click", e => alert(2)); 이벤트가 발생하면 이벤트가 발생한 가장 안쪽 요소가 '타깃 요소(`event.target`)'가 됩니다. +<<<<<<< HEAD - 이벤트는 document에서 시작해 DOM 트리를 따라 `event.target`까지 내려갑니다. 이벤트는 트리를 따라 내려가면서 `addEventListener(..., true)`로 할당한 핸들러를 동작시킵니다. `addEventListener(..., true)`의 `true`는 `{capture: true}`의 축약형입니다. - 이후 타깃 요소에 설정된 핸들러가 호출됩니다. - 이후엔 이벤트가 `event.target`부터 시작해서 다시 최상위 노드까지 전달되면서 각 요소에 `on`로 할당한 핸들러와 `addEventListener`로 할당한 핸들러를 동작시킵니다. `addEventListener`로 할당한 핸들러 중, 세 번째 인수가 없거나 `false`, `{capture: false}`인 핸들러만 호출됩니다. +======= +- Then the event moves down from the document root to `event.target`, calling handlers assigned with `addEventListener(..., true)` on the way (`true` is a shorthand for `{capture: true}`). +- Then handlers are called on the target element itself. +- Then the event bubbles up from `event.target` to the root, calling handlers assigned using `on`, HTML attributes and `addEventListener` without the 3rd argument or with the 3rd argument `false/{capture:false}`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 각 핸들러는 아래와 같은 `event` 객체의 프로퍼티에 접근할 수 있습니다. @@ -220,6 +226,10 @@ elem.addEventListener("click", e => alert(2)); 현실에서 사고가 발생하면 지역 경찰이 먼저 사고를 조사합니다. 그 지역에 대해 가장 잘 아는 기관은 지역 경찰이기 때문입니다. 추가 조사가 필요하다면 그 이후에 상위 기관이 사건을 넘겨받습니다. +<<<<<<< HEAD 이벤트 핸들러도 이와 같은 논리로 만들어졌습니다. 특정 요소에 할당된 핸들러는 그 요소에 대한 자세한 사항과 무슨 일을 해야 할지 가장 잘 알고 있습니다. `
  • `에 할당된 핸들러는 ``에 대한 모든 것을 알고 있기 때문에 ``를 다루는데 가장 적합합니다. 따라서 ``를 다룰 기회를 이 요소에 할당된 핸들러에게 가장 먼저 주는 것입니다. +======= +The same for event handlers. The code that set the handler on a particular element knows maximum details about the element and what it does. A handler on a particular `` may be suited for that exactly ``, it knows everything about it, so it should get the chance first. Then its immediate parent also knows about the context, but a little bit less, and so on till the very top element that handles general concepts and runs the last one. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 버블링과 캡처링은 '이벤트 위임(event delegation)'의 토대가 됩니다. 이벤트 위임은 강력한 이벤트 핸들링 패턴입니다. 다음 챕터에서 이를 다루도록 하겠습니다. diff --git a/2-ui/2-events/03-event-delegation/article.md b/2-ui/2-events/03-event-delegation/article.md index f8a18691af..10087be07d 100644 --- a/2-ui/2-events/03-event-delegation/article.md +++ b/2-ui/2-events/03-event-delegation/article.md @@ -5,7 +5,11 @@ 이벤트 위임은 비슷한 방식으로 여러 요소를 다뤄야 할 때 사용됩니다. 이벤트 위임을 사용하면 요소마다 핸들러를 할당하지 않고, 요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있습니다. +<<<<<<< HEAD 공통 조상에 할당한 핸들러에서 `event.target`을 이용하면 실제 어디서 이벤트가 발생했는지 알 수 있습니다. 이를 이용해 이벤트를 핸들링합니다. +======= +In the handler we get `event.target` to see where the event actually happened and handle it. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 예제를 살펴봅시다. 다음은 고대 중국 철학에서 유래한 [팔괘도(Ba-Gua diagram)](http://en.wikipedia.org/wiki/Ba_gua) 입니다. diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md index a9a3b2c3e3..934cfabc0a 100644 --- a/2-ui/2-events/05-dispatch-events/article.md +++ b/2-ui/2-events/05-dispatch-events/article.md @@ -8,7 +8,11 @@ ## Event의 생성자 +<<<<<<< HEAD 내장 이벤트 클래스는 DOM 요소 클래스같이 계층 구조를 형성합니다. 내장 이벤트 클래스 계층의 꼭대기엔 [Event](http://www.w3.org/TR/dom/#event) 클래스가 있습니다. +======= +Built-in event classes form a hierarchy, similar to DOM element classes. The root is the built-in [Event](http://www.w3.org/TR/dom/#event) class. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 `Event` 객체는 다음과 같이 생성할 수 있습니다. @@ -187,7 +191,6 @@ UI 이벤트별 표준 프로퍼티 목록은 명세서에서 확인할 수 있 ``` +<<<<<<< HEAD 이제 원하는 대로 `dispatchEvent`가 `mouse.onclick`을 포함한 현재 코드 실행이 종료된 이후에 실행됩니다. 이벤트 핸들러들이 완전히 독립적으로 되었네요. +======= +Now `dispatchEvent` runs asynchronously after the current code execution is finished, including `menu.onclick`, so event handlers are totally separate. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 출력 순서는 '1', '2', '중첩 이벤트'입니다. @@ -283,9 +302,15 @@ UI 이벤트별 표준 프로퍼티 목록은 명세서에서 확인할 수 있 이벤트를 직접 만드는 경우라면 `CustomEvent` 생성자를 써야 합니다. `CustomEvent` 생성자엔 `detail`이라는 추가 프로퍼티를 명시할 수 있는데, 여기에 이벤트 관련 정보를 저장해야 합니다. 이렇게 하면 모든 핸들러에서 `event.detail`을 통해 커스텀 이벤트의 정보를 알 수 있습니다. +<<<<<<< HEAD 커스텀 이벤트의 이름을 `click`나 `keydown` 같이 브라우저 내장 이벤트처럼 지을 수 있긴 한데, 이런 경우엔 아주 조심해야 합니다. 되도록이면 내장 이벤트와 같은 이름을 가진 브라우저 이벤트를 만들지 말도록 합시다. 대부분의 경우 설계 관점에서 아주 좋지 않은 영항을 끼치기 때문입니다. +======= +Despite the technical possibility of generating browser events like `click` or `keydown`, we should use them with great care. + +We shouldn't generate browser events as it's a hacky way to run handlers. That's bad architecture most of the time. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 그렇지만 이런 경우에는 브라우저 이벤트를 만드는게 불가피 하니, 사용해도 괜찮습니다. 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 060f5704dc..782af2d64b 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 @@ -52,21 +52,37 @@ `click` 이벤트는 마우스 왼쪽 버튼을, `contextmenu` 이벤트는 마우스 오른쪽 버튼을 눌렀을 때 발생하기 때문에 `click`과 `contextmenu` 이벤트를 다룰 땐 보통 `button` 프로퍼티를 사용하지 않습니다. +<<<<<<< HEAD 반면 `mousedown`이벤트나 `mouseup` 이벤트를 다룰 땐 해당 이벤트의 핸들러에 `event.button`을 명시해 줘야 할 수 있습니다. 이 이벤트들은 마우스 버튼 어디에서나 발생할 수 있는데 `button` 프로퍼티를 사용해야 정확히 어떤 버튼에서 이벤트가 발생했는지 알 수 있기 때문입니다. +======= +From the other hand, `mousedown` and `mouseup` handlers may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between "right-mousedown" and "left-mousedown". +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 주요 `event.button` 프로퍼티 값을 정리하면 다음과 같습니다. | 버튼 | `event.button` | |--------------|----------------| +<<<<<<< HEAD | 왼쪽(주요 버튼) | 0 | | 가운데(보조 버튼) | 1 | | 오른쪽 (두 번째 버튼) | 2 | | X1(뒤로 가기 버튼) | 3 | | X2(앞으로 가기 버튼) | 4 | +======= +| Left button (primary) | 0 | +| Middle button (auxiliary) | 1 | +| Right button (secondary) | 2 | +| X1 button (back) | 3 | +| X2 button (forward) | 4 | +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 상당수의 마우스는 왼쪽, 오른쪽 버튼만 가지고 있기 때문에 이 마우스들이 만들어내는 `event.button` 값은 `0`이나 `2`가 됩니다. 터치를 지원하는 기기들도 사람이 해당 기기를 터치했을 때 유사한 이벤트를 만듭니다. +<<<<<<< HEAD 참고로 `buttons`라는 프로퍼티도 있는데, 이 프로퍼티는 여러 개의 버튼을 한꺼번에 눌렀을 때 해당 버튼들에 대한 정보를 정수 형태로 저장해 줍니다. 실무에서 `buttons` 프로퍼티를 만날 일은 극히 드물긴 하지만 혹시라도 필요하다면[MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)에서 확인해보시길 바랍니다. +======= +Also there's `event.buttons` property that has all currently pressed buttons as an integer, one bit per button. In practice this property is very rarely used, you can find details at [MDN](mdn:/api/MouseEvent/buttons) if you ever need it. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```warn header="역사의 뒤안길로 사라진 `event.which`" 오래된 코드를 보다 보면 `event.which`라는 프로퍼티를 발견할 수 있습니다. `event.which` 프로퍼티는 어떤 버튼을 클릭했는지 알려주는 비표준 프로퍼티로 다음과 같은 값을 가집니다. @@ -156,7 +172,11 @@ Windows와 Linux는 `key:Alt`, `key:Shift`, `key:Ctrl` 키를 지원합니다. 글자 위에서 마우스를 더블클릭하면 글자가 선택되는데, 이런 기본 동작이 사용자 경험을 해칠 때가 있습니다. +<<<<<<< HEAD `dblclick` 이벤트가 발생하면 얼럿창을 띄우고 싶다고 가정해봅시다. 제대로 코드를 작성했음에도 불구하고 핸들러가 실행되는 동시에 글자가 선택되는 불필요한 부수효과가 발생하였습니다. +======= +For instance, double-clicking on the text below selects it in addition to our handler: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```html autorun height=50 이곳을 더블클릭해주세요. 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 c7ac0d4db8..d409c3f127 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 @@ -80,7 +80,7 @@ An important feature of `mouseout` -- it triggers, when the pointer moves from a ``` -If we're on `#parent` and then move the pointer deeper into `#child`, but we get `mouseout` on `#parent`! +If we're on `#parent` and then move the pointer deeper into `#child`, we get `mouseout` on `#parent`! ![](mouseover-to-child.svg) 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 4caed03517..d36949f3e4 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 @@ -124,7 +124,11 @@ ball.style.top = pageY - ball.offsetHeight / 2 + 'px'; ```js // onmousemove +<<<<<<< HEAD // 공은 고정된 포지션을 갖습니다. +======= + // ball has position:absolute +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ball.style.left = event.pageX - *!*shiftX*/!* + 'px'; ball.style.top = event.pageY - *!*shiftY*/!* + 'px'; ``` @@ -276,7 +280,11 @@ function onMouseMove(event) { } ``` +<<<<<<< HEAD 아래 예시에서 공을 축구 골대 위로 드래그하면 골대가 강조 표시됩니다. +======= +In the example below when the ball is dragged over the soccer goal, the goal is highlighted. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 [codetabs height=250 src="ball4"] @@ -300,4 +308,8 @@ function onMouseMove(event) { - `mousedown/up`에 이벤트 위임을 사용할 수 있습니다. `event.target`을 확인하는 넓은 영역의 이벤트 핸들러는 수백 개의 요소에 대한 드래그 앤 드롭을 관리할 수 있습니다. - 등등 +<<<<<<< HEAD `DragZone`, `Droppable`, `Draggable` 및 기타 클래스 등 아키텍처를 구축하는 프레임워크가 있습니다. 대부분은 앞서 드래그와 드롭에 대한 설명과 유사한 작업을 하므로 이해하기 쉽습니다. 때로는 제3의 솔루션 적용보다 쉽게 수행할 수 있습니다. +======= +There are frameworks that build architecture over it: `DragZone`, `Droppable`, `Draggable` and other classes. Most of them do the similar stuff to what's described above, so it should be easy to understand them now. Or roll your own, as you can see that that's easy enough to do, sometimes easier than adapting a third-party solution. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 diff --git a/2-ui/3-event-details/6-pointer-events/article.md b/2-ui/3-event-details/6-pointer-events/article.md index 3e751a4af7..bffd672bfe 100644 --- a/2-ui/3-event-details/6-pointer-events/article.md +++ b/2-ui/3-event-details/6-pointer-events/article.md @@ -9,21 +9,34 @@ Let's make a small overview, so that you understand the general picture and the - Long ago, in the past, there were only mouse events. Then touch devices became widespread, phones and tablets in particular. For the existing scripts to work, they generated (and still generate) mouse events. For instance, tapping a touchscreen generates `mousedown`. So touch devices worked well with web pages. +<<<<<<< HEAD +======= + +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 But touch devices have more capabilities than a mouse. For example, it's possible to touch multiple points at once ("multi-touch"). Although, mouse events don't have necessary properties to handle such multi-touches. - So touch events were introduced, such as `touchstart`, `touchend`, `touchmove`, that have touch-specific properties (we don't cover them in detail here, because pointer events are even better). - Still, it wasn't enough, as there are many other devices, such as pens, that have their own features. Also, writing code that listens for both touch and mouse events was cumbersome. + Still, it wasn't enough, as there are many other devices, such as pens, that have their own features. Also, writing code that listens for both touch and mouse events was cumbersome. - To solve these issues, the new standard Pointer Events was introduced. It provides a single set of events for all kinds of pointing devices. +<<<<<<< HEAD As of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while the newer [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works and is mostly compartible with Pointer Events level 2. Unless you develop for old browsers, such as Internet Explorer 10, or for Safari 12 or below, there's no point in using mouse or touch events any more -- we can switch to pointer events. Then your code will work well with both touch and mouse devices. +======= +As of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while the newer [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works and is mostly compatible with Pointer Events level 2. + +Unless you develop for old browsers, such as Internet Explorer 10, or for Safari 12 or below, there's no point in using mouse or touch events any more -- we can switch to pointer events. + +Then your code will work well with both touch and mouse devices. + +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 That said, there are some important peculiarities that one should know in order to use Pointer Events correctly and avoid surprises. We'll make note of them in this article. ## Pointer event types @@ -43,12 +56,16 @@ Pointer events are named similarly to mouse events: | `gotpointercapture` | - | | `lostpointercapture` | - | -As we can see, for every `mouse`, there's a `pointer` that plays a similar role. Also there are 3 additional pointer events that don't have a corresponding `mouse...` counterpart, we'll explain them soon. +As we can see, for every `mouse`, there's a `pointer` that plays a similar role. Also there are 3 additional pointer events that don't have a corresponding `mouse...` counterpart, we'll explain them soon. ```smart header="Replacing `mouse` with `pointer` in our code" We can replace `mouse` events with `pointer` in our code and expect things to continue working fine with mouse. +<<<<<<< HEAD The support for touch devices will also "magically" improve. Although, we may need to add `touch-action: none` in some places in CSS. We'll cover it below in the section about `pointercancel`. +======= +The support for touch devices will also "magically" improve. Although, we may need to add `touch-action: none` in some places in CSS. We'll cover it below in the section about `pointercancel`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ``` ## Pointer event properties @@ -56,16 +73,26 @@ The support for touch devices will also "magically" improve. Although, we may ne Pointer events have the same properties as mouse events, such as `clientX/Y`, `target`, etc., plus some others: - `pointerId` - the unique identifier of the pointer causing the event. +<<<<<<< HEAD Browser-generated. Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (examples will follow). - `pointerType` - the pointing device type. Must be a string, one of: "mouse", "pen" or "touch". +======= + + Browser-generated. Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (examples will follow). +- `pointerType` - the pointing device type. Must be a string, one of: "mouse", "pen" or "touch". +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 We can use this property to react differently on various pointer types. - `isPrimary` - is `true` for the primary pointer (the first finger in multi-touch). Some pointer devices measure contact area and pressure, e.g. for a finger on the touchscreen, there are additional properties for that: +<<<<<<< HEAD - `width` - the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, it's always `1`. +======= +- `width` - the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, it's always `1`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 - `height` - the height of the area where the pointer touches the device. Where unsupported, it's always `1`. - `pressure` - the pressure of the pointer tip, in range from 0 to 1. For devices that don't support pressure must be either `0.5` (pressed) or `0`. - `tangentialPressure` - the normalized tangential pressure. @@ -102,11 +129,19 @@ Please note: you must be using a touchscreen device, such as a phone or a tablet ## Event: pointercancel +<<<<<<< HEAD The `pointercancel` event fires when there's an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated. Such causes are: - The pointer device hardware was physically disabled. - The device orientation changed (tablet rotated). +======= +The `pointercancel` event fires when there's an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated. + +Such causes are: +- The pointer device hardware was physically disabled. +- The device orientation changed (tablet rotated). +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 - The browser decided to handle the interaction on its own, considering it a mouse gesture or zoom-and-pan action or something else. We'll demonstrate `pointercancel` on a practical example to see how it affects us. @@ -126,7 +161,11 @@ Here is the flow of user actions and the corresponding events: So the issue is that the browser "hijacks" the interaction: `pointercancel` fires in the beginning of the "drag-and-drop" process, and no more `pointermove` events are generated. ```online +<<<<<<< HEAD Here's the drag'n'drop demo with loggin of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`: +======= +Here's the drag'n'drop demo with loggin of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 [iframe src="ball" height=240 edit] ``` @@ -141,7 +180,11 @@ We need to do two things: - We can do this by setting `ball.ondragstart = () => false`, just as described in the article . - That works well for mouse events. 2. For touch devices, there are other touch-related browser actions (besides drag'n'drop). To avoid problems with them too: +<<<<<<< HEAD - Prevent them by setting `#ball { touch-action: none }` in CSS. +======= + - Prevent them by setting `#ball { touch-action: none }` in CSS. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 - Then our code will start working on touch devices. After we do that, the events will work as intended, the browser won't hijack the process and doesn't emit `pointercancel`. @@ -161,6 +204,7 @@ Now we can add the code to actually move the ball, and our drag'n'drop will work Pointer capturing is a special feature of pointer events. The idea is very simple, but may seem quite odd at first, as nothing like that exists for any other event type. +<<<<<<< HEAD The main method is: - `elem.setPointerCapture(pointerId)` - binds events with the given `pointerId` to `elem`. After the call all pointer events with the same `pointerId` will have `elem` as the target (as if happened on `elem`), no matter where in document they really happened. @@ -195,6 +239,56 @@ Pointer capturing provides a means to bind `pointermove` to `thumb` and avoid an - When `pointerup` happens (dragging complete), the binding is removed automatically, we don't need to care about it. So, even if the user moves the pointer around the whole document, events handlers will be called on `thumb`. Besides, coordinate properties of the event objects, such as `clientX/clientY` will still be correct - the capturing only affects `target/currentTarget`. +======= + +The main method is: +- `elem.setPointerCapture(pointerId)` -- binds events with the given `pointerId` to `elem`. After the call all pointer events with the same `pointerId` will have `elem` as the target (as if happened on `elem`), no matter where in document they really happened. + +In other words, `elem.setPointerCapture(pointerId)` retargets all subsequent events with the given `pointerId` to `elem`. + +The binding is removed: +- automatically when `pointerup` or `pointercancel` events occur, +- automatically when `elem` is removed from the document, +- when `elem.releasePointerCapture(pointerId)` is called. + +Now what is it good for? It's time to see a real-life example. + +**Pointer capturing can be used to simplify drag'n'drop kind of interactions.** + +Let's recall how one can implement a custom slider, described in the . + +We can make a `slider` element to represent the strip and the "runner" (`thumb`) inside it: + +```html +
    +
    +
    +``` + +With styles, it looks like this: + +[iframe src="slider-html" height=40 edit] + +

    + +And here's the working logic, as it was described, after replacing mouse events with similar pointer events: + +1. The user presses on the slider `thumb` -- `pointerdown` triggers. +2. Then they move the pointer -- `pointermove` triggers, and our code moves the `thumb` element along. + - ...As the pointer moves, it may leave the slider `thumb` element, go above or below it. The `thumb` should move strictly horizontally, remaining aligned with the pointer. + +In the mouse event based solution, to track all pointer movements, including when it goes above/below the `thumb`, we had to assign `mousemove` event handler on the whole `document`. + +That's not a cleanest solution, though. One of the problems is that when a user moves the pointer around the document, it may trigger event handlers (such as `mouseover`) on some other elements, invoke totally unrelated UI functionality, and we don't want that. + +This is the place where `setPointerCapture` comes into play. + +- We can call `thumb.setPointerCapture(event.pointerId)` in `pointerdown` handler, +- Then future pointer events until `pointerup/cancel` will be retargeted to `thumb`. +- When `pointerup` happens (dragging complete), the binding is removed automatically, we don't need to care about it. + +So, even if the user moves the pointer around the whole document, events handlers will be called on `thumb`. Nevertheless, coordinate properties of the event objects, such as `clientX/clientY` will still be correct - the capturing only affects `target/currentTarget`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 Here's the essential code: @@ -202,15 +296,30 @@ Here's the essential code: thumb.onpointerdown = function(event) { // retarget all pointer events (until pointerup) to thumb thumb.setPointerCapture(event.pointerId); -}; +<<<<<<< HEAD thumb.onpointermove = function(event) { // moving the slider: listen on the thumb, as all pointer events are retargeted to it let newLeft = event.clientX - slider.getBoundingClientRect().left; thumb.style.left = newLeft + 'px'; +======= + // start tracking pointer moves + thumb.onpointermove = function(event) { + // moving the slider: listen on the thumb, as all pointer events are retargeted to it + let newLeft = event.clientX - slider.getBoundingClientRect().left; + thumb.style.left = newLeft + 'px'; + }; + + // on pointer up finish tracking pointer moves + thumb.onpointerup = function(event) { + thumb.onpointermove = null; + thumb.onpointerup = null; + // ...also process the "drag end" if needed + }; +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 }; -// note: no need to call thumb.releasePointerCapture, +// note: no need to call thumb.releasePointerCapture, // it happens on pointerup automatically ``` @@ -218,15 +327,35 @@ thumb.onpointermove = function(event) { The full demo: [iframe src="slider" height=100 edit] + +

    + +In the demo, there's also an additional element with `onmouseover` handler showing the current date. + +Please note: while you're dragging the thumb, you may hover over this element, and its handler *does not* trigger. + +So the dragging is now free of side effects, thanks to `setPointerCapture`. ``` +<<<<<<< HEAD At the end, pointer capturing gives us two benefits: 1. The code becomes cleaner as we don't need to add/remove handlers on the whole `document` any more. The binding is released automatically. 2. If there are any `pointermove` handlers in the document, they won't be accidentally triggered by the pointer while the user is dragging the slider. ### Pointer capturing events +======= +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 + + +At the end, pointer capturing gives us two benefits: +1. The code becomes cleaner as we don't need to add/remove handlers on the whole `document` any more. The binding is released automatically. +2. If there are other pointer event handlers in the document, they won't be accidentally triggered by the pointer while the user is dragging the slider. + +### Pointer capturing events + +There's one more thing to mention here, for the sake of completeness. -There are two associated pointer events: +There are two events associated with pointer capturing: - `gotpointercapture` fires when an element uses `setPointerCapture` to enable capturing. - `lostpointercapture` fires when the capture is released: either explicitly with `releasePointerCapture` call, or automatically on `pointerup`/`pointercancel`. diff --git a/2-ui/3-event-details/6-pointer-events/slider-html.view/index.html b/2-ui/3-event-details/6-pointer-events/slider-html.view/index.html new file mode 100644 index 0000000000..781016f52a --- /dev/null +++ b/2-ui/3-event-details/6-pointer-events/slider-html.view/index.html @@ -0,0 +1,6 @@ + + + +
    +
    +
    diff --git a/2-ui/3-event-details/6-pointer-events/slider-html.view/style.css b/2-ui/3-event-details/6-pointer-events/slider-html.view/style.css new file mode 100644 index 0000000000..9b3d3b82d4 --- /dev/null +++ b/2-ui/3-event-details/6-pointer-events/slider-html.view/style.css @@ -0,0 +1,19 @@ +.slider { + border-radius: 5px; + background: #E0E0E0; + background: linear-gradient(left top, #E0E0E0, #EEEEEE); + width: 310px; + height: 15px; + margin: 5px; +} + +.thumb { + width: 10px; + height: 25px; + border-radius: 3px; + position: relative; + left: 10px; + top: -5px; + background: blue; + cursor: pointer; +} diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/index.html b/2-ui/3-event-details/6-pointer-events/slider.view/index.html index 2c2a69ec77..b29e646a16 100644 --- a/2-ui/3-event-details/6-pointer-events/slider.view/index.html +++ b/2-ui/3-event-details/6-pointer-events/slider.view/index.html @@ -5,22 +5,33 @@
    +

    Mouse over here to see the date

    + diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/style.css b/2-ui/3-event-details/6-pointer-events/slider.view/style.css index 9b3d3b82d4..a84cd5e7e8 100644 --- a/2-ui/3-event-details/6-pointer-events/slider.view/style.css +++ b/2-ui/3-event-details/6-pointer-events/slider.view/style.css @@ -8,6 +8,7 @@ } .thumb { + touch-action: none; width: 10px; height: 25px; border-radius: 3px; diff --git a/2-ui/3-event-details/7-keyboard-events/article.md b/2-ui/3-event-details/7-keyboard-events/article.md index 617852ccfd..51c1618f65 100644 --- a/2-ui/3-event-details/7-keyboard-events/article.md +++ b/2-ui/3-event-details/7-keyboard-events/article.md @@ -139,22 +139,25 @@ For instance, the `` below expects a phone number, so it does not accept ```html autorun height=60 run ``` -Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, `key:Ctrl+V`, do not work in the input. That's a side-effect of the strict filter `checkPhoneKey`. +The `onkeydown` handler here uses `checkPhoneKey` to check for the key pressed. If it's valid (from `0..9` or one of `+-()`), then it returns `true`, otherwise `false`. -Let's relax it a little bit: +As we know, the `false` value returned from the event handler, assigned using a DOM property or an attribute, such as above, prevents the default action, so nothing appears in the `` for keys that don't pass the test. (The `true` value returned doesn't affect anything, only returning `false` matters) +Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, do not work in the input. That's a side-effect of the strict filter `checkPhoneKey`. These keys make it return `false`. + +Let's relax the filter a little bit by allowing arrow keys `key:Left`, `key:Right` and `key:Delete`, `key:Backspace`: ```html autorun height=60 run @@ -162,7 +165,9 @@ function checkPhoneKey(key) { Now arrows and deletion works well. -...But we still can enter anything by using a mouse and right-click + Paste. So the filter is not 100% reliable. We can just let it be like that, because most of time it works. Or an alternative approach would be to track the `input` event -- it triggers after any modification. There we can check the new value and highlight/modify it when it's invalid. +Even though we have the key filter, one still can enter anything using a mouse and right-click + Paste. Mobile devices provide other means to enter values. So the filter is not 100% reliable. + +The alternative approach would be to track the `oninput` event -- it triggers *after* any modification. There we can check the new `input.value` and modify it/highlight the `` when it's invalid. Or we can use both event handlers together. ## Legacy @@ -170,6 +175,12 @@ In the past, there was a `keypress` event, and also `keyCode`, `charCode`, `whic There were so many browser incompatibilities while working with them, that developers of the specification had no way, other than deprecating all of them and creating new, modern events (described above in this chapter). The old code still works, as browsers keep supporting them, but there's totally no need to use those any more. +## Mobile Keyboards + +When using virtual/mobile keyboards, formally known as IME (Input-Method Editor), the W3C standard states that a KeyboardEvent's [`e.keyCode` should be `229`](https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode) and [`e.key` should be `"Unidentified"`](https://www.w3.org/TR/uievents-key/#key-attr-values). + +While some of these keyboards might still use the right values for `e.key`, `e.code`, `e.keyCode`... when pressing certain keys such as arrows or backspace, there's no guarantee, so your keyboard logic might not always work on mobile devices. + ## Summary Pressing a key always generates a keyboard event, be it symbol keys or special keys like `key:Shift` or `key:Ctrl` and so on. The only exception is `key:Fn` key that sometimes presents on a laptop keyboard. There's no keyboard event for it, because it's often implemented on lower level than OS. diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md index fff8f2234c..6edad380e4 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -8,11 +8,19 @@ 폼은 특수한 컬렉션인 `document.forms`의 구성원입니다. +<<<<<<< HEAD `document.forms`는 이름과 순서가 있는 '기명 컬렉션(named collection)'입니다. 개발자는 이 이름이나 순서를 사용해 문서 내의 폼에 접근할 수 있습니다. ```js no-beautify document.forms.my - 이름이 'my'인 폼 document.forms[0] - 문서 내의 첫 번째 폼 +======= +That's a so-called *"named collection"*: it's both named and ordered. We can use both the name or the number in the document to get the form. + +```js no-beautify +document.forms.my; // the form with name="my" +document.forms[0]; // the first form in the document +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ``` 이름이나 순서를 사용해 원하는 폼을 가져온 다음에는 기명 컬렉션 `form.elements`를 사용해 폼의 요소를 얻을 수 있습니다. @@ -36,9 +44,15 @@ document.forms[0] - 문서 내의 첫 번째 폼 ``` +<<<<<<< HEAD 그런데 개발을 하다 보면 이름이 같은 요소 여러 개를 다뤄야 하는 경우가 생기기도 합니다. 라디오 버튼을 다룰 때 이런 상황이 자주 발생하죠. 이때 `form.elements[name]`은 컬렉션이 된다는 사실을 이용할 수 있습니다. 예시를 살펴봅시다. +======= +There may be multiple elements with the same name. This is typical with radio buttons and checkboxes. + +In that case, `form.elements[name]` is a *collection*. For instance: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```html run height=40
    @@ -119,7 +133,11 @@ alert(ageElems[0]); // [object HTMLInputElement] ``` +<<<<<<< HEAD 그런데 폼 요소의 이름을 변경하는 일은 드물기 때문에 보통은 이런 특징이 문제가 되지 않습니다. +======= +That's usually not a problem, however, because we rarely change names of form elements. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```` @@ -176,12 +194,19 @@ input.checked = true; // 체크박스나 라디오 버튼에서 쓸 수 있습 ``의 값을 설정할 수 있습니다. +<<<<<<< HEAD 1. 조건에 맞는 `
    `가 '수정 중'일 때는 다른 셀을 눌러도 클릭 이벤트가 무시되어야 합니다. - 테이블엔 더 많은 셀이 추가될 수 있으므로 이벤트 위임을 사용하세요. +======= +- On click -- the cell should become "editable" (textarea appears inside), we can change HTML. There should be no resize, all geometry should remain the same. +- Buttons OK and CANCEL appear below the cell to finish/cancel the editing. +- Only one cell may be editable at a moment. While a `` is in "edit mode", clicks on other cells are ignored. +- The table may have many cells. Use event delegation. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 데모: diff --git a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md index 206eff5ad0..850d7272a5 100644 --- a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md +++ b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md @@ -8,6 +8,12 @@ importance: 4 [demo src="solution"] +<<<<<<< HEAD 참고1: `#mouse` 요소 이외의 어느 곳에도 이벤트 핸들러를 달지 마세요. -참고2: HTML과 CSS를 수정하지 마세요. 작성할 자바스크립트는 어떤 요소에서도 동작할 수 있는 범용성이 있어야 합니다. \ No newline at end of file +참고2: HTML과 CSS를 수정하지 마세요. 작성할 자바스크립트는 어떤 요소에서도 동작할 수 있는 범용성이 있어야 합니다. +======= +P.S. Don't put event handlers anywhere except the `#mouse` element. + +P.P.S. Don't modify HTML/CSS, the approach should be generic and work with any element. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 diff --git a/2-ui/4-forms-controls/2-focus-blur/article.md b/2-ui/4-forms-controls/2-focus-blur/article.md index 5ccf706e97..51de92697d 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -1,6 +1,10 @@ # focus와 blur +<<<<<<< HEAD 사용자가 폼 요소를 클릭하거나 `key:Tab` 키를 눌러 요소로 이동하면 해당 요소가 포커스(focus)됩니다. `autofocus`라는 HTML 속성을 사용해도 요소를 포커스 할 수 있는데 이 속성이 있는 요소는 페이지가 로드된 후 자동으로 포커싱 됩니다. 이 외에도 요소를 포커싱(focusing)할 수 있는 방법은 다양합니다. +======= +An element receives the focus when the user either clicks on it or uses the `key:Tab` key on the keyboard. There's also an `autofocus` HTML attribute that puts the focus onto an element by default when a page loads and other means of getting the focus. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 요소를 포커싱한다는 것은 일반적으로 '여기에 데이터를 입력할 준비를 하라'는 것을 의미하기 때문에 요소 포커싱이 이뤄지는 순간엔 요구사항을 충족시키는 초기화 코드를 실행할 수 있습니다. @@ -18,8 +22,13 @@ 예시에서 각 핸들러는 다음과 같은 역할을 합니다. +<<<<<<< HEAD - `blur` 핸들러에선 필드에 이메일이 잘 입력되었는지 확인하고 잘 입력되지 않은 경우엔 에러를 보여줍니다. - `focus` 핸들러에선 에러 메시지를 숨깁니다(이메일 재확인은 `blur` 핸들러에서 합니다). +======= +- The `blur` handler checks if the field has an email entered, and if not -- shows an error. +- The `focus` handler hides the error message (on `blur` it will be checked again): +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```html run autorun height=60 startContainer (<p>.firstChild)startOffset (=2)commonAncestorContainer (<p>)endContainer (<b>.firstChild)endOffset (=3) \ No newline at end of file +startContainer (<p>.firstChild)startOffset (=2)commonAncestorContainer (<p>)endContainer (<b>.firstChild)endOffset (=3) \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg b/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg new file mode 100644 index 0000000000..90054cef10 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg @@ -0,0 +1 @@ +<p>Hello</p>p.firstChild \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md index b2279c2d70..30d6457c55 100644 --- a/2-ui/99-ui-misc/03-event-loop/article.md +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -9,7 +9,11 @@ ## 이벤트 루프 +<<<<<<< HEAD *이벤트 루프(event loop)* 정의는 아주 간단합니다. 이벤트 루프는 태스크가 들어오길 기다렸다가 태스크가 들어오면 이를 처리하고, 처리할 태스크가 없는 경우엔 잠드는, 끊임없이 돌아가는 자바스크립트 내 루프입니다(task는 '작업'이라고 번역할 수 있는데, 매크로·마이크로태스크 등의 용어와 일치시키기 위해 '태스크'라고 음차 번역하였습니다 - 옮긴이). +======= +The *event loop* concept is very simple. There's an endless loop, where the JavaScript engine waits for tasks, executes them and then sleeps, waiting for more tasks. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 자바스크립트 엔진이 돌아가는 알고리즘을 일반화하면 다음과 같습니다. @@ -18,7 +22,11 @@ 2. 처리해야 할 태스크가 없는 경우: - 잠들어 있다가 새로운 태스크가 추가되면 다시 1로 돌아감 +<<<<<<< HEAD 바로 이 알고리즘이 우리가 브라우저를 사용해 인터넷을 서핑할 때 돌아가는 알고리즘입니다. 이렇게 자바스크립트 엔진은 대부분의 시간 동안 아무런 일도 하지 않고 쉬고 있다가 스크립트나 핸들러, 이벤트가 활성화될 때만 돌아갑니다. +======= +That's a formalization for what we see when browsing a page. The JavaScript engine does nothing most of the time, it only runs if a script/handler/event activates. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 그렇다면 자바스크립트 엔진을 활성화하는 태스크엔 과연 어떤 것들이 있을까요? 대표적인 태스크는 다음과 같습니다. @@ -41,11 +49,19 @@ 지금까진 어려운 것이 없어 보입니다. 그렇죠? +<<<<<<< HEAD 여기서 잠시 두 가지 세부 사항을 짚고 넘어갑시다. 1. 엔진이 특정 태스크를 처리하는 동안엔 렌더링이 절대 일어나지 않습니다. 태스크를 처리하는 데 걸리는 시간이 길지 않으면 이는 전혀 문제가 되지 않습니다. 처리가 끝나는 대로 DOM 변경을 화면에 반영하면 되기 때문입니다. 2. 태스크 처리에 긴 시간이 걸리면, 브라우저는 태스크를 처리하는 동안에 발생한 사용자 이벤트 등의 새로운 태스크들을 처리하지 못합니다. 인터넷 서핑을 하다 보면 '응답 없는 페이지(Page Unresponsive)'라는 얼럿 창을 만나게 되는 경우가 종종 있습니다. 이 얼럿 창은 아주 복잡한 계산이 필요하거나 프로그래밍 에러 때문에 무한 루프에 빠지게 될 때 나타나는데, 브라우저는 얼럿 창을 통해 사용자에게 페이지 전체와 함께 해당 태스크를 취소시킬지 말지를 선택하도록 유도합니다. 자, 이론을 충분히 살펴봤으니 지금부턴 이 지식을 실무에서 어떻게 활용할 수 있을지 알아보도록 합시다. +======= +Two more details: +1. Rendering never happens while the engine executes a task. It doesn't matter if the task takes a long time. Changes to the DOM are painted only after the task is complete. +2. If a task takes too long, the browser can't do other tasks, such as processing user events. So after a time, it raises an alert like "Page Unresponsive", suggesting killing the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to an infinite loop. + +That was the theory. Now let's see how we can apply that knowledge. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 유스 케이스 1: CPU 소모가 많은 태스크 쪼개기 @@ -161,7 +177,11 @@ count(); 태스크를 여러 개로 쪼갤 때의 장점은 진행 상태를 나타내주는 프로그레스 바(progress bar)를 만들 때도 드러납니다. +<<<<<<< HEAD 브라우저는 스크립트 실행 시간이 오래 걸리든 아니든 상관없이 대개 실행 중인 코드의 처리가 끝난 이후에 렌더링 작업을 합니다. +======= +As mentioned earlier, changes to DOM are painted only after the currently running task is completed, irrespective of how long it takes. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 함수를 사용해 원하는 만큼의 요소를 만들고 이 요소들을 하나하나 문서에 추가한 다음, 각 요소의 스타일을 변경할 수 있다는 점에서 이런 브라우저 동작 방식은 한편으론 아주 유용합니다. 모든 작업이 이뤄지는 동안 사용자는 완성되지 않은 '중간' 상태의 화면을 보지 않아도 되기 때문입니다. @@ -239,7 +259,11 @@ menu.onclick = function() { ## 매크로태스크와 마이크로태스크 +<<<<<<< HEAD 태스크는 이번 챕터에서 설명한 *매크로태스크(macrotask)* 와 챕터에서 다룬 *마이크로태스크(microtask)* 로 나뉩니다. +======= +Along with *macrotasks*, described in this chapter, there are *microtasks*, mentioned in the chapter . +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 마이크로태스크는 코드를 사용해서만 만들 수 있는데, 주로 프라미스를 사용해 만듭니다. 프라미스와 함께 쓰이는 `.then/catch/finally` 핸들러가 마이크로태스크가 되죠. 여기에 더하여 마이크로태스크는 프라미스를 핸들링하는 또 다른 형태의 문법인 `await`를 사용해 만들어지기도 합니다. @@ -304,7 +328,11 @@ alert("code"); ## 요약 +<<<<<<< HEAD 이벤트 루프 알고리즘을 요약하면 다음과 같습니다(자세한 사항은 [명세서](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)에서 확인할 수 있습니다). +======= +A more detailed event loop algorithm (though still simplified compared to the [specification](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)): +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 1. *매크로태스크* 큐에서 가장 오래된 태스크를 꺼내 실행합니다(예: 스크립트를 실행). 2. 모든 *마이크로태스크*를 실행합니다. @@ -317,7 +345,11 @@ alert("code"); 새로운 *매크로태스크*를 스케줄링하는 방법은 다음과 같습니다. - 지연시간이 0인 `setTimeout(f)` 사용하기 +<<<<<<< HEAD 이 방법을 사용하면 계산이 복잡한 큰 태스크 하나를 여러 개로 쪼갤 수 있습니다. 태스크를 여러 개로 쪼개면 태스크 중간중간 사용자 이벤트에 반응할 수 있고, 작업 진척 상태를 화면에 표시해줄 수도 있습니다. +======= +That may be used to split a big calculation-heavy task into pieces, for the browser to be able to react to user events and show progress between them. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 지연시간이 0인 `setTimeout`은 이벤트가 완전히 처리되고 난 후(버블링이 끝난 후)에 특정 작업을 수행하도록 스케줄링할 때도 사용됩니다. diff --git a/3-frames-and-windows/01-popup-windows/article.md b/3-frames-and-windows/01-popup-windows/article.md index 7540b03045..d32715f87e 100644 --- a/3-frames-and-windows/01-popup-windows/article.md +++ b/3-frames-and-windows/01-popup-windows/article.md @@ -192,7 +192,7 @@ newWindow.onload = function() { ``` -## Scrolling and resizing +## Moving and resizing There are methods to move/resize a window: diff --git a/3-frames-and-windows/03-cross-window-communication/article.md b/3-frames-and-windows/03-cross-window-communication/article.md index 53f5f55fc4..091a0cecbb 100644 --- a/3-frames-and-windows/03-cross-window-communication/article.md +++ b/3-frames-and-windows/03-cross-window-communication/article.md @@ -263,7 +263,7 @@ The window that wants to send a message calls [postMessage](mdn:api/Window.postM Arguments: `data` -: The data to send. Can be any object, the data is cloned using the "structured cloning algorithm". IE supports only strings, so we should `JSON.stringify` complex objects to support that browser. +: The data to send. Can be any object, the data is cloned using the "structured serialization algorithm". IE supports only strings, so we should `JSON.stringify` complex objects to support that browser. `targetOrigin` : Specifies the origin for the target window, so that only a window from the given origin will get the message. diff --git a/4-binary/01-arraybuffer-binary-arrays/article.md b/4-binary/01-arraybuffer-binary-arrays/article.md index 6e6ea8022f..accd3c5053 100644 --- a/4-binary/01-arraybuffer-binary-arrays/article.md +++ b/4-binary/01-arraybuffer-binary-arrays/article.md @@ -34,7 +34,7 @@ A view object does not store anything on it's own. It's the "eyeglasses" that gi For instance: -- **`Uint8Array`** -- treats each byte in `ArrayBuffer` as a separate number, with possible values are from 0 to 255 (a byte is 8-bit, so it can hold only that much). Such value is called a "8-bit unsigned integer". +- **`Uint8Array`** -- treats each byte in `ArrayBuffer` as a separate number, with possible values from 0 to 255 (a byte is 8-bit, so it can hold only that much). Such value is called a "8-bit unsigned integer". - **`Uint16Array`** -- treats every 2 bytes as an integer, with possible values from 0 to 65535. That's called a "16-bit unsigned integer". - **`Uint32Array`** -- treats every 4 bytes as an integer, with possible values from 0 to 4294967295. That's called a "32-bit unsigned integer". - **`Float64Array`** -- treats every 8 bytes as a floating point number with possible values from 5.0x10-324 to 1.8x10308. @@ -77,7 +77,7 @@ Please note, there's no constructor called `TypedArray`, it's just a common "umb When you see something like `new TypedArray`, it means any of `new Int8Array`, `new Uint8Array`, etc. -Typed array behave like regular arrays: have indexes and iterable. +Typed arrays behave like regular arrays: have indexes and are iterable. A typed array constructor (be it `Int8Array` or `Float64Array`, doesn't matter) behaves differently depending on argument types. @@ -209,7 +209,7 @@ These methods allow us to copy typed arrays, mix them, create new arrays from ex ## DataView -[DataView](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView) is a special super-flexible "untyped" view over `ArrayBuffer`. It allows to access the data on any offset in any format. +[DataView](mdn:/JavaScript/Reference/Global_Objects/DataView) is a special super-flexible "untyped" view over `ArrayBuffer`. It allows to access the data on any offset in any format. - For typed arrays, the constructor dictates what the format is. The whole array is supposed to be uniform. The i-th number is `arr[i]`. - With `DataView` we access the data with methods like `.getUint8(i)` or `.getUint16(i)`. We choose the format at method call time instead of the construction time. @@ -259,7 +259,7 @@ To do almost any operation on `ArrayBuffer`, we need a view. - `Float32Array`, `Float64Array` -- for signed floating-point numbers of 32 and 64 bits. - Or a `DataView` -- the view that uses methods to specify a format, e.g. `getUint8(offset)`. -In most cases we create and operate directly on typed arrays, leaving `ArrayBuffer` under cover, as a "common discriminator". We can access it as `.buffer` and make another view if needed. +In most cases we create and operate directly on typed arrays, leaving `ArrayBuffer` under cover, as a "common denominator". We can access it as `.buffer` and make another view if needed. There are also two additional terms, that are used in descriptions of methods that operate on binary data: - `ArrayBufferView` is an umbrella term for all these kinds of views. diff --git a/4-binary/02-text-decoder/article.md b/4-binary/02-text-decoder/article.md index 619eb9d2d3..1604f3b5ba 100644 --- a/4-binary/02-text-decoder/article.md +++ b/4-binary/02-text-decoder/article.md @@ -9,10 +9,17 @@ let decoder = new TextDecoder([label], [options]); ``` +<<<<<<< HEAD - **`label`** -- 기본적인 인코딩 방식은 `utf-8`이지만 `big5`, `windows-1251` 및 다른 인코딩 방식도 지원됩니다. - **`options`** -- 선택 항목입니다. - **`fatal`** -- 불린 값. `true`인 경우, 잘못된 글자(디코딩 불가능한 글자)를 대상으로 예외를 던집니다. `false(기본값)`인 경우, 글자를 `\uFFFD`로 대체합니다. - **`ignoreBOM`** -- 불린 값이 `true`인 경우 사용되지 않는 바이트 순서 표식(Byte Order Mark, BOM)을 무시합니다. +======= +- **`label`** -- the encoding, `utf-8` by default, but `big5`, `windows-1251` and many other are also supported. +- **`options`** -- optional object: + - **`fatal`** -- boolean, if `true` then throw an exception for invalid (non-decodable) characters, otherwise (default) replace them with character `\uFFFD`. + - **`ignoreBOM`** -- boolean, if `true` then ignore BOM (an optional byte-order Unicode mark), rarely needed. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 그런 다음 생성했던 객체를 디코딩합니다. diff --git a/4-binary/03-blob/article.md b/4-binary/03-blob/article.md index 84cf6f1cca..5031afa328 100644 --- a/4-binary/03-blob/article.md +++ b/4-binary/03-blob/article.md @@ -55,7 +55,7 @@ This behavior is similar to JavaScript strings: we can't change a character in a ## Blob as URL -A Blob can be easily used as an URL for ``, `` or other tags, to show its contents. +A Blob can be easily used as a URL for ``, `` or other tags, to show its contents. Thanks to `type`, we can also download/upload `Blob` objects, and the `type` naturally becomes `Content-Type` in network requests. @@ -74,7 +74,7 @@ link.href = URL.createObjectURL(blob); We can also create a link dynamically in JavaScript and simulate a click by `link.click()`, then download starts automatically. -Here's the similar code that causes user to download the dynamicallly created `Blob`, without any HTML: +Here's the similar code that causes user to download the dynamically created `Blob`, without any HTML: ```js run let link = document.createElement('a'); @@ -99,7 +99,7 @@ blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273 For each URL generated by `URL.createObjectURL` the browser stores a URL -> `Blob` mapping internally. So such URLs are short, but allow to access the `Blob`. -A generated URL (and hence the link with it) is only valid within the current document, while it's open. And it allows to reference the `Blob` in ``, ``, basically any other object that expects an url. +A generated URL (and hence the link with it) is only valid within the current document, while it's open. And it allows to reference the `Blob` in ``, ``, basically any other object that expects a URL. There's a side-effect though. While there's a mapping for a `Blob`, the `Blob` itself resides in the memory. The browser can't free it. @@ -119,7 +119,7 @@ An alternative to `URL.createObjectURL` is to convert a `Blob` into a base64-enc That encoding represents binary data as a string of ultra-safe "readable" characters with ASCII-codes from 0 to 64. And what's more important -- we can use this encoding in "data-urls". -A [data url](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) has the form `data:[][;base64],`. We can use such urls everywhere, on par with "regular" urls. +A [data url](mdn:/http/Data_URIs) has the form `data:[][;base64],`. We can use such urls everywhere, on par with "regular" urls. For instance, here's a smiley: @@ -151,7 +151,7 @@ reader.onload = function() { }; ``` -Both ways of making an URL of a `Blob` are usable. But usually `URL.createObjectURL(blob)` is simpler and faster. +Both ways of making a URL of a `Blob` are usable. But usually `URL.createObjectURL(blob)` is simpler and faster. ```compare title-plus="URL.createObjectURL(blob)" title-minus="Blob to data url" + We need to revoke them if care about memory. @@ -166,8 +166,8 @@ We can create a `Blob` of an image, an image part, or even make a page screensho Image operations are done via `` element: -1. Draw an image (or its part) on canvas using [canvas.drawImage](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage). -2. Call canvas method [.toBlob(callback, format, quality)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) that creates a `Blob` and runs `callback` with it when done. +1. Draw an image (or its part) on canvas using [canvas.drawImage](mdn:/api/CanvasRenderingContext2D/drawImage). +2. Call canvas method [.toBlob(callback, format, quality)](mdn:/api/HTMLCanvasElement/toBlob) that creates a `Blob` and runs `callback` with it when done. In the example below, an image is just copied, but we could cut from it, or transform it on canvas prior to making a blob: @@ -186,7 +186,7 @@ let context = canvas.getContext('2d'); context.drawImage(img, 0, 0); // we can context.rotate(), and do many other things on canvas -// toBlob is async opereation, callback is called when done +// toBlob is async operation, callback is called when done canvas.toBlob(function(blob) { // blob ready, download it let link = document.createElement('a'); @@ -235,7 +235,7 @@ That makes Blobs convenient for upload/download operations, that are so common i Methods that perform web-requests, such as [XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch) and so on, can work with `Blob` natively, as well as with other binary types. -We can easily convert betweeen `Blob` and low-level binary data types: +We can easily convert between `Blob` and low-level binary data types: - We can make a Blob from a typed array using `new Blob(...)` constructor. - We can get back `ArrayBuffer` from a Blob using `FileReader`, and then create a view over it for low-level binary processing. diff --git a/5-network/01-fetch/article.md b/5-network/01-fetch/article.md index 3f6f3a9d49..a4f5d4fc8b 100644 --- a/5-network/01-fetch/article.md +++ b/5-network/01-fetch/article.md @@ -27,7 +27,11 @@ let promise = fetch(url, [options]) - **`url`** -- 접근하고자 하는 URL - **`options`** -- 선택 매개변수, method나 header 등을 지정할 수 있음 +<<<<<<< HEAD `options`에 아무것도 넘기지 않으면 요청은 `GET` 메서드로 진행되어 `url`로부터 콘텐츠가 다운로드 됩니다. +======= +Without `options`, this is a simple GET request, downloading the contents of the `url`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 `fetch()`를 호출하면 브라우저는 네트워크 요청을 보내고 프라미스가 반환됩니다. 반환되는 프라미스는 `fetch()`를 호출하는 코드에서 사용됩니다. @@ -61,12 +65,21 @@ if (response.ok) { // HTTP 상태 코드가 200~299일 경우 `response` 에는 프라미스를 기반으로 하는 다양한 메서드가 있습니다. 이 메서드들을 사용하면 다양한 형태의 응답 본문을 처리할 수 있습니다. +<<<<<<< HEAD - **`response.text()`** -- 응답을 읽고 텍스트를 반환합니다, - **`response.json()`** -- 응답을 JSON 형태로 파싱합니다, - **`response.formData()`** -- 응답을 `FormData` 객체 형태로 반환합니다. `FormData`에 대한 자세한 내용은 [다음 챕터](info:formdata)에서 다루겠습니다. - **`response.blob()`** -- 응답을 [Blob](info:blob)(타입이 있는 바이너리 데이터) 형태로 반환합니다. - **`response.arrayBuffer()`** -- 응답을 [ArrayBuffer](info:arraybuffer-binary-arrays)(바이너리 데이터를 로우 레벨 형식으로 표현한 것) 형태로 반환합니다. - 이 외에도 `response.body`가 있는데, [ReadableStream](https://streams.spec.whatwg.org/#rs-class) 객체인 `response.body`를 사용하면 응답 본문을 청크 단위로 읽을 수 있습니다. 자세한 용례는 곧 살펴보겠습니다. +======= +- **`response.text()`** -- read the response and return as text, +- **`response.json()`** -- parse the response as JSON, +- **`response.formData()`** -- return the response as `FormData` object (explained in the [next chapter](info:formdata)), +- **`response.blob()`** -- return the response as [Blob](info:blob) (binary data with type), +- **`response.arrayBuffer()`** -- return the response as [ArrayBuffer](info:arraybuffer-binary-arrays) (low-level representation of binary data), +- additionally, `response.body` is a [ReadableStream](https://streams.spec.whatwg.org/#rs-class) object, it allows you to read the body chunk-by-chunk, we'll see an example later. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 지금까지 배운 내용을 토대로 GitHub에서 마지막 커밋을 JSON 객체 형태로 받아봅시다. diff --git a/5-network/02-formdata/article.md b/5-network/02-formdata/article.md index d281d07561..a73d554b17 100644 --- a/5-network/02-formdata/article.md +++ b/5-network/02-formdata/article.md @@ -47,7 +47,7 @@ As you can see, that's almost one-liner: ``` -In this example, the server code is not presented, as it's beyound our scope. The server accepts the POST request and replies "User saved". +In this example, the server code is not presented, as it's beyond our scope. The server accepts the POST request and replies "User saved". ## FormData Methods @@ -75,7 +75,7 @@ formData.append('key2', 'value2'); // List key/value pairs for(let [name, value] of formData) { - alert(`${name} = ${value}`); // key1=value1, then key2=value2 + alert(`${name} = ${value}`); // key1 = value1, then key2 = value2 } ``` @@ -168,7 +168,7 @@ The server reads form data and the file, as if it were a regular form submission [FormData](https://xhr.spec.whatwg.org/#interface-formdata) objects are used to capture HTML form and submit it using `fetch` or another network method. -We can either create `new FormData(form)` from an HTML form, or create a object without a form at all, and then append fields with methods: +We can either create `new FormData(form)` from an HTML form, or create an object without a form at all, and then append fields with methods: - `formData.append(name, value)` - `formData.append(name, blob, fileName)` diff --git a/5-network/03-fetch-progress/article.md b/5-network/03-fetch-progress/article.md index 2d003157d5..76b05d5149 100644 --- a/5-network/03-fetch-progress/article.md +++ b/5-network/03-fetch-progress/article.md @@ -5,11 +5,11 @@ The `fetch` method allows to track *download* progress. Please note: there's currently no way for `fetch` to track *upload* progress. For that purpose, please use [XMLHttpRequest](info:xmlhttprequest), we'll cover it later. -To track download progress, we can use `response.body` property. It's `ReadableStream` -- a special object that provides body chunk-by-chunk, as it comes. Readable streams are described in the [Streams API](https://streams.spec.whatwg.org/#rs-class) specification. +To track download progress, we can use `response.body` property. It's a `ReadableStream` -- a special object that provides body chunk-by-chunk, as it comes. Readable streams are described in the [Streams API](https://streams.spec.whatwg.org/#rs-class) specification. Unlike `response.text()`, `response.json()` and other methods, `response.body` gives full control over the reading process, and we can count how much is consumed at any moment. -Here's the sketch of code that reads the reponse from `response.body`: +Here's the sketch of code that reads the response from `response.body`: ```js // instead of response.json() and other methods @@ -110,3 +110,5 @@ Let's explain that step-by-step: At the end we have the result (as a string or a blob, whatever is convenient), and progress-tracking in the process. Once again, please note, that's not for *upload* progress (no way now with `fetch`), only for *download* progress. + +Also, if the size is unknown, we should check `receivedLength` in the loop and break it once it reaches a certain limit. So that the `chunks` won't overflow the memory. diff --git a/5-network/04-fetch-abort/article.md b/5-network/04-fetch-abort/article.md index 6548f81d27..b142c9b744 100644 --- a/5-network/04-fetch-abort/article.md +++ b/5-network/04-fetch-abort/article.md @@ -18,15 +18,24 @@ let controller = new AbortController(); A controller is an extremely simple object. - It has a single method `abort()`, +<<<<<<< HEAD - And a single property `signal` that allows to set event liseners on it. +======= +- And a single property `signal` that allows to set event listeners on it. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 When `abort()` is called: - `controller.signal` emits the `"abort"` event. - `controller.signal.aborted` property becomes `true`. Generally, we have two parties in the process: +<<<<<<< HEAD 1. The one that performs an cancelable operation, it sets a listener on `controller.signal`. 2. The one one that cancels: it calls `controller.abort()` when needed. +======= +1. The one that performs a cancelable operation, it sets a listener on `controller.signal`. +2. The one that cancels: it calls `controller.abort()` when needed. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 Here's the full example (without `fetch` yet): @@ -35,7 +44,11 @@ let controller = new AbortController(); let signal = controller.signal; // The party that performs a cancelable operation +<<<<<<< HEAD // gets "signal" object +======= +// gets the "signal" object +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 // and sets the listener to trigger when controller.abort() is called signal.addEventListener('abort', () => alert("abort!")); @@ -46,6 +59,7 @@ controller.abort(); // abort! alert(signal.aborted); // true ``` +<<<<<<< HEAD As we can see, `AbortController` is just a means to pass `abort` events when `abort()` is called on it. We could implement same kind of event listening in our code on our own, without `AbortController` object at all. @@ -55,6 +69,17 @@ But what's valuable is that `fetch` knows how to work with `AbortController` obj ## Using with fetch To become able to cancel `fetch`, pass the `signal` property of an `AbortController` as a `fetch` option: +======= +As we can see, `AbortController` is just a mean to pass `abort` events when `abort()` is called on it. + +We could implement the same kind of event listening in our code on our own, without the `AbortController` object. + +But what's valuable is that `fetch` knows how to work with the `AbortController` object. It's integrated in it. + +## Using with fetch + +To be able to cancel `fetch`, pass the `signal` property of an `AbortController` as a `fetch` option: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js let controller = new AbortController(); @@ -65,7 +90,11 @@ fetch(url, { The `fetch` method knows how to work with `AbortController`. It will listen to `abort` events on `signal`. +<<<<<<< HEAD Now, to to abort, call `controller.abort()`: +======= +Now, to abort, call `controller.abort()`: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js controller.abort(); @@ -97,7 +126,11 @@ try { ## AbortController is scalable +<<<<<<< HEAD `AbortController` is scalable, it allows to cancel multiple fetches at once. +======= +`AbortController` is scalable. It allows to cancel multiple fetches at once. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 Here's a sketch of code that fetches many `urls` in parallel, and uses a single controller to abort them all: @@ -113,7 +146,7 @@ let fetchJobs = urls.map(url => fetch(url, { let results = await Promise.all(fetchJobs); -// if controller.abort() is called from elsewhere, +// if controller.abort() is called from anywhere, // it aborts all fetches ``` @@ -137,12 +170,17 @@ let fetchJobs = urls.map(url => fetch(url, { // fetches // Wait for fetches and our task in parallel let results = await Promise.all([...fetchJobs, ourJob]); -// if controller.abort() is called from elsewhere, +// if controller.abort() is called from anywhere, // it aborts all fetches and ourJob ``` ## Summary +<<<<<<< HEAD - `AbortController` is a simple object that generates `abort` event on it's `signal` property when `abort()` method is called (and also sets `signal.aborted` to `true`). - `fetch` integrates with it: we pass `signal` property as the option, and then `fetch` listens to it, so it becomes possible to abort the `fetch`. +======= +- `AbortController` is a simple object that generates an `abort` event on it's `signal` property when the `abort()` method is called (and also sets `signal.aborted` to `true`). +- `fetch` integrates with it: we pass the `signal` property as the option, and then `fetch` listens to it, so it's possible to abort the `fetch`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 - We can use `AbortController` in our code. The "call `abort()`" -> "listen to `abort` event" interaction is simple and universal. We can use it even without `fetch`. diff --git a/5-network/05-fetch-crossorigin/article.md b/5-network/05-fetch-crossorigin/article.md index b46007460b..4e4d662364 100644 --- a/5-network/05-fetch-crossorigin/article.md +++ b/5-network/05-fetch-crossorigin/article.md @@ -28,7 +28,11 @@ CORS는 악의를 가진 해커로부터 인터넷을 보호하기 위해 만들 **과거 수 년 동안, 한 사이트의 스크립트에서 다른 사이트에 있는 콘텐츠에 접근할 수 없다는 제약이 있었습니다.** +<<<<<<< HEAD 이런 간단하지만 강력한 규칙은 인터넷 보안을 위한 근간이었습니다. 보안 규칙 덕분에 해커가 만든 웹 사이트 `hacker.com`에서 `gmail.com`에 있는 메일 박스에 접근할 수 없던 것이죠. 사람들은 이런 제약 덕분에 안전하게 인터넷을 사용할 수 있었습니다. +======= +That simple, yet powerful rule was a foundation of the internet security. E.g. an evil script from website `hacker.com` could not access the user's mailbox at website `gmail.com`. People felt safe. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 그런데 이 당시의 자바스크립트는 네트워크 요청을 보낼 수 있을 만한 메서드를 지원하지 않았습니다. 자바스크립트는 웹 페이지를 꾸미기 위한 토이 랭귀지 수준이었죠. @@ -97,10 +101,15 @@ CORS는 악의를 가진 해커로부터 인터넷을 보호하기 위해 만들 처음 네트워크 요청 메서드가 등장했을 때엔 크로스 오리진 요청이 불가능했습니다. 하지만 긴 논의 끝에 크로스 오리진 요청을 허용하기로 결정합니다. 대신 크로스 오리진 요청은 서버에서 명시적으로 크로스 오리진 요청을 '허가' 했다는 것을 알려주는 특별한 헤더를 전송받았을 때만 가능하도록 제약을 걸게 됩니다. +<<<<<<< HEAD ## 안전한 요청 +======= +## Safe requests +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 크로스 오리진 요청은 크게 두 가지 종류로 구분됩니다. +<<<<<<< HEAD 1. 안전한 요청(safe request) 2. 그 외의 요청(안전한 요청이 아닌 요청) @@ -120,20 +129,55 @@ CORS는 악의를 가진 해커로부터 인터넷을 보호하기 위해 만들 안전한 요청과 그렇지 않은 요청의 근본적인 차이는 **특별한 방법을 사용하지 않고도 ``이나 ` ``` +<<<<<<< HEAD 이제 본격적으로 각 프로퍼티를 살펴봅시다. ## transition-property @@ -77,6 +92,15 @@ growing.onclick = function() { `transition-property` 프로퍼티엔 `left`, `margin-left`, `height`, `color` 같이 애니메이션 효과를 적용할 프로퍼티 목록을 정의합니다. 이때 주의할 점은 모든 프로퍼티에 애니메이션 효과를 적용할 수 없다는 사실입니다. 참고로 [흔히 사용되는 프로퍼티 대다수엔](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties) 애니메이션 효과를 적용할 수 있습니다. +======= +Now, let's cover animation properties one by one. + +## transition-property + +In `transition-property`, we write a list of properties to animate, for instance: `left`, `margin-left`, `height`, `color`. Or we could write `all`, which means "animate all properties". + +Do note that, there are properties which can not be animated. However, [most of the generally used properties are animatable](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties). +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## transition-duration @@ -84,11 +108,19 @@ growing.onclick = function() { ## transition-delay +<<<<<<< HEAD `transition-delay` 프로퍼티엔 애니메이션 효과가 *시작되기 전*에 얼마만큼의 지연 시간을 줄지 설정합니다. `transition-delay` 값을 `1s`로 설정하면 애니메이션 효과가 1초 후에 나타납니다. `transition-delay`엔 음수 값도 넣을 수 있습니다. 값이 음수일 땐 애니메이션 효과가 중간부터 나타납니다. `transition-duration`을 `2s`, 지연 시간을 `-1s`로 설정하면 애니메이션 효과는 1초가 지난 후 1초 동안 지속됩니다. CSS `translate` 프로퍼티와 지금까지 배운 내용을 사용해 `0`부터 `9`까지가 화면에 자연스럽게 나타나도록 해봅시다. +======= +In `transition-delay` we can specify the delay *before* the animation. For instance, if `transition-delay` is `1s` and `transition-duration` is `2s`, then the animation starts 1 second after the property change and the total duration will be 2 seconds. + +Negative values are also possible. Then the animation is shown immediately, but the starting point of the animation will be after given value (time). For example, if `transition-delay` is `-1s` and `transition-duration` is `2s`, then animation starts from the halfway point and total duration will be 1 second. + +Here the animation shifts numbers from `0` to `9` using CSS `translate` property: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 [codetabs src="digits"] @@ -108,13 +140,21 @@ CSS `translate` 프로퍼티와 지금까지 배운 내용을 사용해 `0`부 stripe.classList.add('animate'); ``` +<<<<<<< HEAD 이번엔 `transition-delay`에 음수를 써서 예시를 약간 변형해봅시다. 현재 시각을 기준으로 '초'를 추출하고, 이 값에 마이너스 기호를 붙여서 `transition-delay` 값으로 지정하면 현재 초를 기준으로 숫자가 나타나고, 애니메이션 효과가 적용되는 것을 확인할 수 있습니다. +======= +We could also start it from somewhere in the middle of the transition, from an exact number, e.g. corresponding to the current second, using a negative `transition-delay`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 직접 숫자를 클릭해보세요. 현재 날짜가 2020년 9월 12일 오후 12시 17분 8초라면, 숫자 8부터 스르륵 이동합니다. [codetabs src="digits-negative-delay"] +<<<<<<< HEAD 사용한 코드는 아래와 같습니다. +======= +JavaScript does it with an extra line: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js stripe.onclick = function() { @@ -129,26 +169,49 @@ stripe.onclick = function() { ## transition-timing-function +<<<<<<< HEAD `transition-timing-function` 프로퍼티를 사용해 timing 함수를 만들면 시간에 따라 애니메이션 효과를 어떻게 분배할지를 설정할 수 있습니다. 애니메이션 효과가 초반엔 천천히 나타나다가 나중엔 빠르게 나타나게 할 수 있고, 물론 이 반대도 가능합니다. 처음 이 프로퍼티를 접하면 애니메이션 프로퍼티 중 가장 복잡한 프로퍼티 같다고 느낄 수 있습니다. 그렇지만 시간을 조금 투자해 학습하면 매우 간단한 프로퍼티라는 생각이 들 겁니다. `transition-timing-function` 프로퍼티 값엔 베지어 곡선(bezier curve)이나 단계(step)가 올 수 있습니다. 먼저 사용 빈도가 높은 베지어 곡선부터 알아봅시다. +======= +The timing function describes how the animation process is distributed along its timeline. Will it start slowly and then go fast, or vice versa. + +It appears to be the most complicated property at first. But it becomes very simple if we devote a bit time to it. + +That property accepts two kinds of values: a Bezier curve or steps. Let's start with the curve, as it's used more often. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ### 베지어 곡선 +<<<<<<< HEAD `transition-timing-function`엔 조절점이 4개면서 아래 조건을 만족하는 [베지어 곡선](/bezier-curve)을 지정할 수 있습니다. 1. 첫 번째 조절점: `(0,0)` 2. 마지막 조절점: `(1,1)` 3. 중간 조절점들: `x`가 `0`과 `1` 사이에 있어야 함. `y`엔 제약이 없음 +======= +The timing function can be set as a [Bezier curve](/bezier-curve) with 4 control points that satisfy the conditions: + +1. First control point: `(0,0)`. +2. Last control point: `(1,1)`. +3. For intermediate points, the values of `x` must be in the interval `0..1`, `y` can be anything. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 CSS에선 베지어 곡선을 `cubic-bezier(x2, y2, x3, y3)`형태로 정의합니다. 규칙에 따라 첫 번째 조절점은 `(0,0)`, 네 번째 조절점은 `(1,1)`로 고정되므로 두 번째와 세 번째 조절점만 설정하면 됩니다. +<<<<<<< HEAD 조절점을 변경해 만든 베지어 곡선을 사용해 정의한 timing 함수는 시간이 지남에 따라 얼마나 빠르게 애니메이션 효과가 나타나게 할지를 보여줍니다. - `x`축은 시간이 됩니다. `0`은 애니메이션 시작하는 시간, `1`은 끝나는 시간을 나타냅니다. - `y`축은 프로세스 완성 정도를 나타냅니다. `0`은 프로퍼티 시작 값, `1`은 최종값을 나타냅니다. +======= +The timing function describes how fast the animation process goes. + +- The `x` axis is the time: `0` -- the start, `1` -- the end of `transition-duration`. +- The `y` axis specifies the completion of the process: `0` -- the starting value of the property, `1` -- the final value. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 가장 간단한 timing 함수를 적용하면 애니메이션 효과가 일정한 속도로 나타납니다. `cubic-bezier(0, 0, 1, 1)`를 사용해 기울기가 1인 직선형 함수를 만들어봅시다. @@ -168,7 +231,11 @@ CSS에선 베지어 곡선을 `cubic-bezier(x2, y2, x3, y3)`형태로 정의합 .train { left: 0; transition: left 5s cubic-bezier(0, 0, 1, 1); +<<<<<<< HEAD /* left 속성값(450px)은 자바스크립트에서 설정함 */ +======= + /* click on a train sets left to 450px, thus triggering the animation */ +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 } ``` @@ -191,13 +258,21 @@ CSS: .train { left: 0; transition: left 5s cubic-bezier(0, .5, .5, 1); +<<<<<<< HEAD /* left 속성값(450px)은 자바스크립트에서 설정함 */ +======= + /* click on a train sets left to 450px, thus triggering the animation */ +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 } ``` 직접 베지어 곡선을 만드는 것 말고, 내장 곡선을 사용할 수도 있습니다. 내장 곡선엔 `linear`, `ease`, `ease-in`, `ease-out`, `ease-in-out` 등이 있습니다. +<<<<<<< HEAD `linear`는 위에서 본 직선 형태의 timing 함수를 쓸 때 사용된 `cubic-bezier(0, 0, 1, 1)`와 동일한 효과를 줍니다. +======= +The `linear` is a shorthand for `cubic-bezier(0, 0, 1, 1)` -- a straight line, which we described above. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 각 내장 곡선에 대응하는 베지어 곡선은 다음과 같습니다. @@ -215,22 +290,32 @@ CSS: .train { left: 0; transition: left 5s ease-out; - /* transition: left 5s cubic-bezier(0, .5, .5, 1); */ + /* same as transition: left 5s cubic-bezier(0, .5, .5, 1); */ } ``` 베지어 곡선 `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`을 사용한 것처럼 기차가 점점 느려지긴 하지만 프로세스 정도가 조금 다른 것을 확인할 수 있습니다. +<<<<<<< HEAD 한편, **베지어 곡선을 사용하면 애니메이션이 지정한 범위를 '넘어서' 적용되게 할 수 있습니다.** 베지어 곡선에서 중간 조절점의 `y` 좌표는 제약이 없습니다. 음수 또는 매우 큰 값도 가능하죠. 그런데 조절점의 `y` 좌표가 음수 또는 큰 값일 때 베지어 곡선은 매우 낮거나 높게 그려집니다. 이러면 애니메이션이 정상 범위를 벗어납니다. +======= +**A Bezier curve can make the animation exceed its range.** + +The control points on the curve can have any `y` coordinates: even negative or huge ones. Then the Bezier curve would also extend very low or high, making the animation go beyond its normal range. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 아래쪽 기차 예시에 다음과 같은 css를 적용했다고 가정해봅시다. ```css .train { left: 100px; transition: left 5s cubic-bezier(.5, -1, .5, 2); +<<<<<<< HEAD /* left 속성값(400px)은 자바스크립트에서 설정함 */ +======= + /* click on a train sets left to 450px */ +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 } ``` @@ -244,6 +329,7 @@ CSS: [codetabs src="train-over"] +<<<<<<< HEAD 왜 이렇게 이동하는지는 아래의 베지어 곡선 그래프를 보면 아주 명확히 알 수 있습니다. ![](bezier-train-over.svg) @@ -251,14 +337,31 @@ CSS: 두 번째 조절점의 `y` 좌표가 `0`보다 작아지고, 세 번째 조절점의 좌표가 `1`보다 커지면서 곡선이 '정상' 범위를 벗어난 것을 확인할 수 있습니다. '정상' 범위인 `0..1`을 벗어난 것이죠. 아시다시피, timing 함수의 `y`축은 '애니메이션 프로세스의 완성도'를 나타냅니다. `y`가 `0`일 땐 프로퍼티 시작 값을, `y`가 `1`일 땐 프로세스 종료 값을 의미하죠. 그렇기 때문에 `y`가 `0`보다 작아지면 `left` 프로퍼티가 시작 값인 100px 보다 낮게 설정되고, `y`가 `1`보다 커지면 `left` 프로퍼티가 끝값인 400px보다 커지게 됩니다. +======= +Why it happens is pretty obvious if we look at the graph of the given Bezier curve: + +![](bezier-train-over.svg) + +We moved the `y` coordinate of the 2nd point below zero, and for the 3rd point we made it over `1`, so the curve goes out of the "regular" quadrant. The `y` is out of the "standard" range `0..1`. + +As we know, `y` measures "the completion of the animation process". The value `y = 0` corresponds to the starting property value and `y = 1` -- the ending value. So values `y<0` move the property beyond the starting `left` and `y>1` -- past the final `left`. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 이 예시에선 `y` 값이 정상 범위를 크게 벗어나지 않기 때문에 '부드럽게' 변형이 일어납니다. 그런데 `y` 값이 `-99`나 `99`가 되면 기차가 앞·뒤로 아주 크게 움직이겠죠? +<<<<<<< HEAD 이쯤 되면 원하는 애니메이션 효과를 줄 수 있는 베지어 곡선은 어떻게 만들 수 있을지 의문이 들 겁니다. 베지어 곡선을 만들어주는 툴은 다양한데, 에서도 가능합니다. +======= +But how do we make a Bezier curve for a specific task? There are many tools. For instance, we can do it on the site . +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ### 단계 +<<<<<<< HEAD timing 함수 `steps(number of steps[, start/end])`를 사용하면 애니메이션을 여러 단계(step)로 나눌 수 있습니다. +======= +The timing function `steps(number of steps[, start/end])` allows splitting an transition into multiple steps. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 숫자를 사용해 예시를 만들어봅시다. @@ -285,7 +388,11 @@ timing 함수 `steps(9, start)`에서 첫 번째 인수는 단계의 수이므 두 번째 인수는 `start`나 `end` 중 하나입니다. +<<<<<<< HEAD 두 번째 인수가 `start`인 경우엔 애니메이션이 첫 번째 단계부터 바로 시작됩니다. +======= +The `start` means that in the beginning of animation we need to make the first step immediately. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 숫자를 클릭하면 숫자가 `1`로 바로 바뀌고(첫 번째 단계), 1초 후에 다음 숫자(다음 단계)로 바뀌는 것을 확인할 수 있습니다. @@ -299,10 +406,17 @@ timing 함수 `steps(9, start)`에서 첫 번째 인수는 단계의 수이므 반면 두 번째 인수가 `end`인 경우엔 애니메이션이 바로 시작하지 않고 각 초가 끝날 때 시작됩니다. +<<<<<<< HEAD 초별 프로세스 정도는 다음과 같습니다. - `0s` -- `0` - `1s` -- `-10%` (1초가 지난 후에 첫 단계가 수행됨) +======= +So the process for `steps(9, end)` would go like this: + +- `0s` -- `0` (during the first second nothing changes) +- `1s` -- `-10%` (first change at the end of the 1st second) +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 - `2s` -- `-20%` - ... - `9s` -- `-90%` @@ -324,11 +438,19 @@ CSS 애니메이션이 끝나면 `transitionend` 이벤트가 자동으로 트 `transitionend` 이벤트는 애니메이션이 끝났을 때 무언가를 하고 싶은 경우 많이 사용됩니다. 애니메이션 여러 개를 조합할 때도 자주 쓰이죠. +<<<<<<< HEAD 예를 들어봅시다. 아래 예시에서 배를 클릭하면 배가 오른쪽, 왼쪽으로 움직이는데 한번 왕복할 때마다 오른쪽으로 더 멀리 이동합니다. [iframe src="boat" height=300 edit link] 트랜지션이 종료될 때마다 방향을 뒤집는 함수 `go`가 다시 실행되면서 새로운 애니메이션이 시작되는 것이죠. +======= +For instance, the ship in the example below starts to sail there and back when clicked, each time farther and farther to the right: + +[iframe src="boat" height=300 edit link] + +The animation is initiated by the function `go` that re-runs each time the transition finishes, and flips the direction: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ```js boat.onclick = function() { @@ -337,11 +459,19 @@ boat.onclick = function() { function go() { if (times % 2) { +<<<<<<< HEAD // 오른쪽으로 가기 boat.classList.remove('back'); boat.style.marginLeft = 100 * times + 200 + 'px'; } else { // 왼쪽으로 가기 +======= + // sail to the right + boat.classList.remove('back'); + boat.style.marginLeft = 100 * times + 200 + 'px'; + } else { + // sail to the left +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 boat.classList.add('back'); boat.style.marginLeft = 100 * times - 200 + 'px'; } @@ -357,7 +487,11 @@ boat.onclick = function() { }; ``` +<<<<<<< HEAD 트렌지션과 관련된 이벤트는 몇 가지 특수 프로퍼티를 지원합니다. +======= +The event object for `transitionend` has a few specific properties: +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 `event.propertyName` : 애니메이션이 완료된 프로퍼티로, 동시에 여러 개의 프로퍼티에 애니메이션 효과를 줄 때 사용할 수 있습니다. @@ -369,7 +503,11 @@ boat.onclick = function() { CSS 문법인 `@keyframes`을 사용하면 간단한 애니메이션 여러 개를 한꺼번에 실행시킬 수 있습니다. +<<<<<<< HEAD `@keyframes`엔 애니메이션 '이름'과 무엇을, 언제, 어디서 움직일지를 설정할 수 있습니다. `@keyframes`에 적절한 값을 넣은 후엔 `animation` 프로퍼티를 사용해 원하는 요소에 커스텀 애니메이션을 적용할 수 있습니다. 물론 추가 매개변수도 지정할 수 있습니다. +======= +It specifies the "name" of the animation and rules - what, when and where to animate. Then using the `animation` property, we can attach the animation to the element and specify additional parameters for it. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 주석에 설명을 달아놓은 예시를 살펴봅시다. @@ -405,17 +543,107 @@ CSS 문법인 `@keyframes`을 사용하면 간단한 애니메이션 여러 개 `@keyframes`을 다루는 글이나 [명세서](https://drafts.csswg.org/css-animations/)를 읽으면 더 많은 정보를 찾을 수 있으니 참고해보시기 바랍니다. +<<<<<<< HEAD 참고로 사이트에 있는 특정 요소를 계속 정적인 형태로 움직이지 않는 한은 `@keyframes`를 쓸 일은 많지 않을 겁니다. +======= +You probably won't need `@keyframes` often, unless everything is in constant motion on your sites. + +## Performance + +Most CSS properties can be animated, because most of them are numeric values. For instance, `width`, `color`, `font-size` are all numbers. When you animate them, the browser gradually changes these numbers frame by frame, creating a smooth effect. + +However, not all animations will look as smooth as you'd like, because different CSS properties cost differently to change. + +In more technical details, when there's a style change, the browser goes through 3 steps to render the new look: + +1. **Layout**: re-compute the geometry and position of each element, then +2. **Paint**: re-compute how everything should look like at their places, including background, colors, +3. **Composite**: render the final results into pixels on screen, apply CSS transforms if they exist. + +During a CSS animation, this process repeats every frame. However, CSS properties that never affect geometry or position, such as `color`, may skip the Layout step. If a `color` changes, the browser doesn't calculate any new geometry, it goes to Paint -> Composite. And there are few properties that directly go to Composite. You can find a longer list of CSS properties and which stages they trigger at . + +The calculations may take time, especially on pages with many elements and a complex layout. And the delays are actually visible on most devices, leading to "jittery", less fluid animations. + +Animations of properties that skip the Layout step are faster. It's even better if Paint is skipped too. + +The `transform` property is a great choice, because: +- CSS transforms affect the target element box as a whole (rotate, flip, stretch, shift it). +- CSS transforms never affect neighbour elements. + +...So browsers apply `transform` "on top" of existing Layout and Paint calculations, in the Composite stage. + +In other words, the browser calculates the Layout (sizes, positions), paints it with colors, backgrounds, etc at the Paint stage, and then applies `transform` to element boxes that need it. + +Changes (animations) of the `transform` property never trigger Layout and Paint steps. More than that, the browser leverages the graphics accelerator (a special chip on the CPU or graphics card) for CSS transforms, thus making them very efficient. + +Luckily, the `transform` property is very powerful. By using `transform` on an element, you could rotate and flip it, stretch and shrink it, move it around, and [much more](https://developer.mozilla.org/docs/Web/CSS/transform#syntax). So instead of `left/margin-left` properties we can use `transform: translateX(…)`, use `transform: scale` for increasing element size, etc. + +The `opacity` property also never triggers Layout (also skips Paint in Mozilla Gecko). We can use it for show/hide or fade-in/fade-out effects. + +Paring `transform` with `opacity` can usually solve most of our needs, providing fluid, good-looking animations. + +For example, here clicking on the `#boat` element adds the class with `transform: translateX(300)` and `opacity: 0`, thus making it move `300px` to the right and disappear: + +```html run height=260 autorun no-beautify + + + + +``` + +Here's a more complex example, with `@keyframes`: + +```html run height=80 autorun no-beautify +

    click me to start / stop

    + +``` +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 요약 +<<<<<<< HEAD CSS 애니메이션을 사용하면 하나 또는 여러 CSS 프로퍼티를 부드럽게(부드럽지 않게도 가능) 변화시킬 수 있습니다. +======= +CSS animations allow smoothly (or step-by-step) animated changes of one or multiple CSS properties. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 CSS 애니메이션은 전환이 필요한 대다수의 경우에 큰 도움을 줍니다. 자바스크립트를 사용해도 전환 효과를 줄 수 있긴 한데 이에 대해선 다음 챕터에서 다룰 예정입니다. 참고로 CSS 애니메이션은 자바스크립트 애니메이션과 비교해 다음과 같은 장단점이 있습니다. ```compare plus="CSS animations" minus="JavaScript animations" +<<<<<<< HEAD + 간단한 애니메이션을 간단히 수행함 + 빠르고 CPU를 많이 소모하지 않음 - 자바스크립트 애니메이션보다 덜 유연함. 요소의 '폭발' 같은 특수한 애니메이션 로직을 구현할 수 없음 @@ -423,5 +651,16 @@ CSS 애니메이션은 전환이 필요한 대다수의 경우에 큰 도움을 ``` 사실 대부분의 애니메이션은 이번 챕터에 설명한 CSS 프로퍼티를 사용해 구현할 수 있습니다. 여기에 더하여 `transitionend` 이벤트를 사용해 애니메이션이 끝난 후에 실행시킬 자바스크립트 코드를 지정할 수도 있죠. +======= ++ Simple things done simply. ++ Fast and lightweight for CPU. +- JavaScript animations are flexible. They can implement any animation logic, like an "explosion" of an element. +- Not just property changes. We can create new elements in JavaScript as part of the animation. +``` + +In early examples in this chapter, we animate `font-size`, `left`, `width`, `height`, etc. In real life projects, we should use `transform: scale()` and `transform: translate()` for better performance. + +The majority of animations can be implemented using CSS as described in this chapter. And the `transitionend` event allows JavaScript to be run after the animation, so it integrates fine with the code. +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 하지만 좀 더 복잡한 케이스를 다루려면 자바스크립트 애니메이션을 알아야 하므로 다음 챕터에선 이를 다뤄보겠습니다. diff --git a/7-animation/3-js-animation/1-animate-ball/solution.md b/7-animation/3-js-animation/1-animate-ball/solution.md index 5d3f08eefb..0dc67b8bd7 100644 --- a/7-animation/3-js-animation/1-animate-ball/solution.md +++ b/7-animation/3-js-animation/1-animate-ball/solution.md @@ -2,7 +2,7 @@ To bounce we can use CSS property `top` and `position:absolute` for the ball ins The bottom coordinate of the field is `field.clientHeight`. The CSS `top` property refers to the upper edge of the ball. So it should go from `0` till `field.clientHeight - ball.clientHeight`, that's the final lowest position of the upper edge of the ball. -To to get the "bouncing" effect we can use the timing function `bounce` in `easeOut` mode. +To get the "bouncing" effect we can use the timing function `bounce` in `easeOut` mode. Here's the final code for the animation: diff --git a/7-animation/3-js-animation/article.md b/7-animation/3-js-animation/article.md index 0049543401..517b2481bc 100644 --- a/7-animation/3-js-animation/article.md +++ b/7-animation/3-js-animation/article.md @@ -77,7 +77,7 @@ setInterval(animate3, 20); These several independent redraws should be grouped together, to make the redraw easier for the browser and hence load less CPU load and look smoother. -There's one more thing to keep in mind. Sometimes when CPU is overloaded, or there are other reasons to redraw less often (like when the browser tab is hidden), so we really shouldn't run it every `20ms`. +There's one more thing to keep in mind. Sometimes CPU is overloaded, or there are other reasons to redraw less often (like when the browser tab is hidden), so we really shouldn't run it every `20ms`. But how do we know about that in JavaScript? There's a specification [Animation timing](http://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`. It addresses all these issues and even more. @@ -227,7 +227,7 @@ See in action (click to activate): [iframe height=40 src="quad" link] -...Or the cubic curve or event greater `n`. Increasing the power makes it speed up faster. +...Or the cubic curve or even greater `n`. Increasing the power makes it speed up faster. Here's the graph for `progress` in the power `5`: @@ -397,7 +397,7 @@ The effect is clearly seen if we compare the graphs of `easeIn`, `easeOut` and ` ![](circ-ease.svg) -- Red is the regular variantof `circ` (`easeIn`). +- Red is the regular variant of `circ` (`easeIn`). - Green -- `easeOut`. - Blue -- `easeInOut`. @@ -405,7 +405,7 @@ As we can see, the graph of the first half of the animation is the scaled down ` ## More interesting "draw" -Instead of moving the element we can do something else. All we need is to write the write the proper `draw`. +Instead of moving the element we can do something else. All we need is to write the proper `draw`. Here's the animated "bouncing" text typing: diff --git a/8-web-components/1-webcomponents-intro/article.md b/8-web-components/1-webcomponents-intro/article.md index 37a5d71565..3514389aeb 100644 --- a/8-web-components/1-webcomponents-intro/article.md +++ b/8-web-components/1-webcomponents-intro/article.md @@ -26,9 +26,15 @@ 그리고 우주정거장은 인간이 우주에서 날고, 살아있을 수 있게 해줍니다. +<<<<<<< HEAD 어떻게 이렇게 복잡한 장치가 만들어졌을까요? 개발을 안정적이고 확장 가능한 수준으로 만들기 위해 또는 그에 가깝게 만들기 위해 어떤 원칙을 빌려올 수 있을까요? +======= +How are such complex devices created? + +Which principles could we borrow to make our development same-level reliable and scalable? Or, at least, close to it? +>>>>>>> 4d01fc20d4d82358e61518a31efe80dec9bb2602 ## 컴포넌트 아키텍처 diff --git a/8-web-components/2-custom-elements/article.md b/8-web-components/2-custom-elements/article.md index 702ff90739..a84ed11923 100644 --- a/8-web-components/2-custom-elements/article.md +++ b/8-web-components/2-custom-elements/article.md @@ -115,7 +115,7 @@ customElements.define("time-formatted", TimeFormatted); // (2) > ``` -1. The class has only one method `connectedCallback()` -- the browser calls it when `` element is added to page (or when HTML parser detects it), and it uses the built-in [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat) data formatter, well-supported across the browsers, to show a nicely formatted time. +1. The class has only one method `connectedCallback()` -- the browser calls it when `` element is added to page (or when HTML parser detects it), and it uses the built-in [Intl.DateTimeFormat](mdn:/JavaScript/Reference/Global_Objects/DateTimeFormat) data formatter, well-supported across the browsers, to show a nicely formatted time. 2. We need to register our new element by `customElements.define(tag, class)`. 3. And then we can use it everywhere. @@ -149,7 +149,7 @@ The `connectedCallback` triggers when the element is added to the document. Not In the current implementation of ``, after the element is rendered, further attribute changes don't have any effect. That's strange for an HTML element. Usually, when we change an attribute, like `a.href`, we expect the change to be immediately visible. So let's fix this. -We can observe attributes by providing their list in `observedAttributes()` static getter. For such attributes, `attributeChangedCallback` is called when they are modified. It doesn't trigger for an attribute for performance reasons. +We can observe attributes by providing their list in `observedAttributes()` static getter. For such attributes, `attributeChangedCallback` is called when they are modified. It doesn't trigger for other, unlisted attributes (that's for performance reasons). Here's a new ``, that auto-updates when attributes change: @@ -320,7 +320,7 @@ For example, buttons are instances of `HTMLButtonElement`, let's build upon it. class HelloButton extends HTMLButtonElement { /* custom element methods */ } ``` -2. Provide an third argument to `customElements.define`, that specifies the tag: +2. Provide the third argument to `customElements.define`, that specifies the tag: ```js customElements.define('hello-button', HelloButton, *!*{extends: 'button'}*/!*); ``` @@ -365,7 +365,7 @@ Our new button extends the built-in one. So it keeps the same styles and standar ## References - HTML Living Standard: . -- Compatiblity: . +- Compatiblity: . ## Summary @@ -397,4 +397,4 @@ Custom elements can be of two types: /*