티스토리 뷰
모달은 단순히 띄우는 것보다 **"어떻게 닫느냐"**에 대한 로직 설계가 더 중요합니다.
보통 모달은 세 가지 방법으로 닫히게 설계하죠.
- 닫기(X) 버튼 클릭
- 배경(Dimmed 영역) 클릭
- ESC 키 누르기
로직 설계 (이벤트 위임)
여기서 퍼블리셔가 자바스크립트를 직접 짤 때 가장 많이 실수하는 부분이 **'배경 클릭 시 닫기'**입니다. 모달의 배경을 클릭하면 닫혀야 하지만, 모달 내부의 **'하얀 박스(컨텐츠)'**를 클릭했을 때는 닫히면 안 되기 때문입니다.
여기서 질문! 모달 배경(modal-overlay)에 클릭 이벤트를 걸었을 때, 그 안에 있는 컨텐츠 박스를 눌러도 이벤트가 부모로 전달되어 모달이 닫혀버리는 현상이 발생합니다.
이처럼 **"자식 요소에서 발생한 이벤트가 부모 요소로 전달되는 현상"**을 무엇이라고 부를까요? (힌트: 거품이 올라가는 모습과 비슷합니다. ) 이 용어를 알면 이벤트 제어가 훨씬 쉬워집니다.
그 현상은 바로 이벤트 버블링(Event Bubbling) 🫧이라고 합니다. 마치 물속의 거품이 수면으로 올라오듯, 자식 요소에서 발생한 클릭 이벤트가 부모 요소로 계속해서 전달되는 성질을 말해요.
모달 배경을 눌렀을 때만 팝업이 닫히게 하려면, 이 '거품'이 위로 올라가지 않도록 중간에 터뜨려줘야 합니다. 이때 사용하는 아주 중요한 자바스크립트 명령어가 있어요.
그 명령어는 **event.stopPropagation()**입니다. 말 그대로 이벤트의 전파(propagation)를 멈추게(stop) 하는 역할이죠.
모달 닫기 로직 적용해보기
자, 이제 이 지식을 활용해 모달의 '하얀 박스(컨텐츠)' 부분을 클릭했을 때는 팝업이 닫히지 않도록 방어막을 쳐볼까요?
const modalViewBtn = document.querySelector('.btn-modal-view');
const modalOverlay = document.querySelector('.modal-overlay'); // 검은 배경
const modalContent = document.querySelector('.modal-content'); // 하얀 박스
// 0. 모달보기 버튼 클릭시
modalViewBtn.addEventListener('click', () => {
// 클래스를 추가할 때
modalContent.classList.add('active');
modalOverlay.classList.add('active');
});
// 1. 배경을 클릭하면 모달을 닫습니다.
modalOverlay.addEventListener('click', () => {
modalOverlay.classList.remove('active');
});
// 2. 컨텐츠 박스를 클릭했을 때...
modalContent.addEventListener('click', (event) => {
// 클릭 이벤트가 부모(배경)로 퍼지는 것을 막습니다.
event.stopPropagation();
// 이제 컨텐츠를 클릭해도 팝업이 닫히지 않습니다!
console.log("컨텐츠 클릭! 배경으로 이벤트가 전달되지 않아요.");
});
event.stopPropagation()이 바로 그 '거품(버블링)'을 터뜨려주는 핵심 열쇠입니다.
순수 자바스크립트에서 event.stopPropagation()은 독립적인 명령어로 사용되며, 특정 요소를 다시 찾을 필요 없이 그 자리에서 전파를 즉시 중단시킵니다.
함수로 정의하고 호출하기
/* 모달: 속성 변경 및 전파 방지 */
function openModal(modal, targetImg, src) {
targetImg.setAttribute('src', src); // 이미지 경로 교체
modal.classList.add('active'); // 모달 열기
}
모달 호출 (속성값 활용)
모달은 클릭한 이미지의 src 값을 가져와서 함수로 쏴주는 과정이 필요합니다.
const galleryImgs = document.querySelectorAll('.gallery-item img');
const modal = document.querySelector('.modal-overlay');
const modalImg = document.querySelector('.modal-full-img');
galleryImgs.forEach(img => {
img.addEventListener('click', () => {
// 1. 클릭한 이미지의 src 주소를 따옵니다.
const currentSrc = img.getAttribute('src');
// 2. 함수를 호출하며 (모달창, 모달안의이미지태그, 주소)를 전달!
openModal(modal, modalImg, currentSrc);
});
});
// [추가] 모달 배경 클릭 시 닫기
modal.addEventListener('click', () => {
modal.classList.remove('active');
});
// [추가] 버블링 방지 (컨텐츠 박스 클릭 시 안 닫히게)
const modalContent = document.querySelector('.modal-content');
modalContent.addEventListener('click', (e) => {
e.stopPropagation();
});
💡 실무 팁: this를 쓸 때 주의할 점
함수 안에서 this를 쓰고 싶다면, addEventListener의 콜백 함수를 화살표 함수(() => {})가 아닌 **일반 함수(function() {})**로 써야 합니다.
- 화살표 함수: this가 내가 클릭한 버튼이 아닐 수 있음.
- 일반 함수: this가 정확히 클릭된 그 요소를 가리킴.
※ 해당 내용은 Google Gmini3.0과 함께 공부한 내용입니다.
'WEB > JavaScript' 카테고리의 다른 글
| [JavaScript] 자주하는 질문 FAQ 아코디언 메뉴 (0) | 2026.01.03 |
|---|---|
| [JavaScript] 탭메뉴, 아코디언, 모달 (0) | 2026.01.03 |
| [JavaScript] 아코디언 코드 (0) | 2026.01.03 |
| [JavaScript] 공지사항 더보기 데이터 가져오기 (0) | 2026.01.03 |
| [JavaScript] 스크롤에 따른 이벤트 (0) | 2026.01.03 |
- Total
- Today
- Yesterday
- sw기술자평균임금 #2025년 sw기술자 평균임금
- 광주분식 #광주분식맛집 #상추튀김 #상추튀김맛집 #광주상추튀김
- 연명의료결정제도 #사전연명의료의향서 #사전연명의료의향서등록기관 #광주사전연명의료의향서
- 와이파이증폭기추천 #와이파이설치
- thymeleaf
- 자바스크립트 #javascript #math
- 파비콘 #파비콘 사이트에 적용
- 좋은책 #밥프록터 #부의원리
- jQuery #jQuery이미지슬라이드 #이미지슬라이드
- 바지락칼국수 #월곡동칼국수 #칼국수맛집
- 파비콘사이즈
- 증폭기 #아이피타임증폭기
- iptime와이파이증폭기 #와이파이증폭기설치
- jdk #jre
- 쇼팬하우어 #좋은책
- 정보처리기사 #정보처리기사요약 #정보처리기사요점정리
- ajax
- 썬크림 #닥터지썬크림 #내돈내산 #내돈내산썬크림 #썬크림추천 #spf50썬크림 #닥터지메디유브이울트라선
- lg그램pro #lg그램 #노트북 #노트북추천 #lg노트북
- 테스크탑무선랜카드 #무선랜카드 #아이피타이무선랜카드 #a3000mini #무선랜카드추천
- 자바스크립트정규표현식
- echart
- 탭메뉴자바스크립트
- css미디어쿼리 #미디어쿼리 #mediaquery
- SQL명령어 #SQL
- 무료폰트 #무료웹폰트 #한수원한돋움 #한수원한울림 #한울림체 #한돋움체
- 좋은책
- 자바스크립트break
- // 사진직: 데이터가 없으면 DEFAULT_IMG 사용 const profileSrc = (d.img && d.img !== "") ? d.img : DEFAULT_IMG;('#user-photo').attr('src'
- 자바스크립트countiue
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |

