Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 1 addition & 36 deletions 2-ui/2-events/01-introduction-browser-events/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,7 @@
**CSS 이벤트:**
- `transitionend` -- CSS 애니메이션이 종료되었을 때 발생합니다.

<<<<<<< HEAD
이 외에도 다양한 이벤트가 있는데, 몇몇 이벤트는 다음 챕터에서 자세히 다룰 예정입니다.
=======
There are many other events. We'll get into more details of particular events in upcoming chapters.
>>>>>>> upstream/master

## 이벤트 핸들러

Expand Down Expand Up @@ -164,11 +160,7 @@ button.onclick = sayThanks;
button.onclick = sayThanks();
```

<<<<<<< HEAD
`sayThanks()` 같이 괄호를 덧붙이는 것은 함수를 호출하겠다는 것을 의미합니다. 위 예시의 마지막 줄처럼 sayThanks()를 프로퍼티에 할당하면 함수 호출의 *결괏(result)값*이 할당되죠. 함수 `sayThanks`가 아무것도 반환하지 않는다면 `onclick` 프로퍼티엔 `undefined`이 할당되므로 이벤트가 원하는 대로 동작하지 않습니다.
=======
If we add parentheses, then `sayThanks()` becomes a function call. So the last line actually takes the *result* of the function execution, that is `undefined` (as the function returns nothing), and assigns it to `onclick`. That doesn't work.
>>>>>>> upstream/master

그런데, HTML 속성값에는 괄호가 있어야 합니다.

Expand Down Expand Up @@ -203,11 +195,7 @@ document.body.setAttribute('onclick', function() { alert(1) });

## addEventListener

<<<<<<< HEAD
HTML 속성과 DOM 프로퍼티를 이용한 이벤트 핸들러 할당 방식엔 근본적인 문제가 있습니다. 하나의 이벤트에 복수의 핸들러를 할당할 수 없다는 문제이죠.
=======
The fundamental problem of the aforementioned ways to assign handlers is that we *can't assign multiple handlers to one event*.
>>>>>>> upstream/master

버튼을 클릭하면 버튼을 강조하면서 메시지를 보여주고 싶다고 해 봅시다.

Expand All @@ -219,11 +207,7 @@ input.onclick = function() { alert(1); }
input.onclick = function() { alert(2); } // 이전 핸들러를 덮어씀
```

<<<<<<< HEAD
웹 표준에 관여하는 개발자들은 오래전부터 이 문제를 인지하고, `addEventListener` 와 `removeEventListener` 라는 특별한 메서드를 이용해 핸들러를 관리하자는 대안을 제시했습니다. 핸들러를 여러 개 할당할 수 있도록 말이죠.
=======
Developers of web standards understood that long ago and suggested an alternative way of managing handlers using the special methods `addEventListener` and `removeEventListener` which aren't bound by such constraint.
>>>>>>> upstream/master

문법은 다음과 같습니다.

Expand Down Expand Up @@ -277,11 +261,7 @@ input.removeEventListener("click", handler);
변수에 핸들러 함수를 저장해 놓지 않으면 핸들러를 지울 수 없다는 것을 항상 기억해 놓으셔야 합니다. 이렇게 하지 않으면 `addEventListener`로 할당한 핸들러를 '불러올' 수 없습니다.
````

<<<<<<< HEAD
`addEventListener`를 여러 번 호출하면 아래와 같이 핸들러를 여러 개 붙일 수 있습니다.
=======
Multiple calls to `addEventListener` allow it to add multiple handlers, like this:
>>>>>>> upstream/master

