Recording/멋쟁이사자처럼 BE 13기

[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_27일차_"자바스크립트 이벤트"

LEFT 2025. 1. 9. 17:03

🦁멋쟁이사자처럼 백엔드 부트캠프 13기 🦁
TIL 회고 - [27]일차

🚀27일차에는 자바스크립트의 이벤트 처리에 대해 배울 수 있었다.

HTML과 연동되어 동작하면서 자바스크립트 코드로 HTML태그에 이벤트를 생성하는 방법을 보니

웹 애플리케이션이나 웹페이지에서 자주 보이는 동작과 유사하였다.

회고를 진행하면서 이벤트를 응용하는 실습을 해봐야겠다고 생각했다.


자바스크립트 이벤트

  • 이벤트를 처리할때 HTML태그에 이벤트를 적용해주게 되는데
    HTML태그와 자바스크립트가 연동되는 과정이라고 볼 수 있다.

  • HTML의 DOM에 자바스크립트의 이벤트를 적용해주는 과정을 하게 된다.

DOM

  • 문서 객체 모델 (Document Object Model)
  • XML이나 HTML문서에 접근하기 위한 일종의 “인터페이스”
  • 이 문서 객체 모델 (DOM)은 문서 내의 모든 요소를 정의하고, 각각 요소에 접근하는 방법을 제공

  • 만약 사용자의 입력(혹은 상호작용)에 따라 동적으로 브라우저의 UI를 업데이트하고 싶다면
    자바스크립트를 연동해주어야한다.
  • 보통 상호작용이 많은 경우 Vanilla JavaScript를 사용해서는 많은 양의 코드로 관리가 어려워지므로
    ➡️React, Vue, Angular 등의 도구를 사용한다.
  • Vanilla JavaScript : 순정 자바스크립트만 사용 = 라이브러리/프레임워크는 사용하지 않음

카운터

  • 버튼을 클릭하면 숫자가 올라가거나 내려가는 기능 구현
<body>
  <!--이벤트 처리-->
  <!--이벤트 처리할 대상 (어느 태그에 사용할 것)-->
  <script>
      // 이벤트 대상 = addEventListener() : 이벤트를 걸어주는 표준함수    
  </script>
</body>
  • 기본적인 구조 : 이벤트를 걸어주는 표준함수 addEventListener()를 사용
  • addEventListener()의 인자 : 
    💡addEventListener(eventName, eventHandler, ToCapture);
    ➡️eventName : type이라고도 불리며, 클릭이나 드래그 등 수신할 이벤트 타입을 나타냄
    ➡️eventHandler : Listener라고도 불리며 실제 이벤트가 발생되었을때 할 일을 지정한다
    즉 Event 인터페이스를 구현한 객체 (=지정한 이벤트)를 수신할 객체이며, “함수로 전달”한다.
    (ex. 버튼을 클릭했을때 구현할 작업을 “함수로 전달”)

    ➡️ToCapture : options라고도 불리며, 기본값은 false이지만 생략가능.
    이벤트가 전달되기 전에 이 것이 먼저 발동되어야한다고 알리는 불리언(boolean)값

HTML 태그 찾기

<button>클릭</button>

<script>
  // 이벤트 대상 = addEventListener() : 이벤트를 걸어주는 표준함수
  const btn = document.querySelector("button");
</script>
  • querySelector()로 “button”태그를 단순히 넘겨주기만 하면 버튼을 찾아낼 수 있다.
<button>클릭</button>

<script>
  // 이벤트 대상 = addEventListener() : 이벤트를 걸어주는 표준함수
  const btn = document.querySelector("button");

  // 버튼의 EventHandler (두번째 인자) 정의
  const clicked = () => {
    alert("버튼이 클릭되었습니다.");
  };

  btn.addEventListener("click", clicked);
</script>

  • 첫번째 인자로 이벤트 타입을 정의하고
  • 두번째 인자로 함수를 전달한다. (함수는 밖에서 정의한 clicked()를 사용한다.)
  • 여기서 버튼이 한 개 더 들어오게되면 이때부터는 버튼을 구분하기 위해서 버튼 태그의 id를 지정해주어야한다.
  • 이 경우 querySelector()의 형태또한 바뀌게된다. ➡️document.querySelector("#btn2");
  • id값이므로 “#btn2”로 찾는다. 만약 class값이면 (".btn2")일 것이다.

▶️실습 - “날짜 출력” 버튼

// 버튼2는 현재 날짜를 팝업으로 출력하도록 이벤트 구현
const btn2 = document.querySelector("#btn2");
btn2.addEventListener("click", () => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString("ko-KR", {
      year:"numeric",
      month:"2-digit",
      day:"2-digit",
  });
  alert(formattedDate);
});
  • new로 Date()함수를 생성한다음 toLocalDateString()으로 출력 포맷 형식을 지정해주어야한다.
  • 포맷을 지정하지 않으면 1970년 첫 날을 기준으로 현재까지의 지내온 시간을 ms(밀리초)로 반환하기 때문에
    긴 숫자만 출력된다. (=타임스탬프)
  • year 속성 : numeric ("2025"를 표시), 2-digit ("25"를 표시)
  • month 속성 : numeric ("1"을 표시), 2-digit ("01"를 의미), long ("1월"로 표시)
  • day, hour, minute, second 속성들도 numeric, 2-digit 속성값을 가짐

