[JavaScript] Controlling event

Jingi·2024년 4월 22일

Web

목록 보기
28/40
post-thumbnail

이벤트

일상속의 이벤트

  • 컴퓨터 키보드를 눌러 텍스트를 입력하는 것
  • 전화벨이 울려 전화가 왔음을 알리는 것
  • 손을 흔들어 인사하는 것
  • 전화기의 버튼을 눌러서 통화를 시작하는 것

웹에서의 이벤트

  • 화면을 스크롤하는 것
  • 버튼을 클릭했을 때 팝업 창이 출력되는 것
  • 마우스 커서의 위치에 따라 드래그 앤 드롭하는 것
  • 사용자의 키보드 입력 값에 따라 새로운 요소를 생성하는 것

event 객체

event

  • 무언가 일어났다는 신호, 사건
    • 모든 DOM 요소는 이러한 event를 만들어 냄

event object

  • DOM에서 이벤트가 발생했을 때 생성되는 객체
  • 이벤트 종류
    • mouse, input, keyboard, touch
  • DOM 요소는 event를 받고 받은 event를 처리 할 수 있음

event handler

  • 이벤트가 발생했을 때 실행 되는 함수
    • 사용자의 행동에 어떻게 반응할지를 JavaScript 코드로 표현

.addEventListener()

  • 대표적인 이벤트 핸들러 중 하나
    • 특정 이벤트를 DOM 요소가 수신할 때마다 콜백 함수를 호출
  • EventTarget.addEventListener(type, handler)
  • DOM요소 , 수신할 이벤트, 콜백할 함수
  • 대상에 특정 Event가 발생하면, 지정한 이벤트를 받아 할 일을 등록한다

addEventListener의 인자

  • type
    • 수신할 이벤트 이름
    • 문자열로 작성 (ex. 'click')
  • handler
    • 발생한 이벤트 객체를 수신하는 콜백 함수
    • 콜백 함수는 발생한 event object를 유일한 매개변수로 받음

addEventListener 활용

  • 버튼을 클릭하면 버튼 요소 출력하기
    • 버튼에 이벤트 처리기를 부착하여 클릭 이벤트가 발생하면 이벤트가 발생한 버튼정보를 출력
  • 요소에 addEventListener를 부착하게 되면 내부의 this 값은 대상 요소를 가리키게 됨 (event 객체의 currentTarget 속성 값과 동일)
<button id ='btn'>버튼</button>
// 1. 버튼 선택
const btn = document.querySelector('#btn')

// 2. 콜백 함수
const detectClick = function (event){
    console.log(event) // PointerEvent
    console.log(event.currentTarget) // <button id = "btn">버튼</button>
    console.log(this) // <button id ="btn">버튼</button>
}

// 3.버튼에 이벤트 핸들러를 부착
btn.addEventListener('click', detectClick)

addEventListener의 콜백 함수 특징

  • 발생한 이벤트를 나타내는 event 객체를 유일한 매개변수로 받음
  • 반환 값 없음
const btn = document.querySelector('#btn')

const detectClick = function(event){
    console.log(event)
    console.log(event.currentTarget)
    console.log(this)

}

btn.addEventListener('click', detectClick)

버블링

버블링 개요

  • form > div > p 형태의 중첩된 구조에 각각 이벤트 핸들러가 있을 때 만약

    요소를 클릭하면 어떻게 될까?

    <form id = "form">
        form
        <div id = "div">
            div
            <p id = "p">p</p>
        </div>
    </form>
    const formElement = document.querySelector('#form')
    const divElement = document.querySelector('#div')
    const pElement = document.querySelector('#p')
    
    const clickHandler1 = function (event){
        console.log('form이 클릭되었습니다.')
    }
    const clickHandler2 = function (event){
        console.log('div이 클릭되었습니다.')
    }
    const clickHandler3 = function (event){
        console.log('p이 클릭되었습니다.')
    }
    
    formElement.addEventListener('click', clickHandler1)
    divElement.addEventListener('click', clickHandler2)
    pElement.addEventListener('click', clickHandler3)
  • 요소만 클릭했는데도 불구하고 모든 핸들러가 동작함 - 왜 p를 클릭했는데 div와 form에 할당된 핸들러까지 동작할까?

버블링

  • 한 요소에 이벤트가 발새하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작하는 현상
  • 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작
    • 이벤트가 제일 깊은 곳에 있는 요소에서 시작해 부모 요소를 거슬로 올라가며 발생하는 것이 마치 물속 거품과 닮았기 때문
    • 가장 안쪽의 <p> 요소를 클릭하면 p -> div -> form 순서로 3개의 이벤트 핸들러가 모두 동작했던 것

이벤트가 정확히 어디서 발생했는지 접근할 수 있는 방법

  • event.currentTarget
  • event.target

