본문 바로가기

JavaScript

[JavaScript] 여러 태그에 한번에 이벤트 걸기 가능...?

728x90

 

다중 이벤트

 

  <h3>여러 이벤트를 한번에 걸어주기/h3>

    <ul>
        환혼 주인공들:
        <li class = "item">1.장욱</li>
        <li class = "item">2.서율</li>
        <li class = "item">3.당구</li>
        <li class = "item">4.낙수</li>
        <li class = "item">5.진초연</li>
    </ul>
    

    선택한 태그의 값:
    <p class="result"></p>

    <script>

        var result = document.querySelector(".result");
        var item = document.querySelectorAll(".item");

        //요방법은 비효율적인 방법이다!! - 태그 10000개라면....?
        //좋은 코드라 할 수 없다
        for(var i = 0; i < item.length; i++){
            item[i].onclick = function(){
               result.innerHTML = this.innerHTML;
            }
        }
    </script>

 

Document

여러 이벤트를 한번에 걸어주기

    환혼 주인공들:
  • 1.장욱
  • 2.서율
  • 3.당구
  • 4.낙수
  • 5.진초연
선택한 태그의 값:

 

 

위의 코드처럼 이벤트 실행 함수에 반복문을 걸어서 한 번에 다중으로 이벤트를 걸 수 있지만,

이는 매우 비효율적이라서  다음 코드처럼 이벤트 전파를 이용해 이벤트를 걸어주는 것이 좋다

 

이벤트 전파를 하기 위해서는 이벤트 객체에 대해 먼저 알아야 한다

이벤트 객체란, 이벤트를 발생시킨 요소와 발생한 이벤트에 대한 정보를 제공하는 것을 말한다

이벤트가 발생하면, 이벤트 객체는 동적으로 생성되어 이벤트 핸들러에 암묵적으로 전달된다

 

 

 

 

    <h3>이벤트 전파 특성 활용하기⭐⭐⭐</h3>

        <ul class="parent">
            환혼 주인공들:
            <li class = "item">1.장욱</li>
            <li class = "item">2.서율</li>
            <li class = "item">3.당구</li>
            <li class = "item">4.낙수</li>
            <li class = "item">5.진초연</li>
        </ul>
    
        선택한 태그의 값:
        <p class="result"></p>

        <script>

            var result = document.querySelector(".result");
            var parent = document.querySelector(".parent");

             //부모 태그에 이벤트 걸면 자식 태그에도 전파됨
            parent.onclick = function(e){
                //console.log(this);  //ul태그 
                //result.innerHTML = this.innerHTML;

                //이벤트 객체 - 이벤트 함수의 첫번째 매개변수로 자동 전달
                // console.log(e);
                // console.log(event);

                console.log(event.target); //이벤트가 동작된 실제 태그
                console.log(event.currentTarget); //이벤트가 걸려있는 실제 위치

                // console.dir(e.target); - 기억 안 날 때 보고 쓰자!
                if(e.target.tagName != "LI") return; //li태그가 아니라면 종료
                result.innerHTML = e.target.innerHTML; //this 대신 e.target으로 사용하면 됨

            }

        </script>

 

위 코드처럼 이벤트 핸들러를 선언할 때, 이벤트 객체를 전달 받을 첫번째 매개변수('e' or 'event')를 명시적으로 선언해야 한다

 

 


이벤트 객체의 기능

1️⃣ target - 이벤트를 적용한 타겟 속성

2️⃣ currentTarget - 실제 이벤트가 걸려있는 타겟 속성

3️⃣ stopPropagation( ) - 이벤트 전파를 막는 기능(버블링 중단)

4️⃣ preventDefault( ) - 고유한 특성을 가진 태그의 이벤트를 제거 ( ex. < a태그 >, < submit > )

 


 

e.target & e.currentTarget

e.target : 이벤트가 동작된 실제 태그

e.currentTarget: 이벤트가 걸려있는 실제 위치

 

 

 

 

👩‍💻 이벤트 객체 활용해보기

 

