30일차 - javascript (이벤트, 버블링, 캡처링, 이전-다음 이벤트, 썸네일 이벤트)

Yohan·2024년 4월 2일
0

코딩기록

목록 보기
43/156
post-custom-banner

이미지 슬라이드 (이전, 다음 이벤트)

  • 사진에서처럼 다음으로 넘어갈 때마다 그 다음 사진이 나오게, 이전으로 넘어갈 때 마다 전 사진이 나오게 하기 위해서 공식처럼 사용하는 식이 있음
  • 현재인덱스+1을 길이로 나눈 나머지를 다음 index로 설정하여 증가하는 것을 통해 식을 정함
let currentIndex = 0; // 현재 이미지 위치

      $next.addEventListener('click', () => {
        // 현재 내가 보고 있는 이미지가 배열의 0번 인덱스라면
        // 다음버튼을 누르면 1번을 보여줘야 한다.
        currentIndex = (currentIndex + 1) % images.length;
        $img.setAttribute('src', images[currentIndex]);
      });
  • 이전으로 넘어갈 때는 현재인덱스 -1 + 길이를 길이로 나눈 나머지로 설정
$prev.addEventListener('click', () => {

        currentIndex = (currentIndex - 1 + images.length) % images.length;
        $img.setAttribute('src', images[currentIndex]);
      });

2-2. 이벤트 객체와 전파

이벤트 객체

  • 이벤트가 발생하면 이벤트에 대한 다양한 정보가 들어있는 이벤트 객체가 동적으로 생성
    -> 이벤트에 대한 정보는 console.log(e); 를 통해 확인 가능!
  • 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 자동 전달
  • 이벤트 객체 공통 프로퍼티

