Knowledge/javascript

자바스크립트로 리스트에 이벤트 다는 방법 ( Event delegation / 이벤트 위임 )

TakeKnowledge 2019. 8. 12. 21:51
반응형

먼저 자바스크립트로 이벤트를 걸기 위해선 어떻게 해야할까?

 

querySelector나 getElementBy를 활용해 이벤트를 걸 대상을 찾고

addEventListener를 활용해 원하는 이벤트를 걸어주면 된다

 

코드로 보면 아래와 같다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>자바스크립트 이벤트</title>
    <style>
        #target {
            width: 100px;
            height: 100px;
            background: blue;
        };
    </style>
</head>
<body>
    <div id="target"></div>
    <script>
        document.querySelector("#target").addEventListener("click",function(){
            alert("테스트");
        });
 
    </script>
</body>
</html>
 

잘 작동한다

그렇다면 같은 클래스명을 공유하는 여러개의 엘리먼트에 한번에 이벤트를 주기 위해선 어떻게 해야 할까?

 

querySelectorAll을 활용해 대상을 한꺼번에 찾고 이벤트를 달 때 for문을 활용하는 것만 약간 다르지 

전체적은 방법은 비슷하다. 예제 코드는 아래와 같다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>자바스크립트 이벤트</title>
    <style>
        .target {
            width: 100px;
            height: 100px;
            background: blue;
            margin-bottom: 20px;
        };
    </style>
</head>
<body>
    <div id="first" class="target"></div>
 
    <div id="last" class="target"></div>
    <script>
 
        var target = document.querySelectorAll(".target");
        
        var tartgetLength = target.length;
 
        for(var i=0; i < tartgetLength; i++){
            target[i].addEventListener("click",function(){
                console.log("#"+this.id+"에 이벤트 추가");
        });
        }
 
 
    </script>
</body>
</html>

 

역시 잘 작동한다

 

쉽다. 여기까지는 굳이 포스팅까지 할 필요가 없는 사안이다.

그러나 상상력을 발휘해서 가정을 한번 해보자.

만약  100개의 리스트가 있고 여기에 모두 이벤트 리스너가 필요하다면?

그 때도 이 방법이 효율적일까? 그렇다고 대답하는 개발자는 많지 않을 것이다.

 

실제로 브라우저가 기억하고 있어야 하는 이벤트 리스너 갯수가 많아져서 메모리를 많이 사용해야 하고

ajax로 엘리먼트를 추가하면 그 때마다 이벤트 리스너도 추가해줘야 하는 상황이 발생한다.

 

그럼 이 문제를 효율적으로 해결하려면 어떻게 해야 할까?

이벤트 위임이라는 방법이 있다.  

 

일단 이를 이해하기 위해 자바스트립트 이벤트 특성을 아래 코드와 결과를 보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>자바스크립트 이벤트</title>
    <style>
        .target {
            width: 100px;
            height: 100px;
            background: blue;
        };
    </style>
</head>
<body>
    <div class="one target">
        <div class="two target">
            <div class="three target">
            </div>
        </div>
    </div>
    <script>
 
    
    var targets = document.querySelectorAll('.target');
    targets.forEach(function(target) {
        target.addEventListener('click', logEvent);
    });
 
    function logEvent(event) {
        console.log(event.currentTarget.className);
    }
    </script>
</body>
</html>

 

div 세개를 겹쳐놓고 클릭시엔 현재 타켓의 클래스 명을 클릭하는 이벤트다. 

그런데 클릭하면

 

콘솔에 세개가 찍힌다.

어떻게 된 일일까? 

 

세개가 겹쳐져 있으니 클릭된 건 가장 위에 있는 .three target만 콘솔에 찍혀야 맞는 것 아닐까?

 

그러나 위 결과가 말해주듯 자바스크립트의 이벤트는 전달된다.

이처럼 하위 엘리먼트부터 상위 엘리먼트로 퍼져나가는 걸 이벤트 버블링

 

 