✅ 사진을 클릭해서 결과창에 클릭한 사진이 나오게끔 코드를 작성하시오

 

   <h3>이벤트객체활용</h3>
    <div>
        <ul class="select">
            <li><img src="img/1.jpg" alt="1" width="100"></li>
            <li><img src="img/2.jpg" alt="1" width="100"></li>
            <li><img src="img/3.jpg" alt="1" width="100"></li>
            <li><img src="img/4.jpg" alt="1" width="100"></li>
        </ul>
        결과:
        <div class="result">
            <img src="img/1.jpg" alt="결과" width="300" >
        </div>
    </div>

    <script>

        var select = document.querySelector(".select");
        var result = document.querySelector(".result >img");

        select.onclick = function(e){
          result.src= e.target.src;
        // result.innerHTML = e.target.outerHTML;
       
        }


    </script>

 

 

 

이벤트의 전파원리 - 이벤트 버블링 & 캡쳐링

 

이벤트버블링

부모, 자식 모두에 이벤트가 있을 때, 이벤트가 자식 ➡️ 부모방향으로 전파되는 특징이 있다 

      

<body>

    <div class="func1">
        <div class="func2">
            <div class="func3">func3 자식</div>
        </div>
    </div>
    
    <script>
        /*
         DOM에서 이벤트 동반 방식은 기본으로 버블링이다
         버블링 - 부모, 자식 모두에 이벤트가 있을 때 이벤트가 자식 -> 부모 방향으로 전파되는 특징
        
         3번 클릭 3 -> 2 -> 1로 실행
        */

        var func1 = document.querySelector(".func1");
        func1.onclick = function(){
            alert("func1");
        }

        var func2 = document.querySelector(".func2");
        func2.onclick=function(){
          event.stopPropagation(); //이벤트 버블링 중단
       	  alert("func2");
        }

         var func3 = document.querySelector(".func3");
         func3.onclick=function(){
           alert("func3");
        }
     </script>

 

 

Document
func3 자식

 

 stopPropagation ( ) -  이벤트 전파를 막는 기능

 

    func2.onclick=function(){
      event.stopPropagation(); //이벤트 버블링 중단
      alert("func2");
    }

 

 

이벤트 캡쳐링

부모, 자식에 이벤트가 있을 때, 부모 ➡️ 자식으로 전파되는 특징이 있고 addEventListener(  ) 방식으로만

구현해 줄 수 있다  

 

   /*
     이벤트 캡쳐링 -> 부모, 자식에 이벤트가 있을 때, 부모 -> 자식으로 전파되는 특징
     addEventListener() 방식으로만 구현해줄 수 있다
     잘 안 쓴다(모든 부모 태그를 다 불러오기 때문에)
   */

        var func1 = document.querySelector(".func1");
        var func2 = document.querySelector(".func2");
        var func3 = document.querySelector(".func3");

        func1.addEventListener("click", function(){
            alert("fun1");
        }, true);

        func2.addEventListener("click", function(){
            alert("fun2");
           event.stopPropagation(); //이벤트 버블링 중단 -> 부모 태그만 잔뜩 실행되고 정작 내 거는 실행이 안 됨
       
       }, true);

        func3.addEventListener("click", function(){
            alert("fun3");
            
        }, true);

 

이벤트 캡쳐링은 자식 태그 하나를 실행하기 위해서는 부모태그에 걸려있는 이벤트부터 모두 실행되기 때문에

문제가 발생할 수 있어 잘 사용하지는 않는다

 

 

Document
func3 자식

 

 

이벤트가 위임되었을 때,  클래스가 여러 개이면 어떻게 해야 할까?

다음 실습을 통해 확인해보자!

 

    <section id="section">
        <div class="list">
            <div>
                <span>홍길동</span>
                <button type="button" class="btn sel">선택</button>
                <button type="button" class="btn del">삭제</button>
            
            </div>
            <div>
                <span>김길동</span>
                <button type="button" class="btn sel">선택</button>
                <button type="button" class="btn del">삭제</button>
            </div>
            <div>
                <span>홍길자</span>
                <button type="button" class="btn sel">선택</button>
                <button type="button" class="btn del">삭제</button>
            </div>
        </div>
    </section>

    <script>
        var list = document.querySelector(".list");

        list.onclick = function(){

            if(event.target.tagName != "BUTTON") return; // 버튼이 아니라면 종료
            

            // 부모한테서 이벤트 위임받고 각각 태그별로 구분하려면 className 활용하기!
            if(event.target.classList.contains("sel")){ //선택 버튼 누르면 색깔 바꾸기
                event.target.previousElementSibling.style.backgroundColor = "pink";
                event.target.previousElementSibling.style.color = "white";

            }else if(event.target.classList.contains("del")){ //삭제 버튼 누르면 삭제
                event.target.parentElement.remove();
            }
            
        }

    </script>

 

 