이벤트 전파

  • 이벤트가 다른 요소에게 전이되는 것을 의미
  • 캡쳐링(capturing): 이벤트가 상위요소 -> 하위요소로 전파
  • 타깃 단계(target phase): 이벤트가 타깃에 도달```
  • 버블링(bubbling): 이벤트가 하위요소 -> 상위요소로 전파
    (자식이 이벤트 걸려있으면 부모를 타고 올라가며 이벤트 전부 발생하게 만듦)

  • apple을 눌렀을 땐 ul도 같이 출력됨 (이벤트 전파)

버블링

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .root {
            width: 300px;
            height: 300px;
            background: red;
        }
        .parent {
            width: 200px;
            height: 200px;
            background: green;
        }
        .child {
            width: 100px;
            height: 100px;
            background: blue;
        }
        .target {
            width: 50px;
            height: 50px;
            background: black;
        }
    </style>
</head>
<body>
    
    <div class="root">
        <div class="parent">
            <div class="child">
                <div class="target"></div>
            </div>
        </div>
    </div>

    <script>

        document.querySelector('body').onclick = e => alert('body');
        document.querySelector('.root').onclick = e => alert('root');
        document.querySelector('.parent').onclick = e => alert('parent');
        document.querySelector('.child').onclick = e => {
          e.stopPropagation();
            alert('child');
        };
        document.querySelector('.target').onclick = e => {
          // 버블링 방지 : 이벤트 전파 중단
          e.stopPropagation();
            alert('target');
        };

    </script>

</body>
</html>

  • 검 < 파 < 초 < 빨 < 흰 순서로 부모, 자식 관계에 있는데 가장 자식인 검은색을 클릭하게되면 target, child, parent, root, body 순서로 나오게됨 (버블링!)
  • stopPropagation() 를 이용하면 버블링을 중단시킬 수 있음!
    -> 중단하게된다면 검은색을 클릭해도 자기자신인 target만 alert됨

이벤트 위임 (부모에 이벤트를 건다)

  • 이벤트를 걸어줄 때 부모에 걸면 자식들은 전염되는 특성 (버블링 특성을 이용)
    -> 이벤트를 부모에 걸어서 간단하게 처리 가능!!!
  • matches() : 조건부로 이벤트를 중단시키는 것 가능
// 태그에서 class=active를 제거하는 함수
const removeActive = () => {
      [...$fruits.children].forEach($li => {
        if ($li.classList.contains('active')) {
          $li.classList.remove('active');
          return;
        }
      });
    };


// 부모에 이벤트
  $fruits.addEventListener('click', e => {

    // 이벤트 중단 : 조건부로 이벤트를 중단시키는 것 (matches 이용)
    // 클릭한 이벤트가 li가 아니라면?
    if (!e.target.matches('#fruits li')) { 
      return; // 중단하겠다.
    }

    // console.log(e.target); // 이벤트가 터진 태그를 알기위함

    // 1. active 클래스 제거
    removeActive();
    // 2. 클릭대상에 active 클래스 부여
    e.target.classList.add('active');

    // 3. 클릭한 태그의 텍스트를 em.msg에 저장

    // e.target.textContent = 클릭한 태그의 텍스트
    document.querySelector('.msg').textContent =  e.target.textContent;
  });


  • 클릭했을 때 클릭한 곳에 색과 밑줄이 생기고 아래에 메시지에 그 과일이 입력되게했음
  • matches()를 통해 조건을 걸어서 li이외의 것을 눌렀을 때 에러가 나는 것을 고침
   $btn.addEventListener('click', () => {
       $fruits.innerHTML += `<li id="${$input1.value}">${$input1.value}</li>`
   });
   });
  • 추가를 눌렀을 때 과일이 추가되는 코드를 작성 (innerHTML 이용), 아래처럼 정석으로 작성할 수도 있다!
      // 1. 추가 버튼을 클릭하면
      // 1-1. 추가 버튼 요소 노드를 가져온다
    //   const $addBtn = document.getElementById('add');
    //   const $input = document.querySelector('.text-box');
    //   // 1-2. 버튼에 클릭 이벤트 부여
    //   $addBtn.addEventListener('click', e => {

    //     // 2. 입력한 텍스트를 읽어서
    //     // 2-1. input태그를 가져옴
    //     const inputText = $input.value;

    //     // 3. LI태그로 만들어서 텍스트를 넣고
    //     // 3-1. LI태그 만들기
    //     const $newLi = document.createElement('li');
    //     // 3-2. 텍스트 넣기
    //     $newLi.textContent = inputText;

    //     // 4. 아이디를 부여 한 후
    //     $newLi.setAttribute('id', inputText);

    //     // 5. UL에 추가한다.
    //     $fruits.appendChild($newLi);

    //     // 6. input 비우기
    //     $input.value = '';
    // })
  • 나는 엔터를 눌렀을 떄도 작성하기를 원했기 때문에 이벤트 정보를 이용해서 작성
  // input에 텍스트를 입력하고 엔터를 누르면 추가 버튼을 클릭하게 함
  // 1. Input에 키보드 이벤트를 건다
  $input1.addEventListener('keyup', e => {
    if (e.key === 'Enter') {
      $btn.click();
    }
  });

캡처링 (별로 안중요)

  • 이벤트 캡처링은 이벤트가 상위 요소에서 하위 요소로 전달되는 과정.
    즉, 최상위 요소에서 시작하여 이벤트의 실제 타깃 요소까지 내려가는 과정
    이벤트 캡처링 단계에서는, DOM 트리의 가장 바깥쪽 요소(예를 들어, document)에서 시작하여 이벤트 타깃에 도달할 때까지 내려감
    캡처링을 사용하려면, addEventListener 함수에 세 번째 인자로 true 값을 주어야 합니다.
element.addEventListener('click', function(event) {
  // 핸들러 로직
}, true); // 캡처링 단계에서 이벤트를 처리하기 위해 true 값을 설정

preventDefault

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <a id="link" href="https://github.com">깃허브로 이동</a>

  <form id="my-form" action="https://www.google.com">
    <input type="text" placeholder="이메일을 쓰세요.">
    <button type="submit">확인</button>
  </form>
  
  <script>
    const $link = document.getElementById('link');

    $link.addEventListener('click', e => {
      if (!confirm('깃허브로 이동합니까??')) {
        e.preventDefault(); // 태그의 기본 자바스크립트 기능 중지
      }
    });

    const $form = document.getElementById('my-form');
    
    $form.addEventListener('submit', e => {
      e.preventDefault(); // 기본 submit기능 해제
      if ($form.firstElementChild.value === '') {
        return;
      }
      console.log('양식을 서버에 제출함');
      $form.submit();
    });
  </script>

</body>
</html>
  • 두 번째 $form 관련 이벤트를 보면 submit을 하기전에 미리 submit 기능을 해제하는 것을 볼 수 있음
    -> 이는 계속 데이터를 전송하거나 막 치는 사람들을 방지하기위함 ! (무엇인가를 입력했을 때만 제출할 수 있게함)

이미지 슬라이드2 (썸네일 이벤트, 썸네일을 눌렀을 때 메인 이미지로 등록되게함)

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        height: 100vh;
        margin: 0;
        background-color: #f5f5f5;
      }

      #slider {
        display: flex;
        align-items: center;
        background-color: white;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        border-radius: 10px;
        overflow: hidden;
      }

      #slider img {
        max-width: 400px;
        max-height: 300px;
        display: block;
        object-fit: cover; /* background-size : cover의 image 버전 */
      }

      button {
        background-color: #007bff;
        color: white;
        border: none;
        padding: 10px 20px;
        font-size: 16px;
        cursor: pointer;
        transition: background-color 0.3s ease;
      }

      button:hover {
        background-color: #0056b3;
      }

      button:focus {
        outline: none;
      }
      #thumbnails {
        display: flex;
        justify-content: center;
        gap: 10px;
      }
      .thumbnail {
        width: 100px;
        height: 75px;
        object-fit: cover;
        cursor: pointer;
        transition: opacity 0.3s ease;
        border-radius: 5px;
      }
      .thumbnail:hover {
        opacity: 0.7;
      }
    </style>
  </head>
  <body>
    <div id="slider">
      <button id="prev">이전</button>
      <img
        id="image"
        src="https://blog.kakaocdn.net/dn/dN4uhe/btr8xWDwbrK/5YjBrelhtJwU2BAcLtEVoK/img.jpg"
        style="width: 400px; height: auto"
      />
      <button id="next">다음</button>
    </div>

    <!-- 모든 이미지의 썸네일을 슬라이더 하단에 배치 -->
    <div id="thumbnails"></div>

    <script>
      // 이미지 경로를 저장할 배열
      const images = [
        "https://blog.kakaocdn.net/dn/dN4uhe/btr8xWDwbrK/5YjBrelhtJwU2BAcLtEVoK/img.jpg",
        "https://img.koreanairdfs.com/upload/prd/542/20190917114040414002.png",
        "https://www.sisajournal.com/news/photo/202108/222572_131065_5150.jpg",
        "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?q=80&w=1000&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxleHBsb3JlLWZlZWR8MXx8fGVufDB8fHx8fA%3D%3D",
      ];

      // 배열의 길이만큼 img태그를 만들어서 #thumbnails에 추가
      const $thumbnailContainer = document.getElementById("thumbnails");
      images.forEach((imgSrc, i) => {
        const $sliderImg = document.createElement("img");
        $sliderImg.setAttribute("src", imgSrc);
        $sliderImg.setAttribute("alt", "iamge${i+1}");
        $sliderImg.classList.add("thumbnail");
        // 데이터속성에 인덱스를 담아서 추가
        $sliderImg.dataset.index = i;

        $thumbnailContainer.appendChild($sliderImg);
        // 이런 코드가 나옴
        // <img src="https://blog.kakaocdn.net/dn/dN4uhe/btr8xWDwbrK/5YjBrelhtJwU2BAcLtEVoK/img.jpg"
        // alt="iamge${i+1}" class="thumbnail" data-index="0">
      });

      
      
      
      
      
      
      
      
      
      
      let currentIndex = 0; // 현재 이미지 위치

      // 이전, 다음 버튼 클릭 이벤트
      const [$prev, $sliderImg, $next] = [
        ...document.getElementById("slider").children,
      ];

      // 이미지와 인덱스를 업데이트하는 함수
      const updateImage = (index) => {
        currentIndex = index;
        $sliderImg.setAttribute("src", images[currentIndex]);
      };

      $next.addEventListener("click", () => {
        updateImage((currentIndex + 1) % images.length);
        // if(currentIndex >= images.length) currentIndex = 0;
        // currentIndex++;
      });

      $prev.addEventListener("click", () => {
        updateImage((currentIndex - 1 + images.length) % images.length);
        // currentIndex--;
        // if (currentIndex < 0) {
        //   currentIndex = images.length - 1;
        // }
      });


    // 이미지 썸네일 컨테이너에 클릭이벤트 바인딩
    $thumbnailContainer.addEventListener('click', e => {
      const clickedIndex = +e.target.dataset.index;
      updateImage(clickedIndex);
    });

    </script>
  </body>
</html>
  1. 이미지들을 썸네일로 넣기 위해서 forEach 반복문을 통해 img태그를 생성해서 src,alt, className, data-index를 넣어서 appendChild를 통해 thumbnails의 자식으로 추가
  2. 이미지와 인덱스를 업데이틑 함수를 작성했다. 썸네일을 눌러 그 사진이 메인이미지로 가는 것까진 성공했지만
    이후에 이전, 다음 버튼을 누르면 인덱스가 적용되지않아서 랜덤으로 사진이 나왔다. 그렇기 때문에 이미지를 업데이트할 때 인덱스도 같이 업데이트하는 함수를 만들어주었다.
  3. 이전, 다음 이벤트에 있던 것들에 이미지와 인덱스를 업데이트하는 함수를 적용
  4. 이미지 썸네일 컨테이너를 클릭했을 때 클릭된 이미지의 인덱스를 이벤트 정보에서 구해서 updateImage 함수를 적용하여 메인이미지에 들어갈 수 있게 이벤트를 적용

  • 두 사진을 수직으로 배치하기 위해 코드를 살펴보았다. 두 코드는 body아래에 위치해있고, body에는 display: flex; 가 걸려있기 때문에 수평으로 되어있는 것을 확인했다. 그래서 body에 flex-direction: column; 을 주어서 수직으로 만들어 줌!
profile
백엔드 개발자
post-custom-banner

0개의 댓글