자바스크립트 마우스 이벤트

in #javascript7 years ago (edited)

자바스크립트에서는 DOM에 대하여 여러가지 마우스 이벤트를 지원해줍니다.
그 중에서 이벤트 이름만 보면 동일한 동작을 하는 거처럼 보이는 이벤트도 있고,
특정 이벤트를 사용할 때는 조심해야 되는 이벤트도 있습니다.

그래서 이번에는 마우스 이벤트에 대해서 조금 더 관심을 가지고 보려고 합니다.

이벤트 명발생시기
mouseoverDOM과 하위DOM 에 마우스를 올리면 발생
mouseoutDOM과 하위DOM에서 다른 DOM으로 마우스가 이동될때 발생합니다.
mouseenter이벤트가 걸려있는 DOM에서만 mouseover가 발생합니다.(하위DOM은 이벤트가 발생하지 않음)
mouseleave이벤트가 걸려있는 DOM에서만 mouseout이벤트가 발생합니다.(하위DOM은 이벤트가 발생하지 않음)
mousemoveDOM에서 마우스아 움직일때 발생합니다.
mousedownDOM에서 마우스가 눌려졌을때 발생합니다.
mouseupDOM에서 마우스를 눌렀다가 눌렀다가 땟을때 발생합니다.
clickmousedown이벤트와 mouseup이벤트가 종료가 되면 발생합니다.
dblclick마우스를 더블클릭하면 발생합니다.
wheel마우스의 휠을 내리면 발생합니다.

마우스 이벤트의 종류에는 위에 테이블에 나와 있는 것보다 더 많이 있지만,
개발을 하면서 접하는 이벤트는 위에 이벤트가 대부분이라 생각됩니다.

이 중 몇가지 이벤트를 사용할 때 제대로 된 이해없이 사용하면 문제가 될 수 있는 이벤트를 설명하겠습니다.

mouseover와 mouseout

결론 부터 말씀드리면 아마 이 이벤트를 사용하는 개발자들이 원했던건 over와 out이 아니라 enter와 leave일것입니다.

mouseover와 mouseout은 하위 돔에서 발생하는 over와 out까지 캡쳐를 하기 때문에 만약 아래에 있는 소스코드처럼 작성하게 되면 계속해서 깜빡이는 현상이 발생하게 됩니다.

<div id="div1">
        안녕하세요
        <div id="div2">하위 DOM</div>
</div>
<script>
        var div1 = document.getElementById('div1');
        var div2 = document.getElementById('div2');

        div1.addEventListener('mouseout', function(e){
            div2.style.display='block';
        });

        div2.addEventListener('mouseover', function(e){
            div2.style.display='none';
        });
</script>

실무에서도 dropdown(마우스를 오버했을때 무엇인가 열리고 out했을때 닫히는)같은 것들을 개발할 때 이런 실수는 자주 일어나게 됩니다.

mousemove와 scroll이벤트(사실scroll은 마우스이벤트는 아니고 브라우저 이벤트입니다.)

mousemove와 scroll이벤트는 특정 dom을 이동시켜 다른 dom으로 옮기거나 무한스크롤을 구현할 때 쓰이는 이벤트입니다.

이 두가지는 속도저하라는 큰 문제를 가지고 있습니다.
크롬은 그나마 최적화가 되어있지만, IE같은경우 mousemove와 scroll은 콘솔만 찍어도 안될 정도로 치명적인 속도저하 문제를 가지고 있습니다.

<style>
    html, body {
        width: 100%;
        height: 100%;
    }
</style>
<body>
    <script>
       document.body.addEventListener('mousemove', function(){
           console.log('mousemove');
       });
    </script>
</body>

위에 소스코드를 크롬과 IE에서 확인을 해보세요.

이 문제를 해결하기 위해서는 debounce 또는 throttle 기법, requestAnimationFrame등을 활용해서 해결할 수 있습니다.

debounce는 특정 시간동안 동일한 이벤트가 발생하지 않도록 도와주는 기법이고
throttle같은경우 특정시간이 지나야만 이벤트가 호출될 수 있도록 해주는 기법입니다.

두개의 차이점은 동일한 이벤트 호출시 debounce는 호출이 되지않고 이벤트 지연시간이 초기화 된다는 것이고(50밀리세컨드 이후에 이벤트를 실행하라고 했는데 다시 실행하면 다시 50밀리세컨드가 지나야지 이벤트를 발생할수 있습니다.) throttle은 이벤트 발생이후 지연시간이 지나면 다시 호출이 되어진다는 것입니다.

활용사례는 무한스크롤같은경우 debounce로 이벤트가 종료가 되어진다음에 발생시켜야지 중복된 이벤트 발생을 막을 수 있고, DOM의 위치를 변경하는 javascript같은경우에는 실시간으로 이동되어지는 것처럼 보이면서 이벤트의 횟수를 줄이기 위해 throttle을 사용해야 합니다.

하지만, throttle같은 경우 좋은 기법이긴 하지만, repainting(화면에 dom이 그려지는 내부이벤트)가 발생하기 때문에 아직도 성능저하의 문제를 가지고 있는데 그럴때 requestAnimationFrame을 사용하셔서 repainting을 브라우저 코어에게 위임하여 해당 속도 문제를 해결할 수 있습니다.

하지만 requestAnimationFrame은 IE10이후 부터 지원이 되기 때문에 polyfill(구 브라우저에서도 돌아갈 수 있도록 도와주는 코드조각)을 넣어주셔야 합니다.

debounce와 throttle사용하기

두가지 기법을 사용하기 위해서는 polyfill을 사용하셔도 되지만 underscorejs를 사용하면 두개의 함수를 사용할 수 있습니다.

<head>
    <script src="http://underscorejs.org/underscore.js"></script>

    <style>
        html,
        body {
            width: 100%;
            height: 100%;
        }

        body {
            height: 30000px;
        }
    </style>
</head>
<body>
    <script>
        window.addEventListener('mousemove', _.throttle(function () {
            console.log('mousemove');
        }, 100));
    </script>
</body>

underscorejs는 용량도 크고 좋은 기능을 많이 제공하기 때문에 한번 사용해보시면 좋을것 같습니다.

위에는 mousemove는 throttle로 변경을 한 예제인데 기존에는 이벤트가 엄청 자주 발생했는데, 수정 이후에 이벤트 호출 횟수가 눈에 띄게 줄어든것을 확인할 수 있습니다.

debounce로 수정한 다음에도 테스트를 한번 해보시면 debounce와 throttle의 차이를 명확히 알 수 있게 되니 한번 해보시면 좋을것 같습니다.

참고자료
https://www.w3schools.com/jsref/dom_obj_event.asp
https://css-tricks.com/debouncing-throttling-explained-examples/

Sort:  

Hello I like your post I just gave you an upvote Make me a visit on my blog and if you like something of me an upvote too.@celioeguga

thanks, I also voted for your blog.