TIL : 250401화 (:active, 즐겨찾기 기능 css 변경 진행중-event.target, menuRefs, white-space, 프로그래머스 문자열 내림차순으로 배치하기)

2025. 4. 1. 23:12·Developer/TIL
반응형

1. `:active`

`:active` CSS 의사 클래스는 사용자가 활성화한 요소(버튼 등)를 나타낸다.

예를 들어 마우스를 사용하는 경우, "활성"이란 보통 마우스 버튼을 누르는 순간부터 떼는 시점 까지를 의미한다.

/* 활성화된 모든 <a> 태그를 선택 */
a:active {
  color: red;
}

주로 아래와 같은 요소에서 사용된다.

  • <a>: 링크
  • <button>: 버튼
  • <label>: 폼 요소와 연결된 라벨
  • <input>, <textarea>: 입력 필드

즉, 사용자가 클릭하거나 포커스를 줄 수 있는 요소들에 자주 사용된다.

 

🚧 주의할 점

`:active`는 다른 링크 관련 의사 클래스에 덮어씌워질 수 있다.

링크에 스타일을 적용할 때는 `:link`, `:visited`, `:hover`, `:active` 순서를 지켜야 한다.(LVHA 순서)

a:link { color: blue; }
a:visited { color: purple; }
a:hover { color: green; }
a:active { color: red; } /* 이건 맨 마지막에 있어야 작동함! */

 

만약 :active가 앞에 있고, :hover가 뒤에 있다면?

👉 사용자가 클릭하는 순간 :active가 적용돼도, 바로 :hover가 덮어버린다.

(출처: Mdn `:active` 문서)

 

 

 

웹사이트 즐겨찾기 기능 로직 수정

선임분의 요청에 따라 사이드바의 메뉴에 hover 시, `⋮` 버튼 생성 -> 버튼 클릭 시 옵션 리스트가 나타나고, 그 옵션 리스트 중 즐겨찾기 기능이 포함되도록 수정

 

1. 메뉴 외의 다른 곳을 클릭시, 메뉴가 닫히게 하는 기능 추가 (eventListener 이용)

 

1) sidebar 전체에 ref="menuRefs"를 거는 것만으로는 메뉴 각각의 옵션 리스트를 불러올 수 없는 에러 발생.

해결: ref="(el) => menuRefs[index] = el" 사용!

 

🎯 왜 ref="menuRefs" 대신 ref="(el) => menuRefs[index] = el"를 사용해야 할까?

❌ ref="menuRefs"

  • Vue는 배열을 자동으로 채우지 않음 → `menuRefs.value`는 `{}`(빈 객체)로 남아 있음.
  • ` menuRefs.value.some(menu => menu?.contains(event.target)) ` 코드 진행 시, `menuRefs.value`가 비어 있어서 `contains()`를 호출할 수 없음.

✅ ref="(el) => menuRefs[index] = el"

  • `el`(각 <li> 요소)을 직접 `menuRefs[index]`에 할당 → 배열이 정상적으로 채워짐.
  • `menuRefs.value`가 `[{ li1 }, { li2 }, { li3 }, ...]` 이런 형태가 됨.
  • 이제 `menuRefs.value.some(menu => menu?.contains(event.target))`가 정상적으로 동작함

✅ 결론

✔ `ref="menuRefs"`는 `v-for`에서 자동으로 배열을 채우지 않음 → {}(빈 객체).
✔ `ref="(el) => menuRefs[index] = el"`로 직접 할당해야 함.
✔ 이 방법을 쓰면 `menuRefs.value`가 `<li>` 요소들을 올바르게 참조할 수 있음.

 

 

 

위와 같이 코드를 짜고 메뉴 하나를 클릭한 뒤,`menuRefs.value`를 출력해 보니 아래와 같이 값이 출력되었다.

  1. 0: li.menu
  2. 1: li.menu.active (활성화(클릭)된 메뉴)
  3. 2: li.menu
  4. 3: li.menu
  5. 4: li.menu

 

 

2) event.target에 대해 공부하기

    const closeMenuOnClickOutside = (event) => {
      console.log("Clicked outside the menu", event.target);
      console.log("Menu refs", menuRefs.value);
      if (!menuRefs.value.some((menu) => menu === event.target)) {
        console.log("Clicked outside the menu");
        openedMenuIndex.value = null;
      }
    };

 

`event.target`은 사용자가 클릭한 실제 HTML 요소 (DOM 요소) 를 반환한다.
즉, 어디를 클릭했는지에 대한 정보를 담고 있다.

 

📌 예제: 클릭 위치에 따른 `event.target` 값