Document
홍길동
김길동
홍길자

 

 


 

이벤트를 태그 전체에 걸었을 때 각각 태그별 다른 기능을 한다면 그에 맞게 구별해줘야 한다

어떻게 구분하냐면..클래스명으로 구별한다! 

 

  // 부모한테서 이벤트 위임받고 각각 태그별로 구분하려면 className 활용하기!
  if(event.target.classList.contains("sel")){ //선택 버튼 누르면 색깔 바꾸기
     event.target.previousElementSibling.style.backgroundColor = "pink";
     event.target.previousElementSibling.style.color = "white";

 

만약 이벤트가 적용될 태그의 클래스명에 "sel"이 있다면( = "선택" 버튼을 누른다면 ) 버튼 태그의 이전 형제 태그인 span 태그의 style 속성을 바꿔준다

 

 

   } else if(event.target.classList.contains("del")){ //삭제 버튼 누르면 삭제
         event.target.parentElement.remove();
   }

 

만약 이벤트가 적용될 태그의 클래스명에 "del"이 있다면( = "삭제" 버튼을 누른다면 ) 버튼 태그의 부모 태그인

div태그 자체를 삭제한다

 

그런데 이 조건문은 왜 필요한 걸까?

   if(event.target.tagName != "BUTTON") return; // 버튼이 아니라면 종료

 

이 조건문은 이벤트의 위임으로 발생할 수 있는 문제를 사전에 방지하고자 함이다. 이벤트 위임은 부모 태그 자체에

이벤트를 걸어서 자식 태그에 그 이벤트를 위임하는 것이기 때문에 혹여나 다른 태그도 포함되어 있을 때 그 태그에까지 이벤트가 걸리게 되면 안 되기 때문에 애초에 태그명이 "BUTTON"인 경우에만 이벤트가 적용이 되도록 하는 것이다.

 


이벤트 위임을 이용한 실습 응용ver

 

  <h2>이벤트 위임 이용해서 다음을 만들어 보세요.<h2>

        <table>
            <thead>
                <tr>
                    <th>번호</th>
                    <th>제목</th>
                    <th>내용</th>
                    <th>삭제</th>
                </tr>
            </thead>
            <tbody class="list">
                <tr>
                    <td>1</td>
                    <td>첫글</td>
                    <td>hi</td>
                    <td><button type="button">삭제</button></td>
                </tr>
                <tr>
                    <td>2</td>
                    <td>첫글</td>
                    <td>hi</td>
                    <td><button type="button">삭제</button></td>
                </tr>
                <tr>
                    <td>3</td>
                    <td>첫글</td>
                    <td>hi</td>
                    <td><button type="button">삭제</button></td>
                </tr>
                <tr>
                    <td>4</td>
                    <td>첫글</td>
                    <td>hi</td>
                    <td><button type="button">삭제</button></td>
                </tr>
            </tbody>
        </table>

        <script>
            var list = document.querySelector(".list");
               
            list.onclick = function(e){

            if(e.target.tagName != "BUTTON") return; //button이 아닌 다른 태그를 눌렀을 때 처리되지 못 하게 예외 처리           
              list.removeChild(e.target.parentElement.parentElement); //tr 삭제 (정신 똑바로 차려라 이렇게 간단한 걸 혼자 난리부르스를 치고 앉았네^^)
               //e.target.parentElement.parentElement.remove();
            }
        </script>

 

Document

이벤트 위임 이용해서 다음을 만들어 보세요.

번호 제목 내용 삭제
1 첫글 hi
2 첫글 hi
3 첫글 hi
4 첫글 hi

 

 

위의 코드를 자세하게 살펴보면 다음과 같다

 

    var list = document.querySelector(".list");
           
    list.onclick = function(e){

    if(e.target.tagName != "BUTTON") return; //button이 아닌 다른 태그를 눌렀을 때 처리되지 못 하게 예외 처리           
        list.removeChild(e.target.parentElement.parentElement); //tr 삭제 (정신 똑바로 차리자^^)
        //e.target.parentElement.parentElement.remove();
     }

 

이벤트를 하나하나 다 걸어주기엔 이벤트의 기능이 동일하기 때문에 이벤트가 필요한 태그를 감싸고 있는 부모 태그에 이벤트를 한 번 걸어주었다. ( 변수 list에 부모 태그 저장 )

list 태그를 클릭했을 때 함수가 실행되는데, 이벤트가 적용될 태그의 태그명이 "BUTTON"이 아닌 경우에는 실행이

되지 못 하게 사전에 오류를 방지했다.

그리고 태그가 삭제 기능을 갖추도록 해야 하는데, 이 때 지워져야 하는 건 버튼 태그가 아닌 tr 태그이기 때문에 e.target.parentElemnt.parentElement로 지정해주면 각각 태그에 맞게 삭제 기능이 잘 수행될 것이다.

 


preventDefault ( )  -  고유한 특성을 가진 태그의 이벤트를 제거

 

   <div>
        <ul class="page">
            <li><a href="list.board">1</a></li>
            <li><a href="list.board">2</a></li>
            <li><a href="list.board">3</a></li>
            <li><a href="list.board">4</a></li>
            <li><a href="list.board">5</a></li>

        </ul>
    </div>

    <script>

        var page = document.querySelector(".page");
        page.onclick = function(){
            event.preventDefault(); //a태그 or submit이 가진 기본(고유)이벤트 중단 -> 자바스크립트로 처리 가능
            if(event.target.tagName != "A") return; //  A 태그가 아니라면 종료
            console.log(event.target.innerHTML);
        }

    </script>

    <hr>

   <!-- submit도 고유한 이벤트를 갖고 있음 -->
    <form action="https://www.naver.com">
        <input type="text" name="age">
        <input type="submit" name="클릭" id="btn">
    </form>

    <script>
        var btn = document.getElementById("btn");
        btn.onclick = function(){
            event.preventDefault(); //submit의 고유이벤트 중단
            //...처리
            
        }

    </script>

 

 

고유한 이벤트를 제거하지 않았을 때 ( a태그, submit ) 

 

Document

 

a의 태그의 경우 고유한 이벤트가 하이퍼링크가 걸려있는 것으로 자동으로 해당 링크를 통해 넘어가게 되고

submit의 경우 폼을 웹에 전달하는 역할을 하기 때문에 정보를 넘겨버리는 이벤트가 작동된다

 

따라서 위와 같은 태그들을 사용할 때는 태그 자체가 고유하게 갖고 있는 이벤트들을 제거해줘야 한다

👉 preventDefault ( ) 사용

 

 

태그의 dataset 속성 ( = 태그의 저장소 )

 dataset은 태그 내부에서 "data - 속성명"로 1개의 데이터셋 속성을 지정한다

 

   <h3> 태그의 dateset속성 - 태그의 저장소 </h3>

    <!-- data- 으로 시작하고, 뒤에 오는 이름은 자유롭게 작성 -->
    <ul class="list">
        <li><a href="#" data-user-info='{"id":"1", "age":"10"}'>홍길동</a></li>
        <li><a href="#" data-user-info='{"id":"2", "age":"20"}'>홍길자</a></li>
        <li><a href="#" data-user-info='{"id":"3", "age":"30"}'>이순신</a></li>
        <li><a href="#" data-user-info='{"id":"4", "age":"40"}'>박찬호</a></li>
    </ul>


    결과:
    <div calss="result"></div>

 

 

 

    <script>
      var list = document.querySelector(".list");
      list.onclick = function(){
          //1.
          event.preventDefault();
          //2.
          if(event.target.tagName != "A") return;
          //3. data-으로 만들어진 속성은 태그에서 찾아 쓸 수 있음
          // console.log(event.target.dataset.userInfo); 

          var data = event.target.dataset.userInfo;
          var result = JSON.parse(data);
          console.log(result);
      }

    </script>

 

위의 코드를 통해 데이터셋을 사용하는 일련의 과정을 확인해볼 수 있다.