이렇게 상위 엘리먼트로부터 하위 엘리먼트로 퍼져나가는 걸 이벤트 캡쳐링이라 한다

1
2
3
4
5
6
7
8
9
10
    var targets = document.querySelectorAll('.target');
    targets.forEach(function(target) {
        target.addEventListener('click', logEvent,{
            capture : true
        });
    });
 
    function logEvent(event) {
        console.log(event.currentTarget.className);
    }

캡쳐링은 아래와 같이 addEventListener의 세번째 인자 위치에서 capture를 true로 설정하면 감지할 수 있다

( false가 기본값 )

 

사실 굳이 버블링과 캡쳐링을 구현해보지 않더라도 이런 특성은 

1
2
3
4
5
6
7
8
    var targets = document.querySelectorAll('.target');
    targets.forEach(function(target) {
        target.addEventListener('click', logEvent);
    });
 
    function logEvent(event) {
        console.log(event);
    }

이렇게 event만 콘솔로 찍어봐도 알 수 있다

 

path에 순서대로 window까지 찍혀있는 것을 보이는가?

 

원래 하던 이야기로 돌아가서 이벤트 위임은 이러한 javascript 특성을 살려서

상위 엘리먼트 하나에만 이벤트를 줘서 하위 엘리먼트들의 이벤트를 제어하는 방식이다

 

본격적으로 이벤트 위임에 대해 알아보자

 

- 이벤트 위임

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>자바스크립트 이벤트</title>
    <style>
        ul{
            background: blue;
        }
        li{
            background: yellow;
        }
        a{
            background: red;
            text-decoration: none;
            color : black;
        }
 
    </style>
</head>
<body>
    <ul>
        <li><a href="#">하나</a></li>
        <li><a href="#"></a></li>
        <li><a href="#"></a></li>
        <li><a href="#"></a></li>
        <li><a href="#">다섯</a></li>
    </ul>
    <script>
 
    var ul = document.querySelector('ul');
 
    ul.addEventListener("click",function(e){
        console.log(e.target);
    });
 
    </script>
</body>
</html>

 

사실 별 거 없다. 이렇게 ul 하위에 li > a 가 여러개 있을 경우

상위 엘리먼트인 ul 하나에만 클릭 이벤트 리스너를 달아서 제어하는 것이다.

 

이렇게 세팅해서 각 부분을 클릭해보면

 

클릭한 그 부분이 콘솔에 찍히는 걸 볼 수 있다.

그러면 이제 저 부분들을 이용해서 각자 원하는 기능을 구현하면 된다.

 

하나 팁을 주자면 이렇게 막 구현한 코드 말고

실제로 리스트를 구성하는 ul > li > a는 영역이 엄격히 구분되어 있지 않다. 

유저는 a를 클릭하고 싶어서 a를 클릭했는데 li가 클릭되고 이런 경우가 있을 수 있다는 말.

 

그렇기 때문에 이벤트 위임을 이용할 때는 if 문을 활용해 

e.target.tagName이 'A' 일 경우 / e.target.tagName이 'LI' 일 경우 / e.target.tagName이 'UL' 일 경우 

각각 엄격히 처리해 두는 것이 훗날 정신 건강에 좋다

 

ps. 오류가 없도록 최선을 다했지만 배우는 입장에서 작성한 포스트입니다.

    틀린 부분을 지적해주신다면 확인해보고 감사히 반영하겠습니다.

 

참고 : 

 

 

[LECTURE] 3) Event delegation : edwith

들어가기 전에 list(어떤 목록)가 여러 개인 UI에 각각 비슷한 이벤트를 걸어서 처리해야 한다면 어떻게 해야 할까요?  for문으로 addEventListener를 사용해야 할... - moons

www.edwith.org

 

이벤트 버블링, 이벤트 캡처 그리고 이벤트 위임까지

(기본) 이벤트 버블링, 이벤트 캡처링, 그리고 이벤트 위임까지 이벤트 전달 방식과 관련된 모든 것을 파헤쳐 봅니다.

joshua1988.github.io

 

반응형