'currentTarget' & 'target' 속성

  • 'currentTarget' 속성
    • '현재' 요소
    • 항상 이벤트 핸들러가 연결된 요소만을 참조하는 속성
    • 'this'와 같음
  • 'target' 속성
    • 이벤트가 발생한 가장 안쪽의 요소(target)를 참조하는 속성
    • 실제 이벤트가 시작된 요소
    • 버블링이 진행 되어도 변하지 않음

'target' & 'currentTarget' 예시

  • 세 요소 중 가장 최상위 요소인 outerouter 요소에만 핸들러가 부착

  • 각 요소를 클릭 했을 때 event의 target과 currentTarget의 차이 비교

  • 'currentTarget'

    • 핸들러가 연결된 outerouter 요소만을 가리킴
  • 'target'

    • 실제 이벤트가 발생하는 요소를 가리킴
  • 핸들러는 outerouter에만 할당되어 있지만 하위 요소 outer와 inner를 클릭해도 해당 핸들러가 동작함

  • 클릭 이벤트가 어디서 발생했든 상관없이 outerouter까지 이벤트가 버블링 되어 핸들러를 실행시키기 때문

  <style>
    #outerouter {
      width: 300px;
      height: 300px;
      background-color: yellowgreen;
    }

    #outer {
      width: 200px;
      height: 200px;
      background-color: crimson;
    }

    #inner {
      width: 100px;
      height: 100px;
      background-color: skyblue;
    }
  </style>
  <div id="outerouter">
  outerouter
  <div id="outer">
    outer
    <div id="inner">inner</div>
  </div>
</div>

<script>
  const outerOuterElement = document.querySelector('#outerouter')

  const clickHandler = function (event) {
    console.log('currentTarget:', event.currentTarget.id)
    console.log('target:', event.target.id)
  }

  outerOuterElement.addEventListener('click', clickHandler)
</script>

캡처링

  • 이벤트가 하위 요소 전파되는 단계 (버블링과 반대)

캡처링과 버블링

  • table 안에 td를 클릭하면 이벤트는 최상위 요소부터 아래로 전파
  • 실제 이벤트가 발생한 지점(event.target)에서 실행된 후 다시 위로 전파
    • 이 과정에서 상위 요소에 할당된 이벤트 핸들러가 호출
  • 캡처링은 실제 다루는 경우가 거의 없으므로 버블링에 집중

버블링이 필요한 이유

<div>
    <button></button>
    <button></button>
    <button></button>
    ...
    <button></button>
    <button></button>
</div>
  • 각 버튼의 조상인 div 요소에 이벤트 핸들러 단 하나만 할당하기
  • 요소의 공통 조상에 비벤트 핸들러를 단 하나만 할당하면 여러 요소를 한꺼번에 다룰 수 있음
  • 공통 조상에 할당한 핸들러에서 event.target을 이용하면 실제 어떤 버튼에서 이벤트가 발생했는지 알 수 있기 때문
const divTag = document.querySelector('div')
divTag.addEventListener('click', function (event) {
      console.log(event.target)
    })

event handler 활용 실습

  • 버튼을 클릭하면 숫자를 1씩 증가해서 출력하기
  • 사용자의 입력 값을 실시간으로 출력하기
  • 사용자의 입력 값을 실시간으로 출력
    • '+' 버튼을 클릭하면 출력한 값의 CSS 스타일을 변경하기
  • todo 프로그램 구현
  • 로또 번호 생성기 구현

click Event

  <button id="btn">버튼</button>
  <p>클릭횟수 : <span id="counter">0</span></p>

  <script>
    // 1. 초기 값 할당
    let counterNumber = 0

    // 2. 버튼 요소 선택
    const btn = document.querySelector('#btn')

    // 3. 콜백 함수 (버튼에 클릭 이벤트가 발생할 때마다 실행할 코드)
    const clickHandler = function(){
      // 3.1 초기값 += 1
      counterNumber += 1

      // 3.2 p 요소를 선택
      const spanTag = document.querySelector('#counter')
      // 3.3 p 요소의 컨텐츠를 1 증가한 초기 값으로 설정
      spanTag.textContent = counterNumber
    }

    // 4. 버튼에 이벤트 핸들러 부착 (클릭 이벤트)
    btn.addEventListener('click', clickHandler)
  </script>

input Event

  • 사용자의 입력 값을 실시간으로 출력하기
  <input type="text" id="text-input">
  <p></p>

  <script>
    // 1. input 요소 선택
    const inputTag = document.querySelector('#text-input')
    
    // 2. p 요소 선택
    const pTag = document.querySelector('p')

    // 3. 콜백 함수(input 요소에 input 이벤트가 발생할 때마다 실행할 코드)
    const inputHandler = function(event){
    // 3.1 작성하는 데이터가 어디에 누적되고 있는지 찾기
    // console.log(event.currentTarget)
    console.log(event.currentTarget.value)

    // 3.2 p 요소의 컨텐츠에 작성하는 데이터를 추가
    pTag.textContent = event.currentTarget.value
    }

    // 4. input 요소에 이벤트 핸들러 부착 (input 이벤트)
    inputTag.addEventListener('input', inputHandler)

  </script>