<ul>
  <li class="menu-item">Menu 1</li>
  <li class="menu-item">Menu 2</li>
  <li class="menu-item">Menu 3</li>
</ul>
<button class="hover-menu">⋮</button>
document.addEventListener("click", (event) => {
  console.log(event.target);
});

👀 클릭 위치에 따른 `event.target` 값

클릭한 요소 ` event.target` 값
"Menu 1" ` <li class="menu-item">Menu 1</li>`
"⋮" 버튼 ` <button class="hover-menu">⋮</button>`
빈 공간 (body) ` <body>...</body>`

 

 

🔥 내가 작성한 코드에서 `event.target` 값

const closeMenuOnClickOutside = (event) => {
  console.log("Clicked outside the menu", event.target);
};
  • 🔹 메뉴 아이템을 클릭하면 → `event.target`은 `<li>` 요소.
  • 🔹 점 세 개 버튼을 클릭하면 → `event.target`은 `<button class="hover-menu">⋮</button>`.
  • 🔹 빈 공간을 클릭하면 → `event.target`은 `<body>` 또는 `<div>` 같은 컨테이너.

👉 즉, event.target은 사용자가 클릭한 요소를 그대로 반환!

 

 

3) `!menuRefs.value.some(menu => menu?.contains(event.target))`로는 원하는 동작(외부 클릭 시 메뉴창 닫힘)을 하는데, `!menuRefs.value.some(menu => menu === event.target)`로는 원하는 동작을 하지 않는 현상 발생

✅ `menuRefs.value.some(menu => menu === event.target)`는 왜 안 되나?

if (!menuRefs.value.some((menu) => menu === event.target)) { 
  openedMenuIndex.value = null;
}

📌 여기서 문제가 발생하는 이유

  • `menuRefs.value`에는 `<li>` 요소들이 저장됨.
  • `event.target`은 사용자가 클릭한 정확한 요소인데, `menuRefs.value`와 완전히 일치하는 요소가 아닐 수도 있음.
    • 예를 들어, `<li>` 안의 `<button>`을 클릭하면 `event.target`은` <button>`이지만, `menuRefs.value`에는 `<li>`만 있음.
    • 그러면 `menuRefs.value.some(menu => menu === event.target)`가 false가 되고, 메뉴가 닫혀버림.

 

🎯 해결 방법 → `contains()` 사용!

const closeMenuOnClickOutside = (event) => {
  if (!menuRefs.value.some(menu => menu?.contains(event.target))) {
    openedMenuIndex.value = null;
  }
};

✅ contains(event.target)가 하는 일

  • `menu.contains(event.target)`은 menu 안에 `event.target`이 있는지 확인함.
  • 즉, 메뉴 내부의 버튼을 클릭해도 true를 반환해서 메뉴가 안 닫힘.

 

🔥 결론

✔ `event.target`은 사용자가 클릭한 실제 HTML 요소.
✔ `menuRefs.value.some(menu => menu === event.target)` → 내부 버튼 클릭 시 실패함.
✔` menu.contains(event.target)`를 사용해야 버튼 클릭 시에도 닫히지 않음.

 

 

 

위처럼 코드를 짜니 여러 개의 메뉴창이 동시에 열릴 수가 있었다.

하나의 창이 켜지면 나머지 창은 닫히게 만들고 싶어서, 아래와 같이 메뉴창이 하나만 열릴 수 있도록 코드를 수정했다. 

   const closeMenuOnClickOutside = (event) => {
      if (!menuRefs.value[openedMenuIndex.value]?.contains(event.target)) {
        openedMenuIndex.value = null;
      }
    };

 

 

 

웹 사이트 스타일링 에러 변경 요청 들어와서 수정함

`white-space: nowrap`을 이용해 텍스트 overflow 시 줄바꿈되며 스타일링이 깨지던 문제를 수정했다.

https://developer.mozilla.org/ko/docs/Web/CSS/white-space

 

white-space - CSS: Cascading Style Sheets | MDN

CSS white-space 속성은 요소가 공백 문자를 처리하는 법을 지정합니다.

developer.mozilla.org

 

 

 

👨‍💻 [프로그래머스] 문자열 내림차순으로 배치하기

function solution(s) {
    return s.split('').sort().reverse().join('');
}

reverse(): 배열을 뒤집는 함수

 

 

 

👨‍💻 [프로그래머스] 핸드폰 번호 가리기

function solution(phone_number) {
    return '*'.repeat(phone_number.length - 4) + phone_number.slice(-4);
}

repeat(): 특정 문자를 반복하는 함수

반응형
저작자표시 비영리 변경금지 (새창열림)

'Developer > TIL' 카테고리의 다른 글

