=>1/1 수정사항
return false가 브라우저의 기본동작 막는 것을 addEventListener에서도 적용이 된다고 생각했지만 실제 동작을 시도해보니
<html><head></head><body>
<a href="http://www.naver.com" id='onclick'>onclick</a>
<a href="http://www.naver.com" id='addevent'>addevent</a>
<script>
let click = document.getElementById('onclick');
click.onclick = function(){return false;}
let ex = document.getElementById('addevent');
ex.addEventListener('click',function(){
return false;
})
</script>
</body></html>
addEventListener는 브라우저의 기본동작을 막지는 못했다. (onclick은 가능)
<input value="focus가 동작합니다," onfocus="this.value=''">
<input onmousedown="return false" onfocus="this.value=''" value="클릭해 주세요.">
기본적으로 onmousedown으로 인해 버튼이 클릭이 안되나 그 위에 버튼으로 인해 첫 번째 input에 포커스한 상태에서 Tab 키를 누르면 포커스가 두 번째 input으로 넘어갑니다.
세번째 인자로 지원되는 속성은 다음과 같다.
- capture : 이벤트 전달 방식으로 capturing(true) 또는 bubbling(false) 을 선택함.
- once : true일 경우 이벤트를 한번만 받고 해제함.
- passive : true일 경우 이벤트에 의해 스크롤이 블럭되는 것을 방지함.
기본적으로 세번째인자가 존재하지않으면 capture는 false(bubbling) 상태이다.
Passive
메인 스레드에서는 자바스크립트를 실행한 후 layout, paint를 거쳐 Layout tree를 생성한 후 컴포지터 스레드에 넘기게 됩니다. 컴포지터 스레드는 넘겨받은 Layout tree에 따라 composite를 수행하여 실제로 화면에 그리게 된다.
만약 레이아웃이나 페인트에 영향을 주지 않는 경우 reflow, repaint 모두 발생하지 않고 Composite 작업만 수행하면 되는데, 이때엔 컴포지터 스레드가 메인 스레드의 처리를 기다리지 않고 바로 처리할 수 있기 때문에 성능이 매우 좋습니다.
{ passive:true } 의 진정한 의미는 이벤트를 받는 컴포지터 스레드에 해당 이벤트가 메인 스레드의 처리를 기다리지 않고 바로 Composite를 수행해도 된다는 힌트를 주는 것
이 속성이 설정되면 컴포지터 스레드는 원래의 동작대로 이벤트를 메인 스레드에 넘기기는 하지만, 처리를 기다리지 않고 바로 Composite를 수행합니다. 즉, 스크롤 이벤트를 받아 새 프레임을 바로 합성할 수 있다는 의미이고, 결과적으로 스크롤 성능이 향상되게 됩니다.
기본적 의미로 따지기
- Default => 기본
- Default 행동 => ex) a태그로 웹페이지 로드, submit 버튼으로 폼 데이터를 웹 서버 전송
- Propagation => 전파, 확산
즉, preventDefault는 event 행동 자체에 대한 것을 막는 것이고
stopPropagation은 이벤트 행동은 존재하나 특정 부분에서 전파, 확산을 막는 것이다.
이를 예로 들어보자
<html><head></head><body>
<button id="elem">버튼 레벨 컨텍스트 메뉴</button>
<script>
document.addEventListener('contextmenu',function(event) {
event.preventDefault();
alert("문서 컨텍스트 메뉴");
})
elem.addEventListener('contextmenu',function(event) {
event.preventDefault();
alert("버튼 컨텍스트 메뉴");
})
</script>
</body></html>
1) preventDefault()
현재 script에는 두가지의 Dom이 존재하는데 document 전체를 나타내는 Dom과 elem이라는 button을 나타내는 Dom이 있다.
이를 진행하면 버튼을 우클릭으로 눌렀을 경우 "버튼 컨테스트 메뉴" 출력 후 "문서 컨텍스트 메뉴"가 출력 된다. 이때 preventDefault의 역할은 기본적인 우클릭을 누를경우 출력되는 내용인 "뒤로,앞으로,새로고침,인쇄"와 같은 기본적 우클릭 내용이 나오는 것을 막는다.
2) stopPropagation()
코드의 일부를 수정해본다.
elem.addEventListener('contextmenu',function(event) {
event.preventDefault();
alert("버튼 컨텍스트 메뉴");
event.stopPropagation();
})
elem 부분에 stopPropagation을 추가해줬다.
이를 결과로 보면 기존에 우클릭을 누르면 "버튼 컨테스트 메뉴" 출력 후 "문서 컨텍스트 메뉴"가 출력 되는 결과에서 "버튼 컨테스트 메뉴"만 출력되는 결과로 바뀐다.
이는 캡쳐를 진행하며 elem까지 진행하여 alert를 실행한 후 stopPropagation을 통해 버블링을 통해 document의 이벤트 리스너가 alert를 실행해야 하는데 버블링을 막았기 때문이다.
<script>
elem.oncontextmenu = function(event) {
event.preventDefault();
event.stopPropagation();
alert("버튼 컨텍스트 메뉴");
};
document.oncontextmenu = function(event) {
event.preventDefault();
alert("문서 컨텍스트 메뉴");
};
</script>
위 내용은 같은 결과로
<script>
elem.oncontextmenu = function(event) {
event.preventDefault();
alert("버튼 컨텍스트 메뉴");
};
document.oncontextmenu = function(event) {
if (event.defaultPrevented) return;
event.preventDefault();
alert("문서 컨텍스트 메뉴");
};
</script>
이렇게 표현 할 수도 있다.
defaultPrevented는 기본적으로 false인데 preventDefault()가 실행된 후에는 true로 바뀌게 된다.
위 내용은 따라서 elem이 반응하여 실행되면 defaultPrevented는 true가 되고 버블링을 통해 document의 이벤트 핸들러는 if문이 true이므로 return을 진행한다.
후속이벤트를 보며 tab을 통해서도 되는 것은 신기했다.
stopPropagation과 preventDefault를 좀 더 예제를 보며 정리 할 수 있어서 좋았다.
이제 의도한 대로 버튼에서 마우스 오른쪽 버튼을 클릭하면 버튼 레벨의 컨텍스트 메뉴만 뜹니다. 하지만 이에 대한 대가가 너무 큽니다. 외부 코드를 사용해 더는 마우스 우클릭에 대한 정보를 얻을 수 없기 때문입니다. 통계 자료 수집을 위한 코드가 동작하지 못하죠. 현명하지 못한 해결책입니다.
이렇게 stopPropagation()에 대해 얘기를 하는데 좀 대가라는게 이해가 안된다. https://velog.io/@susu1991/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EC%B4%88
http://sculove.github.io/blog/2016/12/29/addEventListener-passive/
https://amati.io/eventlisteneroptions-passive-true/
브라우저의 기본동작 막기
저는 ko.javascript.info 에서 공부하면서 onclick 과 같은 on~ 으로 시작하는 메서드에 이벤트를 등록하는 방식에만 return false 가 유효하다고 공부를 하였는데, 형욱님의 의견은 어떠신가요 ?