본문 바로가기
Frontend/JavaScript

[JavaScript] DOM 이벤트

by 모너아링 2023. 2. 16.

이벤트

DOM 요소의 기본 동작 중단

preventDefault 메서드 이용

<!DOCTYPE html>
<html>
<body>
    <a href="http://www.google.com">go</a>
    <input type="checkbox">
    <script>
        document.querySelector('a').onclick = e => {
            // a 요소의 기본 동작(지정된 링크로 이동)을 중단
            e.preventDefault();
        }
        document.querySelector('input[type=checkbox]').onclick = e => {
            // checkbox 요소의 기본 동작(체크 또는 해제)을 중단
            e.preventDefault();
        }
    </script>
</body>
</html>
  • preventDefault 메서드 사용 시
<body>
  <form>
    <input type="checkbox" placeholder="값을 입력하세요">
  </form> 

  <script>
    document.querySelector('input[type=checkbox]').onclick = e => {
      e.preventDefault();
      console.log("실패");
    }
  </script>
</body>

이벤트 전파 방지

stopPropagation 메서드

<!DOCTYPE html>
<html>
<body>
    <div class="container">
        <button class="btn1">Button 1</button>
        <button class="btn2">Button 2</button>
        <button class="btn3">Button 3</button>
    </div>
    <script>
        document.querySelector('.container').onclick = ({ target }) => {
            if(!target.matches('.container > button')) return;
            target.style.color = 'red';
        }
        document.querySelector('.btn2').onclick = e => {
            e.stopPropagation();
            e.target.style.color = 'blue';
        }
    </script>
</body>
</html>
  • 현재 이벤트가 캡처링/버블링 단계에서 더 이상 전파되지 않도록 방지

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

<!DOCTYPE html>
<html>
<body>
    <button onclick="handleClick()">Click me</button>
    <script>
        function handleClick(button){
            console.log(button); // 이벤트를 바인딩한 button 요소
            console.log(this); // window
        }
    </script>
</body>
</html>
  • 일반 함수로서 호출되는 함수 내부의 this 는 전역 객체 window를 가리킴
<!DOCTYPE html>
<html>
<body>
    <button class="btn1">0</button>
    <button class="btn2">0</button>
    <script>
        const $button1 = document.querySelector('.btn1');
        const $button2 = document.querySelector('.btn2');

    //이벤트 핸들러 프로퍼티 방식
    $button1.onclick = function(e) {
        //this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
        console.log(this); // $button1
        console.log(e.currentTarget); // $button1
        console.log(this === e.currentTarget); // true

        // $button1의 textContent를 1 증가시킨다.
        ++this.textContent;
    }

    //addEventListener 메서드 방식
    $button2.addEventListener('click', function(e) {
        //this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
        console.log(this); // $button2
        console.log(e.currentTarget); // $button2
        console.log(this === e.currentTarget); // true

        // $button2의 textContent를 1 증가시킨다.
        ++this.textContent;
    });
    </script>
</body>
</html>
  • 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식의 this 는 이벤트를 바인딩한 DOM 요소를 가리킴(이벤트 핸들러 프로퍼티 방식과 동일)
<!DOCTYPE html>
<html>
<body>
    <button class="btn1">0</button>
    <button class="btn2">0</button>
    <script>
        const $button1 = document.querySelector('.btn1');
        const $button2 = document.querySelector('.btn2');

    //이벤트 핸들러 프로퍼티 방식
    $button1.onclick = e => {
        //this는 상위 스코프의 this를 가리킨다.
        console.log(this); // window
        console.log(e.currentTarget); // $button1
        console.log(this === e.currentTarget); // false

        // window.textContent에 NaN을 할당
        ++this.textContent;
    };

    //addEventListener 메서드 방식
    $button2.addEventListener('click', e => {
        //this는 상위 스코프의 this를 가리킨다.
        console.log(this); // window
        console.log(e.currentTarget); // $button2
        console.log(this === e.currentTarget); // false

        // window.textContent에 NaN을 할당
        ++this.textContent;
    });
    </script>
</body>
</html>
  • 화살표 함수로 정의했을 경우 this는 상위 스코프의 this를 가리킴
  • 화살표 함수는 함수 자체의 this 바인딩을 갖지 않음
<!DOCTYPE html>
<html>
<body>
    <button class="btn">0</button>
    <script>
        class App {
            constructor() {
                this.$button = document.querySelector('.btn');
                this.count = 0;

                // increase 메서드를 이벤트 핸들러로 등록
                this.$button.onclick = this.increase;

                //increase 메서드 내부의 this가 인스턴스를 가리키도록 한다.
                //화살표 increase는 프로토타입 메서드가 아닌 인스턴스 메서드
                this.$button.onclick = this.increase.bind(this);
            }
            increase() {
                this.$button.textContent = ++this.count;
            }
            //화살표 함수
            increase = () => this.$button.textContent = ++this.count;
        }
    </script>
</body>
</html>
  • 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리킴
  • increase 메서드 내부의 thisthis.$button을 가리킴

bind()

  • 새로운 함수를 생성하여 첫 인자의 valuethis를 설정하고 이어지는 인자들은 바인드된 함수의 인수로 제공
const module = {
  x: 42,
  getX: function() {
    return this.x;
  }
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX()); // 42

이벤트 핸들러에 인수 전달

  • 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식은 함수 자체를 등록해야 인수 전달 가능
  • 따라서, 이벤트 핸들러 내부에서 함수를 호출하거나 이벤트 핸들러 반환하는 함수를 호출하면서 인수를 전달한다.