🚀실습 - 레벨업 버튼 구현

// 버튼 3 - 숫자를 올리도록 이벤트 구현
const btn3 = document.querySelector("#btn3");
let count = 0;
btn3.addEventListener("click", () => {
  count++;
  let printCount = "[+1 레벨업!] Lv." + count;
  alert(printCount);
});


querySelectorAll()

  • querySelector() : 단일 태그만 찾아낸다. 따라서 버튼이 3개가 있으면 첫번째 만난 버튼의 요소만 리턴하게된다.

  • querySelectorAll() : 모든 요소를 찾아서 리스트로 리턴한다.
    ex. querySelectorAll(”button”) ➡️ 모든 버튼을 찾아서 리스트로 리턴한다.

getElementById()

  • Id값과 일치하는 태그를 찾아 리턴 (단일)
<button class="btn">class 버튼1</button>
<button class="btn">class 버튼2</button>
<button class="btn">class 버튼3</button>
<button id="button">id를 가진 버튼</button>
const btn_id = document.getElementById("button"); // Element 여서 하나만 리턴
console.log("\\n-----[버튼 id 찾기]-----");
console.log(btn_id);

 

getElementsByTagName()

  • 태그이름과 일치하는 태그들을 찾아 배열로 리턴
<body>
		<!-- 버튼들...-->
    <script>
      const btns = document.getElementsByTagName("button"); // Elements 여서 배열로 리턴
      console.log("\\n-----[버튼들의 태그 이름 찾기]-----");
      console.log(btns);
	  </script>
</body>

 

getElementsByClassName()

  • 클래스 이름과 일치하는 태그들을 찾아 배열로 리턴
<body>
		<!-- 버튼들...-->
    <script> 
		  function testCode() {
	      alert("테스트 함수입니다.");
	    }
      const btn_classes = document.getElementsByClassName("btn"); // 클래스 이름이 btn인 Elements 배열로 리턴
      console.log("\\n-----[버튼들의 클래스 이름 찾기]-----");
      console.log(btn_classes);
      btn_classes[0].addEventListener("click", testCode); 
    </script>
</body>
  • btn_classes[0].addEventListener(”click”, testCode”);
    ➡️배열이기때문에 인덱스로 접근하여 하나의 태그를 선택하여 이벤트를 설정해주어야한다.

  • 브라우저를 출력해보면 이처럼 배열로 받아온 것을 확인할 수 있다.

▶️실습 - 배경색상을 변경해주는 버튼 구현

<body>
  <button>배경색상 변경</button>

  <script>
    function changeColor() {
      const color = prompt();
      document.body.style.backgroundColor = color;
    }

    const btn = document.querySelector("button");
    btn.addEventListener("click", changeColor);
  </script>
</body>

  • document.body.style.backgroundColor
    ➡️document 문서의 body 태그에 들어가서 그 body태그의 style 속성 중 backgroundColor를 설정
  • prompt()
    ➡️입력을 받는데 red, blue, lightgreen 처럼 색상의 "이름"값을 받아서 바로 backgroundColor에 적용

 