TIL : 250404금 (프로그래머스 다트 게임, Git 원격 브랜치 코드 로컬 브랜치에 적용하기, 자바스크립트 소수점 자르기 방법, 디버거 사용법, JS 딕셔너리)  (0) 2025.04.04
TIL : 250403목 (프로그래머스 크레인 인형뽑기 게임, debugger의 중요성...)  (0) 2025.04.04
TIL : 250402수 (falsy값 처리(?., ??, || 언제 써야 할까?), setTimeout 0ms 비동기 처리, 프로그래머스 완주하지 못한 선수, for...in vs for...of 차이점 정리, 프로그래머스 실패율)  (0) 2025.04.02
TIL : 250331월 (프로그래머스 해시, 윤년, 같은 숫자는 싫어, 문자열 내 마음대로 정렬하기(sort()함수에 대해 알아보기))  (1) 2025.03.31
TIL : 250328금 (메뉴 검색창 만들기, 즐겨찾기 기능 구현, 프로그래머스 유연근무제)  (0) 2025.03.29
[TIL] 250327목 (isNaN, Number.isNaN / split, splice, slice / reduce / map vs reduce)  (1) 2025.03.27
'Developer/TIL' 카테고리의 다른 글
  • TIL : 250403목 (프로그래머스 크레인 인형뽑기 게임, debugger의 중요성...)
  • TIL : 250402수 (falsy값 처리(?., ??, || 언제 써야 할까?), setTimeout 0ms 비동기 처리, 프로그래머스 완주하지 못한 선수, for...in vs for...of 차이점 정리, 프로그래머스 실패율)
  • TIL : 250331월 (프로그래머스 해시, 윤년, 같은 숫자는 싫어, 문자열 내 마음대로 정렬하기(sort()함수에 대해 알아보기))
  • TIL : 250328금 (메뉴 검색창 만들기, 즐겨찾기 기능 구현, 프로그래머스 유연근무제)
청량리 물냉면
청량리 물냉면
프로그래밍 공부를 하고 있습니다. 공부 내용 정리 겸 정보 공유를 목적으로 합니다.
    반응형
  • 청량리 물냉면
    노력중인 블로그
    청량리 물냉면
  • 전체
    오늘
    어제
    • 분류 전체보기 (506)
      • 프로그래밍 (41)
        • Programming (1)
        • C | C++ (6)
        • Java (28)
        • Python (5)
      • 웹 프로그래밍 (2)
        • HTML | CSS (5)
        • JavaScript | TypeScript (41)
        • React (25)
        • Vue.js (0)
        • Next.js (18)
        • Spring & Spring Boot (13)
        • JSP & Servlet (1)
        • DB (4)
      • 웹 프로젝트 (77)
        • 웹 프로젝트 (22)
        • 🥨스낵몰 (3)
        • 👨‍👨‍👧‍👧소셜 가계부 (26)
        • 🌜꿈 일기장 (11)
        • 🔮포트폴리오 사이트 (11)
        • 🏃‍♂️팀 프로젝트: 일정관리 프로그램 (0)
        • 📈팀 프로젝트: AI기반 주식 분석 플랫폼 (0)
        • 😺Just Meow It: 조언 사이트 (2)
        • 📕Workly: 교대근무 다이어리 (1)
      • 앱 프로그래밍 (26)
        • Flutter (24)
        • Kotlin (2)
      • Problem Solving (166)
        • 백준 (52)
        • 프로그래머스 (79)
        • SWEA (29)
      • Computer Science (40)
        • 알고리즘 (14)
        • 컴퓨터 네트워크 (18)
        • 이산수학 (8)
      • Developer (47)
        • 후기 (4)
        • 자료정리 (4)
        • 취업 | 취준 (9)
        • SSAFY (1)
        • 웹개발 교육 프로그램 (9)
        • TIL (20)
  • 블로그 메뉴

    • 홈
    • Github
  • 공지사항

    • 프로그래밍 공부 중😊
  • 인기 글

  • 태그

    프로젝트
    spring boot
    파이썬
    백준
    알고리즘
    ZeroCho
    자바스크립트
    블로그 제작
    SWEA
    강의내용정리
    bfs
    구현
    프로그래머스
    포트폴리오
    플러터
    Til
    공식문서
    자바
    mysql
    컴퓨터네트워크
    d3
    타입스크립트
    Next.js
    Jiraynor Programming
    클론 프로젝트
    AWS
    React
    뉴렉처
    리액트
    웹사이트
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
청량리 물냉면
TIL : 250401화 (:active, 즐겨찾기 기능 css 변경 진행중-event.target, menuRefs, white-space, 프로그래머스 문자열 내림차순으로 배치하기)
상단으로

티스토리툴바