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`를 출력해 보니 아래와 같이 값이 출력되었다.
- 0: li.menu
- 1: li.menu.active (활성화(클릭)된 메뉴)
- 2: li.menu
- 3: li.menu
- 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(): 특정 문자를 반복하는 함수