1. 먼저 해당 태그는 < a 태그 >이기 때문에 고유한 이벤트를 가지고 있어 event.preventDefault( )로  이벤트를

    차단해줘야 한다.

2. 태그의 태그명이 "A" ( = a태그가 아닌 경우)이 아닌 경우에는 함수가 실행이 되지 않도록 사전에 방지해야 한다.

3.  data-으로 만들어진 속성은 태그에서 접근이 가능하므로 event.target.dataset.userInfo로 접근해 변수에

      저장할 수 있다  

 

 

 

문자열 형식

  var data = event.target.dataset.userInfo;
  console.log(data);

 

dataset의 속성값이 객체로 저장되어 있기 때문에 바로 형변환 없이 바로 출력하면 다음과 같이 나온다

 

 

 

문자열 👉 JSON 형식

 

 var result = JSON.parse(data);
 console.log(result);

 

제이슨 형식으로 사용하기 위해서는 위에서처럼 형변환을 해주어야 다음과 같은 결과를 받아볼 수 있다

 

 

 


👩‍💻 이벤트 버블링을 활용한 토글탭 만들어보기

 

다음과 같이 조건을 바탕으로 코드를 작성하면 된다

 

    <!--
    1. ul에 이벤트 버블링을 이용해서 클릭이벤트를 걸고 클릭되는 타겟의 data-id를 얻습니다.
    2. toggle-menu의 active속성을 삭제
    3. data-id의 값에 알맞는 태그에 active속성을 추가하면됩니다.
    -->

 

    <section>
        <ul class="toggle">
            <li data-id="#toggle1">메뉴1</li>
            <li data-id="#toggle2">메뉴2</li>
            <li data-id="#toggle3">메뉴3</li>
        </ul>

        <div>
            <div class="toggle-menu active" id="toggle1">
                토글메뉴1
            </div>
            <div class="toggle-menu " id="toggle2">
                토글메뉴2
            </div>
            <div class="toggle-menu " id="toggle3">
                토글메뉴3
            </div>
            
        </div>

    </section>

 

 

  <script>
  
      var toggle = document.querySelector(".toggle");
      toggle.onclick = function() {
      if(event.target.tagName != "LI") return;

      var trigger = event.target.dataset.id
      var tag = document.querySelector(trigger); //id를 가지고 있는 태그

       //다른태그는 active삭제
       var menu = document.querySelectorAll(".toggle-menu");
       for(var i = 0; i < menu.length; i++) {
           menu[i].classList.remove("active");
       }
            
          tag.classList.add("active"); //active추가
        }
    
    </script>

 

위 코드를 자세히 분석해보면...

 

   var trigger = event.target.dataset.id
   var tag = document.querySelector(trigger); //id를 가지고 있는 태그

 

이벤트가 적용될 부분(event.target)의 dataset의 속성인 id에 접근해 변수 trigger에 저장하고

그 trigger와 동일한 id를 갖고 있는 태그에 접근할 수 있도록 새로운 변수(tag)에 저장한다

    

 

   //다른태그는 active삭제
   var menu = document.querySelectorAll(".toggle-menu");
   for(var i = 0; i < menu.length; i++) {
       menu[i].classList.remove("active");
   }

 

toggle-menu의 active 속성을 활용해 이벤트가 작동하도록 해야하는데, active는현재 클래스명으로 존재하기 때문에 classList로접근해 제거해줘야 한다 ( active가 추가된 태그만 작동할 수 있도록 다른 태그는 제거해주기 )

 

 

   tag.classList.add("active"); //active추가

 

 

제거를 해주고 이벤트가 작동할 태그에는 active를 추가하면 된다

( 이 때 dataset으로 가져와서 쿼리셀렉터로 다시 추린 변수를 활용하면 된다 )    

 


https://cheonmro.github.io/2018/09/04/event-object/

 

Event Object(이벤트 객체)

Event Object(이벤트 객체)란 무엇인가? 이벤트 객체란, 이벤트를 발생시킨 요소와 발생한 이벤트에 대한 정보를 제공하는 것을 말한다. 이벤트가 발생하면, 이벤트 객체는 동적으로 생성되어, 이벤

cheonmro.github.io