<!DOCTYPE html>
<html>
<body>
    <label>User name <input type='text'></label>
    <em class="message"></em>
    <script>\const MIN_USER_NAME_LENGTH = 5;
        const $input = document.querySelector('input[type=text]');
        const $msg= document.querySelector('.message');

    const checkUserNameLength = min => {
        $msg.textContent
            = $input.balue.length < min ? `이름은 ${min}자 이상 입력해 주세요` : '';
    }

    //이벤트 핸들러 내부에서 함수를 호출하면서 인수 전달    
    $input.onblur = () => {
        checkUserNameLength(**MIN_USER_NAME_LENGTH**);
    };

    //이벤트 핸들러를 반환하는 함수
    const checkUserNameLength = min => e => {
        $msg.textContent
            = $input.balue.length < min ? `이름은 ${min}자 이상 입력해 주세요` : '';
    };

    //이벤트 핸들러를 반환하는 함수를 호출하면서 인수 전달
    $input.onblur = checkUserNameLength(**MIN_USER_NAME_LENGTH**);
    </script>
</body>
</html>
  • 인수
    • MIN_USER_NAME_LENGTH

커스텀 이벤트 생성

커스텀 이벤트: 개발자가 임의의 이벤트 타입을 지정한 이벤트

Event의 생성자

  • 이벤트 클래스 계층의 제일 꼭대기엔 Event 클래스가 있음
//Event 객체 생성
let event = new Event(type[, options]);
  • type
    • 이벤트 타입을 나타내는 문자열
  • options
    • 두 개의 선택 프로퍼티가 있는 객체
    • bubbles: true/false ⇒ 버블링
    • cancelable: true/false ⇒ 기본 동작 실행 방지
//KeyboardEvent 생성자 함수로 keyup 이벤트 타입의 커스텀 이벤트 객체를 생성
const keyboardEvent = new KeyboardEvent('keyup');
console.log(keyboardEvent.type); // keyup

//CustomEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
const customEvent= new KeyboardEvent('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
  • 생성된 커스텀 이벤트 객체는 버블링되지 않으며 preventDefault 메서드로 취소할 수 없다. (false 로 기본 설정)

true로 설정하기 위해서는?

⇒ 이벤트 생성자 함수의 두 번째 인수로 bubbles 또는 cancelable 프로퍼티를 갖는 객체 전달

  • 반드시 명시적으로 설정해야함
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

isTrusted 프로퍼티

  • boolean 값 반환
    • 사용자 액션에 의해 생성된 이벤트는 true
    • EventTarget.dispatchEvent() 로 발송한 이벤트나 스크립트를 통해 생성되었다면 false
const customEvent = new InputEvent('foo');
console.log(customEvent.isTrusted); // false

커스텀 이벤트 디스패치

  • 생성된 커스텀 이벤트를 dispatchEvent 메서드로 디스패치(이벤트를 발생시키는 행위)를 해야 한다.
<!DOCTYPE html>
<html>
<body>
    <button class="btn">0</button>
    <script>
        const $button = document.querySelector('.btn');

        // 버튼 요소에 foo 커스텀 이벤트 핸들러를 등록
        $button.addEventListener('click', e => {
            console.log(e);
            alert(`${e} Clicked!`);
        });

        // 커스텀 이벤트 생성
        const customEvent = new MouseEvent('click');

        // 커스텀 이벤트 디스패치(동기 처리). click 이벤트 발생
        $button.dispatchEvent(customEvent);
    </script>
</body>
</html>

다양한 이벤트

  • UIEvent
  • FocusEvent
  • MouseEvent
  • WheelEvent
  • KeyboardEvent
  • 등등의 이벤트는 new Event를 통한 event 객체를 생성할 수 없고, 반드시 관련 내장 클래스를 사용해야 한다.
let event = new MouseEvent("click", {
  bubbles: true,
  cancelable: true,
  clientX: 100,
  clientY: 100
});

console.log(event.clientX); // 100

//Event 생성자를 사용한 표준 프로퍼티
let event = new Event("click", {
  bubbles: true, // Event 생성자에선
  cancelable: true, // bubbles와 cancelable 프로퍼티만 동작합니다.
  clientX: 100,
  clientY: 100
});

console.log(event.clientX); // undefined

CustomEvent 이용한 커스텀 이벤트

  • CustomEvent vs Event
    • CustomEvent ⇒ 두 번째 인수에 객체가 들어갈 수 있는데 detail 프로퍼티를 추가해 커스텀 이벤트 관련 정보를 명시하여 이벤트에 전달
    • detail 프로퍼티 사용 이유: 다른 이벤트 프로퍼티와의 충돌을 피하기 위함
    • <h1 id="elem">Hi! javascript</h1> <script> elem.addEventListener("hello", function(event) { console.log(event.detail.name); // javascript }); elem.dispatchEvent(new CustomEvent("hello", { detail: { name: "javascript" } })); </script>

'Frontend > JavaScript' 카테고리의 다른 글

[JavaScript] javascript 주요 개념 간단 정리  (0) 2023.02.22
[JavaScript] 제너레이터와 async/await  (0) 2023.02.16
[JavaScript] Set  (2) 2023.02.16
[JavaScript] 이터러블  (1) 2023.02.16
[JavaScript] 함수와 일급 객체  (0) 2022.11.08