40장 이벤트

40.1 이벤트 드리븐 프로그래밍

<!DOCTYPE html>
<html>
    <body>
        <button>Click me!</button>
            <script>
                const $button = document.querySelector'button');
                // 사용자가 버튼을 클릭하면 함수를 호출하도록 요청
                $button.onclick =()=>{ alert('button click'); };
            </script>
    </body>
</html>

40.2 이벤트 타입

40.3 이벤트 핸들러 등록

40.3.1 이벤트 핸들러 어트리뷰트 방식
<!DOCTYPE html>
<html>
    <body>
        <button onclick="sayHi('Lee')">Click me!</button>
            <script>
                function sayHi(name) {
                console.log('Hi! ${name}.');
                }
            </script>
    </body>
</html>
function onclick(event) {
    sayHi('Lee');
}
<button onclick="console.log('Hi!'); console.l.og('Lee');">Click me!</button>
40.3.2 이벤트 핸들러 프로퍼티 방식
<!DOCTYPE html>
<html>
    <body>
        <button>Click me!</button>
            <script>
                const $button = document.querySelector('button')
                // 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩
                $button.onclick function () {
                    console.log('button click');
                }
            </script>
    </body>
</html>
40.3.3 addEventListener 메서드 방식
<!D0CTYPE html>
<html>
    <body>
        <button>Click me!</button>
        <script>
            const $button = document.querySelector('button');
            // 이벤트 핸들러 프로퍼티 방식
            // $button.onclick = function () {
            // console.log('button click');
            // };
            // addEventListener 메서드 방식
            $button.addEventListener('click', function () {
            console.log('button click');
            }) 
        </script>
    </body>
</html>

40.4 이벤트 핸들러 제거

<!DOCTYPE html>
<html>
    <body>
        <button>Click me!</button>
        <script>
            const $button = document.querySelector('button');
            const handleClick = () => console.log('button click');
            // 이벤트 핸들러 등록
            $button.addEventListener('click', handleClick);
            // 이벤트 핸들러 제거
            // addEventListener 머/서드에 전달한 인수와 removeEventListener 메서드에
            // 전달한 안수가 일치하지 않으면 이벤트 핸들러가 제거되지 않는다.
            $button.removeEventListener('click', handleClick, true); // 실패
            $button.removeEventListener('click', handleClick); // 성공
        </script>
    </body>
</html>
// 이벤트 핸들러 등록
$button.addEventListener('click', () => console.log('button click'));
// 등록한 이벤트 핸들러를 참조할 수 없으므로 저/거할 수 없다.
// 기명 함수를 이벤트 핸들러로 등록'
$button.addEventListener('click', function foo() {
    console.log('button click');
    // 이벤트 핸들러를 제거한다. 따라서 이벤트 핸들러는 단 한 번만 호출된다.
    $button.removeEventListener('click', foo);
})
const $button = document.querySelector('button');
$button.onclick = null;

40.5 이벤트 객체

<!DOCTYPE html>
<html>
    <body>
        <p>클릭하세요. 클릭한 곳의 좌표가 표시됩니다.</p>
        <em class= "message"></em>
        <script>
            const $msg = document.querySelector('.message');
            // 클릭 이벤트에 의해 생성된 이벤트 객체 이벤트 핸들러의 첫 번째 인수로 전달된다.
            function showCoords(e) {
                $msg.textContent = 'clientX : ${e.clientX}, clientY: ${e.clientY}';
            }
            document.onclick = showCoords;
        </script>
    </body>
</html>
40.5.1 이벤트 객체의 상속 구조
// Event 생성자 함수를 호출하여 foo 이벤트 타입의 Event 객체를 생성한다.
let e = new Event('foo');
console.log(e);
// Event {isTrusted: false, type: "foo", target: null, ... }
console.log(e.type); // "foo"
console.log(e instanceof Event); // true
console.log(e instanceof Object); // true
// FocusEvent 생성자 함수를 호출하여 focus 이벤트 타입의 FocusEvent 객체를 생성한다.
e = new FocusEvent('focus');
console.log(e);
// FocusEvent {isTrusted: false, relatedTarget: null, view: null, ... }
40.5.2 이벤트 객체의 공통 프로퍼티
40.5.3 마우스 정보 취득
40.5.4 키보드 정보 취득

40.6 이벤트 전파

<!DOCTYPE html>
<html>
<body>
    <ul id=”fruits”>
        <li id=”apple”>Apple</li>
        <li id=”banana">Banana</li>
        <li id=“orange”>Orange</li>
    </ul>
</body>
</html>
<!DOCTYPE html>
<html>
    <body>
        <ul id= "fruit">
            <li id= "apple">Apple</li>
            <li id= "banana">Banana</li>
            <li id= "orange">Orange</li>
        </ul>
        <script>
            const $fruits = document.getElementById('fruit');
            // itfruits 요소의 하위 요소인 li 요소를 클릭한 경우
            $fruits.addEventListener('click', e => {
                console.log( ${e eventPhase} ); // 3: 버블링 단계
                console.log('이벤트 타깃: ${e. target}'); // [object HTMLLIElement]
                console.log('커런트 타깃: ${e.currentTarget}'); // [object HTMLUListElement]
            }) 
        </script>
    </body>
</html>

40.7 이벤트 위임

<!DOCTYPE html>
<html>
<head>
    <style>
        #fruits {
            display: flex;
            list-style-type: none;
            padding: 0;
        }
        #fruits li {
            width: 100px;
            cursor: pointer;
        }
        #fruits .active {
            color: red;
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <nav>
        <ul id= "fruit">
            <li id= "apple" class="active">Apple</li>
            <li id= "banana">Banana</li>
            <li id= "orange">Orange</li>
        </ul>
    </nav>
<div>선택된 내비게이션 아이템: <em class= >apple</em></div>
    <script>
        const $fruits = document.getElementById('fruits');
        const $msg = document.querySelector('.msg');
        // 사용자 클릭에 의해 선택된 내비게이션 아이템(li 요소)에 active 클래스를 추가하고
        // 그 외의 모든 내비게이션 아이템의 active 클래스를 제거한다.
        function activate({ target }) {
            [ ... $fruits.children].forEach($fruit => {
            $fruit.classList.toggle('active', $fruit === target);
            $msg.textcontent = target.id;
            })
        }
        // 모든 내비게이션 아이템(li 요소)에 이벤트 핸들러를 등록한다.
        document.getElementById('apple').onclick = activate;
        document.getElementById('banana').onclick = activate;
        document.getElementById('orange').onclick = activate;
    </script>
</body>
</html>
// 사용자 클릭에 의해 선택된 내비게이션 아이템(li 요소)에 active 클래스를 추가하고
// 그 외의 모든 내비게이션 아이템의 active 클래스를 제거한다.
function activate({ target }) {
    // 이벤트를 발생시킨 요소(target)가 ul#fruits의 자식 요소가 아니라면 무시한다.
    if (!target.matches('#fruits > li')) return;
    [ ... $fruits.children].forEach($fruit => {
        $fruit.classList.toggle('active', $fruit === target);
        $msg.textContent = target.id;
    })
}
// 이벤트 위임: 상위 요소(ul#fruits)는 하위 요소의 이벤트를 캐치할 수 있다.
$fruits.onclick = activate;
function activate({ target }) {
    // 이벤트를 발생시킨 요소(target)이 ul#fruits의 자식 요소가 아니라면 무시한다.
    if (!target.matches('#fruits > li')) return;
}

40.8 DOM 요소의 기본 동작 조작

40.8.1 DOM 요소의 기본 동작 중단

40.8.2 이벤트 전파 방지

40.9 이벤트 핸들러 내부의 this

40.9.1 이벤트 핸들러 어트리뷰트 방식

<!DOCTYPE html>
<html>
    <body>
        <button onclick="handleClick()">Click me</button>
        <script>
            function handleClick() {
                console.log(this); // window
            }
        </script>
    </body>
</html>
<!DOCTYPE html>
<html>
    <body>
        <button onclick="handleClick(this)">Click me</button>
        <script>
            function handleClick(button) {
                console.log(button); // 이벤트를 바인딩한 button 요소
                console.log(this); // window
            }
        </script>
    </body>
</html>

40.9.2 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식

