기본동작
기본 동작이란 브라우저에서 제공하는 동작들을 의미하며, 대표적으로 스크롤을 이용하여 view port 이동이나 페이지 전환 등이 있습니다.
JavaScript에서는 event 객체의 preventDefault를 사용하여 이러한 기본 동작을 사용하지 못하도록 방지할 수 있습니다.
우선 클래스 명이 parent 인 요소에 마우스 휠 이벤트가 발생할 때 해당 콘솔이 출력되도록 해주었습니다.
기본 동작에 해당하는 스크롤을 사용하지 못하도록 앞서 언급한 event 객체의 preventDefault를 사용하여 스크롤을 하지 못하도록 해주었으며, 해당 요소에서 스크롤 시 콘솔 출력은 정상적으로 이루어지지만 스크롤이 되지 않는 것을 확인할 수 있습니다.
*preventDefault는 이벤트가 발생하는 것을 막는 것이 아닌 브라우저가 가지고 있는 기본 동작만 방지하기 때문에 등록한 이벤트의 두 번째 인수로 전달된 콜백함수의 실행문은 정상적으로 작동하게 됩니다.
위의 방법을 a 태그 클릭시 event 객체의 preventDefault를 사용하여 브라우저의 기본 동작인 페이지 전환을 방지할 수 있습니다.
언급한 스크롤, 페이지 전환 외에도 브라우저의 기본동작을 방지하고 싶은 경우 event 객체의 preventDefault를 사용하여 방지할 수 있습니다.
이벤트 버블링
이벤트 버블링이란 하위요소에서 상위요소로 이벤트가 전파되는 방식입니다.
부모 요소인 parent 안에 자식 요소인 child가 있으며, 각각의 요소에 parent와 child를 콘솔로 출력하는 이벤트가 등록되어 있습니다.
이때 부모 요소인 parent 클릭 시 parent! 콘솔만 출력이 되지만, 자식 요소인 child를 클릭할 경우 parent!, child! 두 개의 콘솔이 출력되는 것을 확인할 수 있습니다.
자식 요소인 child를 클릭 한클릭한 것은 자식 요소를 감싸고 있는 parent 요소를 클릭한 것과 같기 때문에 하위 요소인 child의 이벤트가 부모 요소인 parent로 이벤트가 전파되었기 때문입니다.
이벤트 버블링으로 인해 브라우저가 원하지 않은 동작을 할 수 있기 때문에 적절하게 이벤트 버블링 제어를 해주어야 합니다.
- 이벤트 버블링 정지
stopPropagation()
이벤트 추가 시 이벤트 객체의 stopPropagation 메서드를 호출해주면 자식 요소인 child 클릭 시 부모 요소인 parent로 전파되지 않고
child! 콘솔만 출력되게 됩니다.
- 이벤트 캡쳐
앞서 언급한 대로 이벤트는 기본적으로 상위 요소로 전파되는 이벤트 버블링이 발생합니다. 이는 하위 요소의 이벤트부터 순차적으로 상위 요소의 이벤트가 동작함을 의미합니다.
{ capture : true } : 해당 이벤트를 캡처하여 해당 이벤트의 handler 함수가 먼저 동작하도록 해주는 속성
이때 부모 요소인 parent에 세 번째 인자로 옵션을 설정하는 객체를 전달하고 해당 객체에 capture 속성을 true로 전달하게 되면 parent의 이벤트가 우선적으로 동작하고, 원래대로 하위 요소의 이벤트부터 상위 요소의 이벤트로 순차적으로 동작하게 됩니다.
*이벤트 캡처를 여러 요소에 설정한 경우 가장 상위의 요소부터 순차적으로 동작하게 됩니다.
만약 이벤트 캡처와 이벤트 버블링을 모두 사용할 경우 자식 요소인 child 클릭 시 parent! 만 콘솔에 출력되게 됩니다.
이는 이벤트 캡쳐링을 통해 하위 요소 클릭 시 capture의 속성이 true로 설정된 부모 요소의 이벤트가 우선적으로 동작하였고, 다음으로 자식 요소로 이벤트 동작이 넘어가야 되는데 stopPropagation 메서드로 인해 이벤트 전파가 차단되었기 때문입니다.
*addEventListner로 세 번째 인자로 capture 속성을 설정한 경우 해당 이벤트를 제거하는 removeEventListner 사용 시에도 동일하게 capture 설정을 해주어야 addEventListner로 추가한 이벤트가 동작하지 않고 정상적으로 제거가 됩니다.
이벤트 옵션
이벤트의 옵션으로 앞서 언급한 capture 외에도 아래와 같은 옵션 설정이 가능합니다.
- { once : true } - handler 함수를 한 번만 실행합니다.
- { passive : true } - 요소의 기본 동작과 handler 함수의 실행을 분리합니다.
*handler 함수 내부에 실행하는 로직이 다소 무거운 경우 passive속성 설정을 해주게 되면 기본 동작과 handler 함수가 분리되었기 때문에 끊김 없는 기본 동작을 확인할 수 있습니다. 때문에 사용자는 무거운 로직의 실행과 상관없이 부드러운 브라우저 기본 동작을 사용할 수 있기 때문에 보다 나은 사용자 경험을 느낄 수 있게 됩니다.
이벤트 위임
이벤트 위임이란 특정한 대상에 이벤트를 직접 부여하는 것이 아닌 대상의 조상 요소에 이벤트를 위임하여 이벤트를 한 번에 제어할 수 있는 하나의 패턴으로 비슷한 패턴의 여러 요소에서 이벤트를 핸들링해야 하는 경우 각각의 요소에 이벤트를 개별적으로 추가하는 것이 아닌 단일 조상 요소에서 한번에 이벤트를 제어할 수 있습니다.
child라는 클래스를 가지고 있는 모든 요소를 찾아 childEls라는 변수에 할당을 해주었고, forEach 메서드를 사용하여 해당 변수에 담긴 각각의 요소에 이벤트를 추가해주었기 때문에 결과적으로 총 4개의 이벤트를 추가한 것이 됩니다.
각각의 요소에 추가해준 이벤트는 event 객체의 textContent를 콘솔로 출력하는 공통된 실행문을 가지고 있기 때문에 handler 함수를 총 4번을 등록할 필요 없이 이벤트 위임 개념을 이용하여 1개의 이벤트만 추가할 수 있습니다.
closest 메서드를 사용하여 대상 요소(인수로 전달된 child)와 가장 가까운 자신을 포함한 조상요소를 찾아 childEl이라는 변수에 할당해 주었습니다.
부모 요소인 parentEl을 클릭하였을 때 childEl에 할당된 값이 null이 아니라면 조건문 내부의 실행문일 실행되기 때문에 forEach 메서드를 사용하여 이벤트를 추가한 것과 동일한 결과를 확인할 수 있게 됩니다.
이벤트 디스패치
dispatchEvent() : 이벤트를 강제로 발생 시킴
클래스 명이 child에 해당하는 두 번째 요소에 각각의 이벤트를 추가해주어 해당 이벤트 발생 시 콘솔이 출력되게 해 주었습니다.
클래스 명이 child에 해당하는 첫 번째 요소에는 클릭 이벤트 발생 시 child 두 번째 요소에 dispatchEvent와 인자로 Event 인스턴스를 전달하고 Event 생성자 함수의 인자로 이벤트 문자를 전달해 주었습니다.
Event 생성자의 인자로 이벤트 문자를 전달하게 되면 전달받은 문자는 이벤트로 생성되게 되고 dispatch를 사용하여 해당 요소에 생성된 이벤트를 강제로 발생하게 합니다.
때문에 child 첫 번째 요소를 클릭하게 되면 child 두 번째 요소의 이벤트가 강제로 발생되기 때문에 위와 같은 결과를 확인할 수 있습니다.
커스텀 이벤트
앞서 언급한 이벤트 디스패치와 Event 생성자를 사용하면 원하는 이벤트를 생성하여 동작하게 할 수 있습니다.
child 첫 번째 요소에는 JavaScript에 존재하지 않는 hello라는 이벤트를 부여하여 해당 이벤트가 발생할 경우 handler 함수가 실행되게 해주었고 child 두 번째 요소 클릭 시 Event 생성자 함수에서 hello 라는 이벤트가 생성해주어 해당 이벤트가 강제로 발생되게 해주었습니다.
*Event 생성자 함수의 인자로 전달되는 이벤트는 JavaScript에 존재하지 않은 이벤트라도 전달받은 문자로 이벤트 생성이 가능합니다.
child 두 번째 요소 클릭 시 위의 결과를 확인할 수 있으며 커스텀 이벤트의 경우 detail이 존재하지 않는 것을 확인할 수 있습니다.
기존의 사용하던 Event 생성자 함수 대신 CustomEvent 생성자 함수를 사용하고 두 번째 인자로 detail을 전달해주게 되면 정상적으로 이벤트의 detail을 확인할 수 있습니다.
*detail 속성으로 전달하는 특정 데이터가 이벤트 내부에서 필요하지 않다면 CustomEvent 생성자 함수 사용 대신 Event 함수를 사용해도 됩니다.
'JavaScript' 카테고리의 다른 글
Cookie, Storage (0) | 2022.12.22 |
---|---|
Console (0) | 2022.12.22 |
비동기 (0) | 2022.12.21 |
JavaScript 라이브러리 (0) | 2022.12.06 |
map() (0) | 2022.10.01 |