▶️실습 - 입력받은 텍스트를 화면에 출력

  • <div> 부분에 입력받은 텍스트를 출력
<body>
  <button id="input_btn">화면에 텍스트 입력</button>
  <div></div>

  <script>
    const btn2 = document.querySelector("#input_btn");
    btn2.addEventListener("click", () => {
      const text = prompt();
      document.querySelector("div").innerHTML = text;
    });
  </script>
</body>
  • prompt()로 사용자 입력을 받고 querySelector(”div”)로 div태그를 찾아서 innerHTML을 통해 텍스트를 바꾼다.
  • 여러 개의 div가 있어도 “첫번째 만난 요소만”을 변경하기때문에 사용이 가능한 방법
    ➡️만약 여러개의 div를 한번에 바꾸고 싶다면 클래스로 지정하고,
    특정 div만 바꾸고 싶다면 id값을 지정하면 될 것이다.

🚀실습 - 여러 개의 div 태그를 입력된 값으로 바꾸기

  • 만약 여러개의 div이지만 아무속성이 없는 div태그, 클래스를 가진 div태그, id를 가진 div태그
    어떻게 선택해서 prompt()로 바꿔주어야할까를 고민해보았다.
<body>
    <div></div>
    <div class="messages"></div>
    <div class="messages"></div>
    <div class="messages"></div>
    <div id="id_msg"></div>

    <script>
			// 텍스트 입력 버튼을 눌렀을 경우
      const btn2 = document.querySelector("#input_btn");
      btn2.addEventListener("click", () => {
        const text = prompt();
        document.querySelector("div").innerHTML = text;
        const texts = document.querySelectorAll(".messages");
        for (let i = 0; i < texts.length; i++) {
          texts[i].innerHTML = text;
        }
        document.querySelector("#id_msg").innerHTML = text;
      });
    </script>
  </body>
  • <div> : 아무속성이 없는 이 태그는
    ➡️querySelector(”div”).innerHTML = text; 로 접근

  • <div class=”messages”> : 클래스가 있는 태그들은
    ➡️querySelectorAll(”.div”)로 해당 클래스가 있는 태그들이 texts 에 담기게되고
    ➡️for문 안에서 각 데이터들을 index로 탐색하여 innerHTML로 인해 text가 바뀌게된다.

  • <div id=”id_msg”> : id가 있는 태그는
    ➡️querySelector(”#id_msg”).innerHTML = text; 로 접근

  • Element를 확인해보면 모두 텍스트가 바뀌어서 잘 출력됨을 확인할 수 있다.

▶️실습 - 화면 클릭 시 배경색 변경

  • Math.floor(Math.random() * 256) 사용 : 랜덤한 r, g, b값을 계속 갱신
  • 클릭 이벤트가 발생할때마다 함수에서 r, g, b 값을 랜덤으로 변경
<body>
  <div>
		<h2>화면을 클릭하면<br />배경색을 변경합니다.</h2>
  </div>
  <script>
    function changeColor() {
      // Math.floor(Math.random()*256) 활용
      let r = Math.floor(Math.random() * 256);
      let g = Math.floor(Math.random() * 256);
      let b = Math.floor(Math.random() * 256);
      document.body.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
    }

    document.addEventListener("click", changeColor);
  </script>
</body>
  • backgroundColor로 색을 바꿀때는 템플릿 리터럴을 활용해 실제 값을 바로 넣어줄 수 있도록 한다.

화면 클릭 시 배경색 변경된 실행결과

▶️실습 - 화면 클릭 시 배경색 변경 (함수 분리)

<script>
  function changeColor() {
    document.body.style.backgroundColor = makeColor();
    console.log(makeColor());
  }

  function makeColor() {
    let r = Math.floor(Math.random() * 256);
    let g = Math.floor(Math.random() * 256);
    let b = Math.floor(Math.random() * 256);
    return `rgb(${r}, ${g}, ${b})`;
  }

  document.addEventListener("click", changeColor);
</script>

  • 함수를 분리하여 리턴값으로 받아서 갱신할 수 있도록 구현할 수도 있다.
  • changeColor된 부분에서 console.log()로 출력해보면 rgb값이 잘 출력되는 것도 확인할 수 있다.

이벤트 삭제

// 이벤트 삭제
document.removeEventListener("click", changeColor);
  • 위에서 정의되었던 이벤트가 한번 수행된 후에 이벤트가 삭제되어
    화면을 클릭하여도 더 이상 배경색상이 바뀌지 않는다.
// 3초 후 이벤트 삭제
setTimeout(() => {
  document.removeEventListener("click", changeColor);
  console.log("이벤트가 삭제되었습니다.")
}, 3000)
  • 지연시간 후에 이벤트를 삭제하도록 setTimeout()함수를 사용할 수도 있다.

이벤트 버블링 

  • Event Bubbling
  • 이벤트 안쪽에서 바깥쪽으로 전파되는 방식을 거품이 일어나는듯 하여 “이벤트 버블링”
  • 물(이벤트)가 아래에서 위로 올라오는 거품인 상황 ➡️ 하위요소에서 상위요소로 전파
<style>
  * {
    border: 3px solid black;
  }
</style>
<!-- ... -->
<body>
  <div onclick="alert('outer-div');">
    <div onclick="alert('inner-div');">
      <div onclick="alert('inner-inner_div');">
        <h1 onclick="alert('h1');">
          <p onclick="alert('p');">p태그</p>
        </h1>
      </div>
    </div>
  </div>
</body>

  • 이벤트버블링 : 위에 있는 <p>태그를 누르면 박스들이 겹쳐져있기때문에
    다른 박스들도 함께 눌리게되는 전파 방식을 가진다.


이벤트 캡처링

  • Event Capturing
  • 이벤트 바깥쪽에서 안쪽으로 전파되는 방식을 “이벤트 캡처링” (추가된 방식)
  • 비(이벤트)가 내리듯 위에서 아래로 내리는 상황 ➡️ 상위 요소에서 하위 요소로 전파
  • e.stopPropagation(); ➡️ 이벤트 전파를 막아주는 역할을 하는 함수
<body>
    <div class="pink">
      <div class="red"></div>
    </div>

    <script>
      let pinkbox = document.querySelector(".pink");
      let redbox = document.querySelector(".red");
      let body = document.querySelector("body");
      let html = document.querySelector("html");
      
      pinkbox.addEventListener(
        "click",
        function (e) {
          //   e.stopPropagation();
          alert("pink box!!");
        },
        false
      );
      // ...다른 박스들 또한 addEventListener 추가...
    </script>
  </body>
  • 이벤트 전파를 막아주는 함수인 e.stopPropagation() 을 주석처리하여 테스트해보면
    핑크박스가 눌렸을때 바깥쪽에서 안쪽으로 이벤트가 전파되는 “이벤트 캡처링”방식을 가진다.

  • 즉 각 이벤트 리스너에서 ToCapture (세번째 인자 옵션)을 기본값 false로 두면
    ➡️안에서 밖으로 이벤트가 전파되는 "이벤트 버블링" 방식
    ➡️클릭 이벤트가 발생했을 때: .red → .pink → body → html 순서로 전달

  • ToCapture(세번쨰 인자 옵션)true로 바꿔주면
    ➡️밖에서 안으로 이벤트가 전파되는 "이벤트 캡처링" 방식
    ➡️클릭 이벤트가 발생했을 때: html → body → .pink → .red 순서로 전달

  • e.stopPropagation을 특정태그에서 걸어주면 그 태그의 시점에서 이벤트 전파가 멈춘다.

  • 이벤트 캡처링의 예시이다. 핑크박스 → body box → html box로 이벤트가 전파되고 있다.

🚀실습 - 행복지수와 불쾌지수

<body>
  <button id="button-a">행복지수 증가</button>
  <button id="button-b">불쾌지수 감소</button>

  <h1>행복지수 : [ <span id="counter-a">0</span> ]%</h1>
  <h1>불쾌지수 : [ <span id="counter-b">0</span> ]%</h1>

  <script>
    let count1 = 0;
    let count2 = 0;
    function increaseCounter() {
      count1++;
      document.body.querySelector("#counter-a").innerHTML = count1.toString();
    }

    function decreaseCounter() {
      count2--;
      document.body.querySelector("#counter-b").innerHTML = count2.toString();
    }

    const btn = document.querySelector("#button-a");
    btn.addEventListener("click", increaseCounter);

    const btn2 = document.querySelector("#button-b");
    btn2.addEventListener("click", decreaseCounter);
  </script>
</body>

  • 각각 increaseCounter()와 decreaseCounter() 함수로 구현하고
  • querySelector를 통해 <span>태그들을 찾은 후 innerHTML로 값을 변경해준다.
  • 값을 변경할때는 count1, count2값이 정수이기때문에 toString()함수를 통해 문자열로 바꿔주어도되고,
    그대로 숫자로 출력하고싶으면 count1, count2만 출력해주어도 된다.

  • 실행과정
    ➡️ “행복지수 증가” 버튼 선택(#button-a) 후 클릭 → btn 이벤트 발생 → increaseCounter 이벤트 핸들러 실행
    → count값 증가 후 span 태그 문자 수정

    ➡️ “불쾌지수 증가” 버튼 선택(#button-b) 후 클릭 → btn 이벤트 발생 → decreaseCounter 이벤트 핸들러 실행
    → count값 감소 후 span 태그 문자 수정

이벤트가 발생한 객체 가져오기 (target)

  • 함수를 호출할때 이벤트가 발생했던 객체를 첫번째 인자로 넣어서 보내준다.
    ➡️(const increaseFunc = (event) => …) : 주로 event, e 이름으로 쓰인다.
    ➡️(const increaseFunc = () => …)  : 이렇게  받지 않을 수도 있다. 
<script>
    const increaseFunc = (e) => {
        console.log(e.target.id)
    }
</script>
  • e (event 발생할때 발생하는 객체) 를 통해 화살표함수를 만든다.
<script>
    const increaseFunc = function(e){
        console.log(this.id);
    }
</script>
  • 일반함수로 정의되었을 경우에는 this키워드로 이 객체(Object)에 접근할 수 있을 것이지만
    화살표함수로 정의되었을 경우의 this는 Window객체를 가리키기때문에

  • 화살표함수에서의 this 사용은 적합하지 않은 방법이다.
if(e.target.id == "button-a"){
	counterA.innerHTML = Number(counterA.innerHTML) + 1;
else {
	counterB.innerHTML = Number(counterB.innerHTML) + 1;
  • event발생 객체를 가져와서 id값을 탐색하고 ‘button-a’인 버튼의 경우에는
  • counterA span태그의 innerHTML의 문자를 바꿔주는데
    ➡️Number()를 이용하여 +1을 해주는 방법
    ➡️toString() : (정수 → 문자열) 형변환 방법
    ➡️parseInt() : (문자열 → 정수) 형변환 방법 등을 다양하게 사용해볼 수 있다.

🚀실습 - 강우 확률 프로그램

  • 클릭 이벤트 발생 시 어떤 버튼이 클릭되었는지 알기 위해 event.target 활용
    ➡️counter(event) 함수에서 클릭 이벤트가 발생한 버튼을 event.target으로 식별
    ➡️클릭된 버튼의 id 값을 기준으로 조건 분기. ex. event.target.id
<body>
  <h1>비 올 확률 : [ <span id="rainfall">0</span> ]%</h1>
  <button id="button-a">비 올 확률 증가</button>
  <button id="button-b">비 올 확률 감소</button>

  <script>
    let count = 0;
    function counter(event) {
      const clickedButtonId = event.target.id; // 클릭된 버튼 ID 확인 가능

      if (clickedButtonId === "button-a") {
        count++;
      } else if (clickedButtonId === "button-b") {
        count--;
      }
      document.body.querySelector("#rainfall").innerHTML = count.toString();
    }
    const btn = document.querySelector("#button-a");
    btn.addEventListener("click", counter);

    const btn2 = document.querySelector("#button-b");
    btn2.addEventListener("click", counter);
  </script>
</body>

증가 버튼 클릭 / 감소 버튼 클릭 2개의 실행결과

  • <span>태그를 두 개로 나눠서 행복지수, 불쾌지수를 출력했던 부분을 수정하여
  • 결과를 가져와 출력할 <span>태그를 하나로 만들었다.
  • 버튼 별로 다른 기능을 구현하여 이 <span>태그의 텍스트 값을 바꾸는 실습을 해볼 수 있었다.

form태그

 

▶️실습 - 회원가입 폼 만들기

  • <input> 태그의 속성 중 value=”기본값” 설정
    ➡️input 폼 안에 “기본값”이 미리 작성되어있는 상태로 확인 가능
    ➡️이를 활용하여 input요소.value 로 input태그에 입력된 값이 무엇인지 찾아낼 수 있다.

  • 요구사항 :
    ➡️이름이 입력되지 않았다면 “이름을 입력하세요” 메시지 출력
    ➡️비밀번호가 입력되지 않았거나 (비밀번호와 비밀번호확인)의 입력값이 다르다면
    "알맞은 비밀번호를 입력하세요" 출력

  • 이벤트는 submit의 이벤트이름으로 들어올 것이다 (속성 eventName 부분에 속성 "submit" 을 인자로 전달)
<form action="08.html" id="myForm">
  <label for="name">이름</label>
  <input type="text" name="name" id="name" /><br />
  
  <label for="password">비밀번호</label>
  <input type="password" name="password" id="password" /><br />

  <label for="checkPassword">비밀번호 확인</label>
  <input type="password" name="checkPassword" id="checkPassword" /><br />

  <input type="submit" value="등록" />     
</form>
  • form action=”08.html” :
    ➡️ form태그로 action속성을 활용하여 이동할 페이지를 지정할 수 있음

  • <label for=name”>이름</label>
    ➡️for속성으로 밑의 input태그와 연결됨

  • <input type=”text” name=”name” id=”name” /><br/>
    ➡️브라우저의 서버쪽에서는 이 name이라는 속성이 중요하다 이 속성을 통해서 값을 꺼내게 된다.

  • <input type=”submit” value=”등록”/>
    ➡️타입으로써 submit을 지정하면 이 것이 눌렸을때 form에서 정의했던 action 경로로 이동하게된다.
<script>
  const inputName = document.getElementById("name");
  const inputPw = document.getElementById("password");
  const inputCheckPw = document.getElementById("checkPassword");
  const form = document.getElementById("myForm");

  function validation(event) {
    event.preventDefault();

    const nameValue = inputName.value.trim();
    const pwValue = inputPw.value.trim();
    const checkPwValue = inputCheckPw.value.trim();

    if (nameValue === "") {
      alert("이름을 입력하세요!");
      return;
    }

    if (pwValue === "") {
      alert("비밀번호를 입력하세요!");
      return;
    }

    if (pwValue !== checkPwValue) {
      alert("비밀번호 확인이 일치하지 않습니다.");
      return;
    }

    alert("정상적으로 회원가입 되었습니다!");
    form.submit();
  }

  form.addEventListener("submit", validation);
</script>
  • getElementById(”name”)
    ➡️inputName 값만으로 비교로직을 수행한다면 DOM객체가 비교되기때문에
    input 안에 입력된 “값”들이 비교되진 않을 것이다.
    ➡️해결방법 : .value 로 값을 꺼내주어야한다.

  • const nameValue = inputName.value.trim();
    ➡️.value : DOM객체에 입력된 값을 꺼낸다.
    ➡️trim() 함수 : 이 함수를 이용하여 입력된 값의 “공백”을 제거한 후 변수에 담는다.
    trim()함수는 앞뒤의 공백을 제거해주는 기능을 한다. ex. (”공백 비밀번호입니다.”) (”공백공백 비밀번호입니다.”)
    이 둘은 다른 값으로 취급되어야하므로 공백을 제거하여 유효한 값만 비교할 수 있도록 도와준다.

  • event.preventDefault();
    ➡️이 기능은 기본 동작인 페이지 이동을 막음
    ➡️유효성을 검사하여 제대로 입력되지 않으면 페이지 이동을 막기 위해서 사용하는 문법

  • form.submit() :
    ➡️ 전체적인 유효성 검사를 마치고 정상적으로 확인이 되었다면 form에 submit()으로 전달
if (nameValue === "") {
  alert("이름을 입력하세요!");
  nameValue.focus(); // 커서를 이 태그에 둘 수 있게함
  return;
}

if (pwValue === "") {
  alert("비밀번호를 입력하세요!");
  pwValue.focus(); // 커서를 이 태그에 둘 수 있게함
  return;
}

if (pwValue !== checkPwValue) {
  alert("비밀번호 확인이 일치하지 않습니다.");
  checkPwValue.focus(); // 커서를 이 태그에 둘 수 있게함
  return;
}
  • focus() :
    ➡️원하는 요소에 커서를 위치시킬 수 있도록 함
    ➡️유효성 검사가 수행되지 않은 부분에다가 커서를 두어 그 부분부터 입력을 다시 할 수 있도록 유도할 수 있는 기능


🚀실습 - Todo List 

<body>
    <ul id="todoList">
      <li>할일1</li>
      <li>할일2</li>
      <li>할일3</li>
      <li>할일4</li>
      <li>할일5</li>
    </ul>

    <label for="todo">오늘의 할 일</label>
    <input type="text" name="todo" id="todo" />
    <button id="uploadTodo">업로드</button>

    <script>
      // 1. 할일들을 클릭했을때 alert로 할일1 처럼 출력
      // 2. 입력을 했을때 할일들의 맨 밑에 계속 추가되는 것 구현
      const parent = document.getElementById("todoList");
      const uploadBtn = document.getElementById("uploadTodo");

      function createTodo() {
        const myTodo = document.getElementById("todo").value.trim();
        const addList = document.createElement("li");
        addList.innerHTML = myTodo;
        parent.appendChild(addList);
      }

      // 1. 할일 클릭 시 할일 alert 출력
      parent.addEventListener("click", (e) => {
        alert(e.target.innerHTML);
      });

      // 2. 할 일 입력 시 할 일 리스트의 밑으로 계속 추가
      uploadBtn.addEventListener("click", createTodo);
    </script>
  </body>
  • const parent :
    ➡️<li> 태그들의 부모역할을 하는 <ul>태그를 가져와 직관적으로 보기 위해 parent라는 변수명사용

  • const uploadBtn :
    ➡️업로드 버튼을 눌렀을 경우의 이벤트를 작성하기 위해 객체를 가져옴

  • const myTodo :
    ➡️”todo”라는 input 태그를 가져와서 value.trim()으로 그 안에 있는 입력 값을 공백을 제거해 저장

  • const addList = document.createElement(”li”) :
    ➡️<li> 태그를 하나 만들어냄

  • addList.innerHTML = myTodo;
    ➡️방금 추가한 <li> 태그의 텍스트를 innerHTML을 이용하여
    input태그로부터 가져온 입력값이 담긴 myTodo로 갱신해줌

  • parent.appendChild(addList);
    ➡️수정한 <li>태그를 부모역할의 <ul>태그의 자식태그로 추가함

  • alert(e.target.innerHTML);
    ➡️<ul>안에 <li>들이 속해있으므로 <ul>의 id값을 통해 만들어낸 parent변수에 이벤트를 추가함
    그 이벤트로 클릭이 되었을때 alert() 알림을 띄운다.
    ➡️e.target : 이벤트가 발생한 부분의 객체를 받아오고
    ➡️innerHTML : 그 객체의 텍스트 부분을 alert()로 띄우게된다.

  • 💡이벤트 버블링을 이용하면 이러한 계층구조에서 이벤트 전파를 쉽게 구현할 수 있다.

 

만약 부모역할의 <ul> 태그에 이벤트를 걸지 않고
자식태그인 <li> 태그들에 직접 이벤트를 생성한다면

// 1. 할일 클릭 시 할일 alert 출력 (li태그에 직접 구현)
const list = document.getElementsByTagName("li");
for (let i = 0; i < list.length; i++) {
  list[i].addEventListener("click", (e) => {
    alert(e.target.innerText);
  });
}
  • 새롭게 추가하는 <li>태그들에게는 이벤트가 할당되지 않는다.
  • 기존의 <li>태그들의 length 만큼만 수행하였기때문이다.
  • 따라서 이벤트를 걸어주는 주체를 <li>에 걸지 않고 상위 요소인 <ul>태그에 걸어주는 것이다.

실행결과


🚀 이벤트 처리를 배우면서 원하는 기능들이 직관적으로 보이다보니 재미를 느낄 수 있었다.

회고를 통해 이벤트 버블링과 캡처링을 좀 더 이해할 수 있었고 

이벤트를 처리할때 화살표함수에서의 this 사용 금지, focus()를 통해 포커스를 가져가는 부분 등에서

디테일한 부분을 많이 느낄 수 있었다.