<!D0CTYPE html>
<html>
<body>
    <button class= >0</button>
    <script>
        class App {
            constructor() {
                this.$button = document.querySelector( );
                this.count = 0;
                II increase 메서드를 이벤트 핸들러로 등록
                this.$button.onclick = this.increase
            }
            increase() {
                // 이벤트 핸들러 increase 내부의 this는 DOM 요소(this.$button)를 가리킨다.
                I/ 따라서 this.$button은 this.$button.$button과 같다.
                this.$button.textcontent = ++this.count;
                // — TypeError: Cannot set property 'textcontent' of undefined
            }
        }
        new App();
    </script>
</body>
</html>
class App {
    constructor() {
        this.$button = document.querySelector('.btn' );
        this.count = 0;
        // increase 메서드를 이벤트 핸들러로 등록
        // this.$button.onclick = this.increase;
        // increase 메서드 내부의 this가 인스턴스를 가리키도록 한다.
        this.$button.onclick = this.increase.bind(this)
    }
    increase() {
        this.$button.textcontent = ++this.count;
    }
}

40.10 이벤트 핸들러에 인수 전달

<!DOCTYPE html>
<html>
<body>
    <label>User name <input type='text'></label>
    <em class="message"></em>
    <script>
        const MIN_USER_NAIV|E_LENGTH = 5; // 이름 최소 길이
        const $input = document.querySelector( 'input[type=text]');
        const $msg = document.querySelector('.message');
        const checkllserNameLength = min => {
            $msg.textcontent
            = $input.value.length < min ? '이름은 ${min}자 이상 입력해 주세요' : '';
        }
        // 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달한다.
        $input.onblur =()=>{
            checkUserNameLength(|V|IN_USER_NAIVIE_LENGTH);
        }
    </script>
</body>
</html>

40.11 커스텀 이벤트

40.11.1 커스텀 이벤트 생성

// KeyboardEvent 생성자 함수로 keyup 이벤트 타입의 커스텀 이벤트 객체를 생성
const keyboardEvent = new KeyboardEvent('keyup');
console.log(keyboardEvent.type); // keyup
// CustomEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
const CustomEvent = new CustomEvent('foo');
console.log(CustomEvent.type); // foo
// MouseEvent 생성자 함수로 click 이벤트 타입의 커스텀 이벤트 객체를 생성
const CustomEvent = new MouseEvent('click');
console.log(CustomEvent.type); // click
console.log(CustomEvent.bubbles); // false
console.log(CustomEvent.cancelable); // false
// MouseEvent 생성자 함수로 click 이벤트 타입의 커스텀 이벤트 객체를 생성
const CustomEvent = new MouseEvent('click', {
    bubbles: true,
    cancelable: true
})
console.log(CustomEvent.bubbles); // true
console.log(CustomEvent.cancelable); // true
// MouseEvent 생성자 함수로 click 이벤트 타입의 커스텀 이벤트 객체를 생성
const mouseEvent = new MouseEvent('click', {
    bubbles: true,
    cancelable: true,
    clientx 50,
    clientY 100
})
console.log(mouseEvent.clientx); // 50
console.log(mouseEvent.clientY); // 100
// KeyboardEvent 생성자 함수로 keyup 이벤트 타입의 커스텀 이벤트 객체를 생성
const keyboardEvent new KeyboardEvent('keyup', { key: Enter })
console.log(keyboardEvent.key); // Enter

40.11.2 커스텀 이벤트 디스패치

// CustomEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
const customEvent = new CustomEvent('foo');
console.log(customEvent.type); // foo
<!DOCTYPE html>
<html>
<body>
    <button class= "btn">Click me</button>
    <script>
        const $button = document.querySelector('.btn');
        // 버튼 요소에 foo 커스텀 이벤트 핸들러를 등록
        // 커스텀 이벤트를 디스패치하기 이전에 이벤트 핸들러를 등록해야 한다.
        $button.addEventListener('foo', e => {
        // e.detail에는 CustomEvent 함수의 두 번째 인수로 전달한 정보가 담겨 있다.
        alert(e.detail.message);
        })
        // CustomEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
        const CustomEvent = new CustomEvent( 'foo', {
        detail: { message: 'Hello' } // 이벤트와 함께 전달하고 싶은 정보
        })
        // 커스텀 이벤트 디스패치
        $button.dispatchEvent(CustomEvent);
    </script>
</body>
</html>

끝!