[CSS] - 아마존 클론#2

sarang_daddy·2023년 3월 19일
0

CSS

목록 보기
3/6
post-thumbnail

🚫 아마존 웹사이트를 클론해보면서 CSS를 학습하는 내용입니다.
구현은 되었으나 정확한 코드가 아니며 더 좋은 방법은 학습해가며 수정하려고 합니다.

🌱 사전 학습 키워드

커밋을 하는 이유

  • 오류 발생시 당시로 돌아가기 위해
  • 커밋을 모으면 하나의 패치리스트처럼 흐름, 변경 사항 등의 확인이 용이하다.

커밋의 단위

  • 커밋은 작은 단위일수록 찾기도 쉽고 해결도 쉽다.
  • 기능 단위마다 커밋을 한다면 어떤 기능에 대한 분석을 할때 확인이 용이하다.
  • 기능의 구현이 100%로가 아니라도 괜찮지만 작업이 미완성 상태에서의 커밋은 안된다.

template literal 문법

백틱(`)을 사용한 문자열

  • 여러 줄에 걸친 문자열을 쉽게 작성할 수 있다.
  • 문자열 안에 변수나 표현식을 삽입할 수 있다.
  • 태그된 탬플릿 리터럴을 사용하면 문자열을 함수로 전달하거나 가공할 수 있다.

Tagged Template Literals 문법 :: 마이구미 :: 마이구미의 HelloWorld (tistory.com)

insertBefore 메서드

DOM에서 요소를 다른 요소의 앞에 삽입하는 메서드다.
삽입하려는 요소와 삽입할 위치를 인수로 받아오며, 삽입할 위치를 지정할 수 있다.

삽입하고자 하는 부모노드.insertBefore(삽입할 노드, 삽입할 위치(부모 노드의 자식이어야 한다.)

<ul id="myList">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
const myList = document.querySelector("#myList");
const newItem = document.createElement("li");
newItem.textContent = "New Item";
myList.insertBefore(newItem, myList.firstChild);

위 예시에서 newItem이라는 li를 myList 자식 중 첫번째 자식 위에 삽입한다.

이벤트 버블링

이벤트 버블링은 특정 화면 요소에서 이벤트가 발생했을 때 해당 이벤트가 더 상위의 화면 요소들로 전달되어 가는 특성을 말한다.

const divs = document.querySelectorAll("div");

divs.forEach(function (div) {
  div.addEventListener("click", logEvent);
});

function logEvent(event) {
  console.log(event.currentTarget.className); // three
}

위 예제 코드처럼 one div > two div > three div 트리 구조를 가지고 모든 div에 클린 이벤트를 주었을 때,

three만 클릭을 해도 이벤트는 3 → 2 → 1 순으로 발생한다.
이것을 이벤트 버블링 이라 한다.

이벤트 캡쳐

이벤트 캡쳐는 이벤트 버블링과 반대 방향으로 진행되는 이벤트 전파 방식이다.

// 이벤트 캡쳐링
const divs = document.querySelectorAll("div");

divs.forEach(function (div) {
  div.addEventListener("click", logEvent, {
    capture: true, // default 값은 false다.
  });
});

function logEvent(event) {
  console.log(event.currentTarget.className);
}

three를 클릭시 1 → 2 → 3 순으로 이벤트가 전파 된다.

event.stopPropagation()

이벤트 버블링과 캡쳐를 막기위한 메서드다.

const divs = document.querySelectorAll("div");

divs.forEach(function (div) {
  div.addEventListener("click", logEvent);
});

function logEvent(event) {
  event.stopPropagation();
  console.log(event.currentTarget.className);
}

3번을 클릭하면 3번에서만 이벤트가 발생한다.

이벤트 위임(Event Delegation)

자식 요소가 많은 상위 요소에서 이벤트 핸들링을 하는 기술이다.
각각의 자식 요소에 이벤트 핸들러를 등록하면 코드의 양이 많아지고, 성능 문제를 야기 할 수 있다.
때문에 상위 요소에서 이벤트를 처리하고, 이벤트가 발생한 자식 요소를 식별하는 것이 가능하다.

const container = document.querySelector("#container");

container.addEventListener("click", function (event) {
  // 클릭 이벤트가 발생한 요소 확인
  const clickedElement = event.target;

  // 클릭된 요소가 특정 자식 요소인지 확인
  if (clickedElement.classList.contains("myClass")) {
    // 특정 자식 요소가 클릭되었을 때 실행되는 코드
    // ...
  }
});

🔥 구현내용

✅ 스크롤 안보이게 숨기기

.homeSidebar {
  overflow-y: scroll;
}

.homeSidebar::-webkit-scrollbar {
  display: none;
}

CSS에서 "-webkit-"은 웹킷(WebKit) 기반 브라우저에서만 동작하는 비표준(vendor-prefixed) CSS 속성 접두사(vendor prefix)다.

WebKit은 과거에 Apple 사의 사파리(Safari) 브라우저에서 사용되는 렌더링 엔진이었으며, 현재는 몇몇 브라우저(예: 사파리, 크롬 등)에서 공통으로 사용되는 엔진으로 발전하였다.

WebKit 기반 브라우저에서 사용되는 비표준 CSS 속성 접두사로는 "-webkit-" 이외에도, "-moz-"(Firefox), "-o-"(Opera), "-ms-"(Internet Explorer) 등이 존재한다.

따라서, ".homeSidebar::-webkit-scrollbar"는 웹킷 기반 브라우저에서만 동작하는 스크롤바의 스타일을 지정하는 CSS 선택자로 -webkit기반 브라우저가 아닌 곳에서는 적용이 안될 수 있다.


✅ 확장 리스트 목록 JS로 가져오기

  • HTML 안에 모든 리스트가 하드코딩으로 적혀있던 li들을 제거
  • 웹사이트 실행시 JS에서 리트 항목을 추가 해준다.
/* 생성될 li가 들어갈 ul과 기능 버튼만 남긴다. */
<ul class="homeSidebar__lists hideLists">
  <li class="homeSidebar__mode--close hoverIcon">
    <span>간단히 보기</span>
    <i class="fa-solid fa-chevron-up"></i>
  </li>
</ul>
const sideBarExtend = () => {
  // 확장하면 나오는 리스트 타이틀 항목들
  const DOWN_LIST = [
    "자동차 용품",
    "유아",
    "뷰티 및 퍼스널 케어",
    "여성 패션",
    "남성 패션",
    "여아용 의류",
    "남아용 의류",
    "건강 및 가정용품",
    "가정 및 주방",
    "산업용 및 과학용",
    "여행 가방",
    "영화 및 TV",
  ];

  const appendExtentionList = () => {
    let count = 8;
    DOWN_LIST.forEach((item) => {
      const ul = document.querySelector(".hideLists");
      const li = document.createElement("li");
      const icon = document.createElement("i");
      const closeMore = document.querySelector(".homeSidebar__mode--close");
      li.innerText = item;
      li.classList.add("homeSidebar__subTitle", "hoverIcon");
      li.dataset.columns = count;
      count++;
      icon.classList.add("fa-solid", "fa-chevron-right");
      ul.insertBefore(li, closeMore);
      li.appendChild(icon);
    });
  };
};

👍  JS로 HTML 요소를 만들어줌으로서 오는 장점

  1. HTML코드가 간결해진다.
  2. 요소가 추가, 삭제, 수정 될 때, DATA파일만 수정해주면 된다.

✅ 데이터 속성 dataset 사용하여 li에 고유값 부여하기

어떤 리스트를 클릭 했을 때 해당 리스트의 내용을 가진 레이아웃으로 전환하기 위해서
각 리스트 항목에 data-columns 값을 부여 해준다.

<li class="homeSidebar__subTitle hoverIcon" data-columns="1">
  <span>Amazon Music</span>
  <i class="fa-solid fa-chevron-right"></i>
</li>

데이터 속성 참고 자료 - mdn
데이터 속성 dataset 사용하기 (ft. JS/CSS 활용)
How to Use HTML5 Data Attributes


✅ 이벤트 위임

  • 사이드바 세부페이지로 넘어가기 위해서 모든 li에 이벤트를 주는것은 비효율적이다.
  • li의 상위 요소 UL에 클릭 이벤트를 부여하고 이벤트를 위임한다.
const sideBar = document.querySelector(".homeSidebar");
const sidebarLists = document.querySelector(".homeSidebar__lists");

const selectListInSidebar = (event) => {
  // li전체, li이름, li버튼 어디를 클릭해도 이벤트 타겟은 li가 되도록 해준다.
  const liEventTarget = event.target.closest("li");

  // 이벤트 타겟이 li가 아닌 경우는 예외처리 해준다.
  if (!liEventTarget) return;

  // 클릭된 li의 고유번호 data-columns를 찾는다.
  const columnNumber = liEventTarget.dataset.columns;

  // 클릭된 li의 고유번호를 가진 세부항목 ul을 찾는다.
  const selector = `ul[data-columns="${columnNumber}"]`;
  const element = sideBar.querySelector(selector);

  // 고유번호를 가진 ul이 아닌 경우 예외처리
  if (!element) return;

  const elementColumns = element.getAttribute("data-columns");

  // 세부항목 ul중에서 클릭된 li의 columns과 같은 ul을 찾는다.
  if (columnNumber === elementColumns) {
    element.classList.add("show", "showSidebarDetailAnimation");
    sidebarLists.classList.add("none");
  }
};

// 사이드바 에서 리스트들을 가지고 있는 ul에 이벤트를 부여한다. (이베
sidebarLists.addEventListener("click", selectListInSidebar);
  • closest()메서드는 주어진 CSS 선택자와 일치하는 요소를 찾을 때까지, 자기 자신을 포함해 위쪽(부모 방향, 문서 루트까지)으로 문서 트리를 순회한다.

closest() 참고 링크


✅  이벤트 예외처리

addEventListener로 클릭 이벤트를 등록하여 이벤트 타겟(클릭된 요소)를 사용하던 중,
사용되지 않는 요소를 클릭시 이벤트 타겟 값이 Null이되는 경우 에러가 발생한다.

이를 해결하기 위해 이벤트 타겟으로 정의되지 않은 요소가 클릭될 시 return 해주는 예외 처리가 필요하다.

const sidebar = document.querySelector(".homeSidebar");
const sidebarlists = document.querySelector(".homeSidebar__lists");

const selectListInSidebarDeail = (event) => {
  const thisElement = event.target.closest("ul");
  const liEventTarget = event.target.closest("li");

  // liEventTarget가 타겟 된다면 실행, 아니면 return
  if (liEventTarget) {
    const backButtons = document.querySelectorAll(".homeSidebar__mode--back");
    const selected = [...backButtons].find((button) => {
      return button.contains(event.target);
    });

    // 타겟된 liEventTarget가 selected라면 실행, 아니면 return
    if (selected && liEventTarget.className === selected.className) {
      sidebarlists.classList.add("hideSidebarDetailAnimation");
      sidebarlists.classList.remove("none");
      thisElement.classList.remove("show", "showSidebarDetailAnimation");
      thisElement.classList.remove("hideSidebarDetailAnimation");
    }
  }
};

sidebar.addEventListener("click", selectListInSidebarDeail);

🛠️ 더 생각해보기

  • parsing, 렌더링을 고려하면서 구현해보기
  • transition과 애니메이션의 차이점 생각해보기
  • transition을 사용하면 좋은점 생각해보기
profile
한 발자국, 한 걸음 느리더라도 하루하루 발전하는 삶을 살자.

0개의 댓글