currnetTarget 주의사항

  • console.log()로 event 객체를 출력할 경우 currentTarget 키의 값은 null을 가짐
  • currentTarget은 이벤트가 처리되는 동안에만 사용할 수 있기 때문
  • 대신 console.log(event.currentTarget)를 사용하여 콘솔에서 확인 가능
  • currentTarget 이후의 속성 값들은 target을 참고해서 사용하기

Click & input 이벤트 실습

  • 사용자의 입력 값을 실시간으로 출력
    '+' 버튼을 클릭하면 출력한 값의 CSS 스타일을 변경하기
  <style>
    .blue {
      color: blue;
    }
  </style>
</head>

<body>
  <h1></h1>
  <button id="btn">클릭</button>
  <input type="text" id="text-input">

  <script>
    const inputTag = document.querySelector('#text-input')
    const h1Tag = document.querySelector('h1')

    const inputHandler = function (event){
      h1Tag.textContent = event.currentTarget.value
    }

    inputTag.addEventListener('input', inputHandler)

    // click 구현
    const btn = document.querySelector('#btn')

    const clickHandler = function (){
      //  1. add 방법
      h1Tag.classList.add('blue')

      // 2. toggle 방법
      h1Tag.classList.toggle('blue')

      // 3. if 방법
      if (h1Tag.classList.value){
        h1Tag.classList.remove('blue')
      } else{
        h1Tag.classList.add('blue')
      }
    }

    btn.addEventListener('click', clickHandler)
  </script>

todo 실습

  <input type="text" class="input-text">
  <button id="btn">+</button>
  <ul></ul>

  <script>
    const inputTag = document.querySelector('.input-text')
    const btn = document.querySelector('#btn')
    const ulTag = document.querySelector('ul')

    const addTodo = function (event) {
      //  2.1 사용자 입력 데이터 저장
      const inputData = inputTag.value
      
      // 2.2 데이터를 저장할 li 요소를 생성
      const liTag = document.createElement('li')

      // 2.3 li 요소 컨텐츠에 데이터 입력
      liTag.textContent = inputData

      // 2.4 li 요소를 부모 ul 요소의 자식 요소로 추가
      ulTag.appendChild(liTag)

      // todo 추가 후 input의 입력 데이터는 초기화
      inputTag.value = ""
    }
  </script>
  • 빈 문자열 입력 방지
  • 입력이 없을 경우 경고 대화상자를 띄움
    const addTodo = function (event) {
      //  2.1 사용자 입력 데이터 저장
      const inputData = inputTag.value
      
      if (inputData.trim()){
      // 2.2 데이터를 저장할 li 요소를 생성
      const liTag = document.createElement('li')

      // 2.3 li 요소 컨텐츠에 데이터 입력
      liTag.textContent = inputData

      // 2.4 li 요소를 부모 ul 요소의 자식 요소로 추가
      ulTag.appendChild(liTag)

      // todo 추가 후 input의 입력 데이터는 초기화
      inputTag.value = ""
    }}
    else{
        alert('할 일을 입력하세요')
    }
  </script>

lodash

  • 모듈성, 성능 및 구하 기능을 제공하는 JavaScript 유틸리티 라이브러리
  • array,object 등 자료구조를 다룰 때 사용하는 유용하고 감편한 함수들을 제공

이벤트 기반 동작 최소하기

  • HTML의 각 요소가 기본적으로 가지고 았는 이벤트가 때로는 방해가 되는 경우가 있어 이벤트의 기본 동작을 취소할 필요가 있음
  • 예시
    • form 요소의 제출 이벤트를 취소하여 페이지 새로고침을 막을 수 있음
    • a 요소를 클릭 할 때 페이지 이동을 막고 추가 로직을 수행할 수 있음

.preventDefault()

  • 해당 이벤트에 대한 기본 동작을 실행하지 않도록 지정

이벤트 동작 취소 실습

  • copy 이벤트 동작 취소
    • 콘텐츠를 복사 하는 것을 방지
<h1>중요한 내용 </h1>

<script>
    const h1Tag = document.querySelector('h1')

    h1Tag.addEventListener('copy', function (event) {
        console.log(event)
        event.preventDefault()
        alert('복사 할 수 없습니다.')
    })
</script>
  • form 제출 시 페이지 새로고침 동작 취소
    • form 요소의 submit 동작을 취소시킴
  <form id="my-form">
    <input type="text" name="username">
    <button type="submit">Submit</button>
  </form>

  <script>
    const formTag = document.querySelector('#my-form')

    const handleSubmit = function (event){
      event.preventDefault()
    }

    formTag.addEventListener('submit', handleSubmit)
    </script>
profile
데이터 분석에서 백엔드까지...

0개의 댓글