```html run no-beautify
<input id="elem" type="button" value="클릭해 주세요."/>
Expand All @@ -308,11 +288,7 @@ Multiple calls to `addEventListener` allow it to add multiple handlers, like thi
````warn header="어떤 이벤트는 `addEventListener`를 써야만 동작합니다."
DOM 프로퍼티에 할당할 수 없는 이벤트가 몇몇 있습니다. 이런 이벤트는 무조건 `addEventListener`를 써야 합니다.

<<<<<<< HEAD
문서를 읽고 DOM 트리 생성이 완료되었을 때 트리거되는 이벤트인 `DOMContentLoaded`가 대표적인 예입니다.
=======
For instance, the `DOMContentLoaded` event, that triggers when the document is loaded and the DOM has been built.
>>>>>>> upstream/master

```js
// 이 얼럿창은 절대 뜨지 않습니다.
Expand Down Expand Up @@ -358,17 +334,10 @@ document.addEventListener("DOMContentLoaded", function() {
`event.currentTarget`
: 이벤트를 처리하는 요소. 화살표 함수를 사용해 핸들러를 만들거나 다른 곳에 바인딩하지 않은 경우엔 `this`가 가리키는 값과 같음, 화살표 함수를 사용했거나 함수를 다른 곳에 바인딩한 경우엔 `event.currentTarget`를 사용해 이벤트가 처리되는 요소 정보를 얻을 수 있음

<<<<<<< HEAD
`event.clientX / event.clientY`
`event.clientX` / `event.clientY`
: 포인터 관련 이벤트에서, 커서의 상대 좌표(모니터 기준 좌표가 아닌, 브라우저 화면 기준 좌표 - 옮긴이)

이 외에도 다양한 프로퍼티가 있습니다. 이벤트 타입에 따라 이벤트 객체에서 제공하는 프로퍼티는 다릅니다. 추후 다양한 종류의 이벤트를 학습하면서 이벤트별 프로퍼티에 대해서도 상세히 알아보겠습니다.
=======
`event.clientX` / `event.clientY`
: Window-relative coordinates of the cursor, for pointer events.

There are more properties. Many of them depend on the event type: keyboard events have one set of properties, pointer events - another one, we'll study them later when as we move on to the details of different events.
>>>>>>> upstream/master

````smart header="이벤트 객체는 HTML 핸들러 안에서도 접근할 수 있습니다."
HTML에서 핸들러를 할당한 경우에도 아래와 같이 `event` 객체를 사용할 수 있습니다.
Expand Down Expand Up @@ -404,11 +373,7 @@ HTML에서 핸들러를 할당한 경우에도 아래와 같이 `event` 객체

보시다시피 `addEventListener`가 인수로 객체 형태의 핸들러를 받으면 이벤트 발생 시 `obj.handleEvent(event)`가 호출됩니다.

<<<<<<< HEAD
클래스를 사용할 수도 있습니다.
=======
We could also use objects of a custom class, like this:
>>>>>>> upstream/master


```html run
Expand Down
61 changes: 8 additions & 53 deletions 2-ui/2-events/02-bubbling-and-capturing/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,46 +120,27 @@

이벤트엔 버블링 이외에도 '캡처링(capturing)' 이라는 흐름이 존재합니다. 실제 코드에서 자주 쓰이진 않지만, 종종 유용한 경우가 있으므로 알아봅시다.

<<<<<<< HEAD
표준 [DOM 이벤트](http://www.w3.org/TR/DOM-Level-3-Events/)에서 정의한 이벤트 흐름엔 3가지 단계가 있습니다.
=======
The standard [DOM Events](https://www.w3.org/TR/DOM-Level-3-Events/) describes 3 phases of event propagation:
>>>>>>> upstream/master
표준 [DOM 이벤트](https://www.w3.org/TR/DOM-Level-3-Events/)에서 정의한 이벤트 흐름엔 3가지 단계가 있습니다.

1. 캡처링 단계 -- 이벤트가 하위 요소로 전파되는 단계
2. 타깃 단계 -- 이벤트가 실제 타깃 요소에 전달되는 단계
3. 버블링 단계 -- 이벤트가 상위 요소로 전파되는 단계

<<<<<<< HEAD
테이블 안의 `<td>`를 클릭하면 어떻게 이벤트가 흐르는지 아래 그림을 보고 이해해 봅시다.
=======
Here's the picture, taken from the specification, of the capturing `(1)`, target `(2)` and bubbling `(3)` phases for a click event on a `<td>` inside a table:
>>>>>>> upstream/master

![](eventflow.svg)

`<td>`를 클릭하면 이벤트가 최상위 조상에서 시작해 아래로 전파되고(캡처링 단계), 이벤트가 타깃 요소에 도착해 실행된 후(타깃 단계), 다시 위로 전파됩니다(버블링 단계). 이런 과정을 통해 요소에 할당된 이벤트 핸들러가 호출됩니다.

<<<<<<< HEAD
**캡처링 단계를 이용해야 하는 경우는 흔치 않기 때문에, 이전까진 주로 버블링만 설명했습니다. 캡처링에 관한 코드를 발견하는 일은 거의 없을 겁니다.**
캡처링 단계를 이용해야 하는 경우는 흔치 않기 때문에, 이전까진 주로 버블링만 설명했습니다. 캡처링에 관한 코드를 발견하는 일은 거의 없을 겁니다.

`on<event>` 프로퍼티나 HTML 속성, `addEventListener(event, handler)`를 이용해 할당된 핸들러는 캡처링에 대해 전혀 알 수 없습니다. 이 핸들러들은 두 번째 혹은 세 번째 단계의 이벤트 흐름(타깃 단계와 버블링 단계)에서만 동작합니다.
=======
Until now, we only talked about bubbling, because the capturing phase is rarely used.

In fact, the capturing phase was invisible for us, because handlers added using `on<event>`-property or using HTML attributes or using two-argument `addEventListener(event, handler)` don't know anything about capturing, they only run on the 2nd and 3rd phases.
>>>>>>> upstream/master

캡처링 단계에서 이벤트를 잡아내려면 `addEventListener`의 `capture` 옵션을 `true`로 설정해야 합니다.

```js
elem.addEventListener(..., {capture: true})
<<<<<<< HEAD
// 아니면, 아래 같이 {capture: true} 대신, true를 써줘도 됩니다.
=======

// or, just "true" is an alias to {capture: true}
>>>>>>> upstream/master
elem.addEventListener(..., true)
```

Expand Down Expand Up @@ -189,7 +170,7 @@ elem.addEventListener(..., true)

<script>
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(`캡쳐링: ${elem.tagName}`), true);
elem.addEventListener("click", e => alert(`캡처링: ${elem.tagName}`), true);
elem.addEventListener("click", e => alert(`버블링: ${elem.tagName}`));
}
</script>
Expand All @@ -199,57 +180,39 @@ elem.addEventListener(..., true)

`<p>`를 클릭하면 다음과 같은 순서로 이벤트가 전달됩니다.

<<<<<<< HEAD
1. `HTML` -> `BODY` -> `FORM` -> `DIV` (캡처링 단계, 첫 번째 리스너)
2. `P` (타깃 단계, 캡쳐링과 버블링 둘 다에 리스너를 설정했기 때문에 두 번 호출됩니다.)
2. `P` (타깃 단계, 캡처링과 버블링 둘 다에 리스너를 설정했기 때문에 두 번 호출됩니다.)
3. `DIV` -> `FORM` -> `BODY` -> `HTML` (버블링 단계, 두 번째 리스너)
=======
1. `HTML` -> `BODY` -> `FORM` -> `DIV -> P` (capturing phase, the first listener):
2. `P` -> `DIV` -> `FORM` -> `BODY` -> `HTML` (bubbling phase, the second listener).

Please note, the `P` shows up twice, because we've set two listeners: capturing and bubbling. The target triggers at the end of the first and at the beginning of the second phase.
>>>>>>> upstream/master

`event.eventPhase` 프로퍼티를 이용하면 현재 발생 중인 이벤트 흐름의 단계를 알 수 있습니다. 반환되는 정숫값에 따라 이벤트 흐름의 현재 실행 단계를 구분할 수 있죠. 하지만 핸들러를 통해 흐름 단계를 알 수 있기 때문에 이 프로퍼티는 자주 사용되지 않습니다.

```smart header="핸들러를 제거할 때 `removeEventListener`가 같은 단계에 있어야 합니다."
`addEventListener(..., true)`로 핸들러를 할당해 줬다면, 핸들러를 지울 때, `removeEventListener(..., true)`를 사용해 지워야 합니다. 같은 단계에 있어야 핸들러가 지워집니다.
```

<<<<<<< HEAD
````smart header="같은 요소와 같은 단계에 설정한 리스너는 설정한 순서대로 동작합니다."
특정 요소에 `addEventListener`를 사용해 한 단계에 이벤트 핸들러를 여러개 설정했다면 이 핸들러들은 설정한 순서대로 동작합니다.
=======
````smart header="Listeners on the same element and same phase run in their set order"
If we have multiple event handlers on the same phase, assigned to the same element with `addEventListener`, they run in the same order as they are created:
>>>>>>> upstream/master

```js
elem.addEventListener("click", e => alert(1)); // 첫 번째로 트리거됩니다.
elem.addEventListener("click", e => alert(2));
```
````

```smart header="The `event.stopPropagation()` during the capturing also prevents the bubbling"
The `event.stopPropagation()` method and its sibling `event.stopImmediatePropagation()` can also be called on the capturing phase. Then not only the futher capturing is stopped, but the bubbling as well.
```smart header="캡처링 단계에서 `event.stopPropagation()`을 호출하면 버블링도 막힙니다."
`event.stopPropagation()` 메서드와 형제 메서드인 `event.stopImmediatePropagation()`은 캡처링 단계에서도 호출할 수 있습니다. 이 경우 캡처링이 더 이상 진행되지 않을 뿐만 아니라 버블링도 중단됩니다.

In other words, normally the event goes first down ("capturing") and then up ("bubbling"). But if `event.stopPropagation()` is called during the capturing phase, then the event travel stops, no bubbling will occur.
다시 말해, 이벤트는 보통 먼저 아래로 내려가다가("캡처링") 다시 위로 올라갑니다("버블링"). 하지만 캡처링 단계에서 `event.stopPropagation()`이 호출되면 이벤트 전파가 멈추고, 버블링은 발생하지 않습니다.
```


## 요약

이벤트가 발생하면 이벤트가 발생한 가장 안쪽 요소가 '타깃 요소(`event.target`)'가 됩니다.

<<<<<<< HEAD
- 이벤트는 document에서 시작해 DOM 트리를 따라 `event.target`까지 내려갑니다. 이벤트는 트리를 따라 내려가면서 `addEventListener(..., true)`로 할당한 핸들러를 동작시킵니다. `addEventListener(..., true)`의 `true`는 `{capture: true}`의 축약형입니다.
- 이후 타깃 요소에 설정된 핸들러가 호출됩니다.
- 이후엔 이벤트가 `event.target`부터 시작해서 다시 최상위 노드까지 전달되면서 각 요소에 `on<event>`로 할당한 핸들러와 `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<event>`, HTML attributes and `addEventListener` without the 3rd argument or with the 3rd argument `false/{capture:false}`.
>>>>>>> upstream/master

각 핸들러는 아래와 같은 `event` 객체의 프로퍼티에 접근할 수 있습니다.

Expand All @@ -259,18 +222,10 @@ In other words, normally the event goes first down ("capturing") and then up ("b

핸들러에서 `event.stopPropagation()`을 사용해 이벤트 버블링을 멈출 수 있습니다. 다만, 이 방법은 추천하지 않습니다. 지금은 상위 요소에서 이벤트가 어떻게 쓰일지 확실치 않더라도, 추후에 버블링이 필요한 경우가 생기기 때문입니다.

<<<<<<< HEAD
캡처링 단계는 거의 쓰이지 않고, 주로 버블링 단계의 이벤트만 다뤄집니다. 이렇게 된 데는 논리적 배경이 있습니다.
=======
The capturing phase is used very rarely, usually we handle events on bubbling. And there's a logical explanation for that.
>>>>>>> upstream/master

현실에서 사고가 발생하면 지역 경찰이 먼저 사고를 조사합니다. 그 지역에 대해 가장 잘 아는 기관은 지역 경찰이기 때문입니다. 추가 조사가 필요하다면 그 이후에 상위 기관이 사건을 넘겨받습니다.

<<<<<<< HEAD
이벤트 핸들러도 이와 같은 논리로 만들어졌습니다. 특정 요소에 할당된 핸들러는 그 요소에 대한 자세한 사항과 무슨 일을 해야 할지 가장 잘 알고 있습니다. `<td>`에 할당된 핸들러는 `<td>`에 대한 모든 것을 알고 있기 때문에 `<td>`를 다루는데 가장 적합합니다. 따라서 `<td>`를 다룰 기회를 이 요소에 할당된 핸들러에게 가장 먼저 주는 것입니다.
=======
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 `<td>` may be suited for that exactly `<td>`, 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.
>>>>>>> upstream/master
이벤트 핸들러도 이와 같은 논리로 만들어졌습니다. 특정 요소에 할당된 핸들러는 그 요소에 대한 자세한 사항과 무슨 일을 해야 할지 가장 잘 알고 있습니다. `<td>`에 할당된 핸들러는 `<td>`에 대한 모든 것을 알고 있기 때문에 `<td>`를 다루는데 가장 적합합니다. 따라서 `<td>`를 다룰 기회를 이 요소에 할당된 핸들러에게 가장 먼저 주는 것입니다. 바로 위 부모 요소도 자식 요소만큼은 아니지만 관련 맥락을 알고 있으며, 이런 식으로 최상위 요소까지 거슬러 올라가 마지막에는 가장 일반적인 개념을 다루는 핸들러가 실행됩니다.

버블링과 캡처링은 '이벤트 위임(event delegation)'의 토대가 됩니다. 이벤트 위임은 강력한 이벤트 핸들링 패턴입니다. 다음 챕터에서 이를 다루도록 하겠습니다.
8 changes: 0 additions & 8 deletions 2-ui/2-events/03-event-delegation/article.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@

# 이벤트 위임

<<<<<<< HEAD
캡처링과 버블링을 활용하면 강력한 이벤트 핸들링 패턴인 *이벤트 위임(event delegation)* 을 구현할 수 있습니다.
=======
Capturing and bubbling allow us to implement one of the most powerful event handling patterns called *event delegation*.
>>>>>>> upstream/master

이벤트 위임은 비슷한 방식으로 여러 요소를 다뤄야 할 때 사용됩니다. 이벤트 위임을 사용하면 요소마다 핸들러를 할당하지 않고, 요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있습니다.

<<<<<<< HEAD
공통 조상에 할당한 핸들러에서 `event.target`을 이용하면 실제 어디서 이벤트가 발생했는지 알 수 있습니다. 이를 이용해 이벤트를 핸들링합니다.
=======
In the handler we get `event.target` to see where the event actually happened and handle it.
>>>>>>> upstream/master

예제를 살펴봅시다. 다음은 고대 중국 철학에서 유래한 [팔괘도(Ba-Gua diagram)](http://en.wikipedia.org/wiki/Ba_gua) 입니다.

Expand Down
8 changes: 0 additions & 8 deletions 2-ui/2-events/04-default-browser-action/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@
- 첫 번째 방법은 `event` 객체를 사용하는 것입니다. 이때 `event` 객체에 구현된 `event.preventDefault()` 메서드를 사용합니다.
- 핸들러가 `addEventListener`가 아닌 `on<event>`를 사용해 할당되었다면 `false`를 반환하게 해 기본 동작을 막을 수도 있습니다.

<<<<<<< HEAD
아래 HTML에선 링크를 클릭해도 해당 URL로 이동하지 않습니다.
=======
In this HTML, a click on a link doesn't lead to navigation; the browser doesn't do anything:
>>>>>>> upstream/master

```html autorun height=60 no-beautify
<a href="/" onclick="return false">이곳</a>
Expand Down Expand Up @@ -100,11 +96,7 @@ menu.onclick = function(event) {

`addEventListener`의 `passive: true` 옵션은 브라우저에게 `preventDefault()`를 호출하지 않겠다고 알리는 역할을 합니다.

<<<<<<< HEAD
이 옵션은 왜 필요한 걸까요?
=======
Why might that be needed?
>>>>>>> upstream/master

모바일 기기에는 사용자가 스크린에 손가락을 대고 움직일 때 발생하는 `touchmove`와 같은 이벤트가 있습니다. 이런 이벤트는 기본적으로 스크롤링(scrolling)을 발생시킵니다. 그런데 핸들러의 `preventDefault()`를 사용하면 스크롤링을 막을 수 있습니다.

Expand Down
Loading