해당 코드에서는 backdrop을 눌러도 card를 눌러도 card가 닫히는 현상이 생김
<template>
<button @click="toggleCard" class="toggleBtn">toggle</button>
<!-- <div :class="{ 'backdrop': isShown }"> vue 동적 클래스 바인딩을 안쓰고 -->
<div class="backdrop" @click="toggleCard">
<div class="card" v-if="isShown">
<label>REPORTS</label>
<ul>
<li>
<span class="icon"><i class="fa-solid fa-clock"></i></span>
Real-Time
</li>
<li>
<span class="icon"><i class="fas fa-user"></i></span>
Audience
</li>
<li>
<span class="icon"><i class="fa-solid fa-flag"></i></span>
Conversions
</li>
</ul>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue'
const isShown = ref(false);
const toggleCard = () => {
const backdrop = document.querySelector('.backdrop');
isShown.value = !isShown.value;
backdrop.style.display = isShown.value? 'block' : 'none';
}
</script>
<style>
.toggleBtn{
padding: 10px;
border: 1px solid;
border-radius: 5px;
background: white;
}
.backdrop {
display: none; /* vue 동적 클래스 바인딩 안 쓸 시 추가 */
position: fixed; /* 화면 전체에 고정 */
z-index: 10;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7); /* 반투명 회색 */
}
.card {
/* position: relative; */
z-index: 20; /* backdrop 위에 위치 */
width: 300px;
padding: 15px 0 10px;
margin: 150px auto;
border: 1px solid gainsboro;
border-radius: 5px;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3);
background: white; /* 카드 배경색 설정 */
}
.card > label {
display: block; /* label을 블록 레벨 요소로 만들어 margin 적용 가능하게 함 */
margin-bottom: 10px;
padding: 0 20px;
color: gray;
font-weight: 400;
text-align: left;
}
.card > ul {
padding-left: 0; /* 왼쪽 패딩 제거 */
list-style-type: none; /* 리스트 스타일 제거 */
text-align: left;
}
.card > ul > li {
padding: 0.25rem 20px; /* 각 항목 사이의 간격 조정 */
font-weight: 500;
cursor: pointer;
}
.card > ul > li:hover {
background-color: #f0f0f0;
}
.card > ul > li > .icon {
display: inline-block;
width: 34px;
}
.card > ul > li > .icon > i{
color:gray;
}
</style>
stopPropagation 는 상위 엘리먼트들로의 이벤트 전파를 중단시킨다.
https://pa-pico.tistory.com/20
[개념잡기] e.preventDefault() 와 stopPropagation() 의 차이
stopPropogation vs preventDefault e.preventDefault()와 e.stopPropagation()의 차이 두개의 코드 모두 이벤트 관련 동작에서 많이 사용되는 코드이다. 둘의 차이점은 무엇일까 알아보자. e.preventDefault() html 에서 a
pa-pico.tistory.com
문제의 원인은 `<div class="backdrop">` 요소에 클릭 이벤트를 할당하여 `toggleCard` 함수를 호출했기 때문입니다. 이 `<div>`는 `card`를 포함하고 있으므로 `card`를 클릭할 때도 `backdrop`의 이벤트가 실행되어 토글 기능이 작동하게 됩니다. 이는 이벤트 버블링 때문에 발생하는 현상으로, 클릭 이벤트가 자식 요소에서 부모 요소로 전파됩니다.
### 해결 방안: 이벤트 버블링 중지
`card` 요소 내부의 클릭 이벤트가 `backdrop`으로 전파되지 않도록 이벤트 버블링을 중지해야 합니다. 이를 위해 `card` 클릭 이벤트 핸들러에서 `event.stopPropagation()` 메서드를 사용할 수 있습니다. `stopPropagation()` 메서드는 이벤트가 더 이상 전파되지 않도록 막아 줍니다.
```vue
<template>
<button @click="toggleCard" class="toggleBtn">toggle</button>
<div class="backdrop" @click="toggleCard">
<div class="card" v-if="isShown" @click.stop="preventClose">
<label>REPORTS</label>
<ul>
<li>
<span class="icon"><i class="fa-solid fa-clock"></i></span>
Real-Time
</li>
<li>
<span class="icon"><i class="fas fa-user"></i></span>
Audience
</li>
<li>
<span class="icon"><i class="fa-solid fa-flag"></i></span>
Conversions
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const isShown = ref(false);
const toggleCard = () => {
const backdrop = document.querySelector('.backdrop');
isShown.value = !isShown.value;
backdrop.style.display = isShown.value ? 'block' : 'none';
}
const preventClose = (event) => {
event.stopPropagation(); // 이벤트 버블링을 막아 card 클릭 시 닫히지 않도록 함
}
</script>
```
### 변경된 부분:
- `card` 요소에 `@click.stop="preventClose"`를 추가하여 클릭 이벤트가 부모 요소로 전파되는 것을 막습니다. `.stop`은 Vue에서 이벤트 전파를 자동으로 중지하는 지시자입니다.
- `preventClose` 함수를 추가하여 클릭 이벤트를 처리합니다. 이 함수는 이벤트 객체를 받아 `stopPropagation()`을 호출합니다.
이렇게 하면 `card` 내부에서 일어나는 클릭 이벤트는 그 자리에서 처리되고, `backdrop`으로의 이벤트 전파가 중지되어 `card`를 클릭했을 때 창이 닫히는 문제를 방지할 수 있습니다.
이벤트 객체 사용 이유
- toggleCard 함수: 이 함수는 버튼을 클릭했을 때 호출됩니다. 여기서는 버튼을 클릭할 때마다 카드가 보이거나 숨겨지게 만드는 기능만 수행합니다. 그래서 이벤트의 세부 정보(예: 어디에서 클릭했는지)는 필요하지 않습니다. 단순히 카드의 표시 상태만 바꾸면 되기 때문이죠.
- preventClose 함수: 이 함수는 카드 내부의 클릭을 처리할 때 사용됩니다. 카드 내부의 어떤 부분을 클릭해도 백드롭(즉, 카드 뒤의 어두운 부분) 클릭으로 인식되어 카드가 닫히는 것을 막기 위해 필요합니다. 이 함수는 클릭 이벤트가 카드 외부로 전파되지 않도록 멈추어야 하므로, 이벤트의 전파를 중지하는 stopPropagation() 메서드를 호출하기 위해 이벤트 객체에 접근해야 합니다.
즉, toggleCard 함수는 "카드 보이기/숨기기"만 제어하고, preventClose는 "카드 내부 클릭 시 밖으로 이벤트가 전파되지 않도록" 제어합니다. 이런 차이 때문에 preventClose 함수에서는 이벤트 객체가 필요한 거죠!
'개인공부' 카테고리의 다른 글
| const 정의 못찾아서 function으로 변경 (0) | 2024.07.07 |
|---|---|
| [Vue3.js] 새로운 프로젝트 생성, vite 설치까지 (2) | 2024.06.02 |
| [Vue.js] 동적 클래스 바인딩 (1) | 2024.05.20 |
| [css] position 값 (0) | 2024.05.20 |
| [graphql] gpt 질의응답 및 graphql-db연동의 prisma (1) | 2024.05.17 |