모달창 클릭 이벤트 전파 막기

Janet·2024년 2월 27일
0

JavaScript

목록 보기
26/26
post-thumbnail

Intro

모달 구현 시 모달창 내부에 '닫기'버튼을 통해 닫을 수 있지만, 모달창 외부의 배경을 클릭 시 모달을 닫는 경우도 있습니다.
후자의 경우 모달창 외부 요소(Overlay, Container)에 모달을 닫기 위한 로직을 삽입하게 되는데 이 로직을 삽입하게 되면, 하위 요소(모달창)를 클릭 시에도 모달이 계속 닫히게 됩니다. 이를 막기 위해서는 모달창을 클릭 시 이벤트 버블링 단계에서 상위 요소로 이벤트가 전파되는 것을 막아야만 합니다.

❓ 이벤트 전파(이벤트 버블링과 이벤크 캡쳐링)

이벤트 전파(Event Propagation)란, HTML은 계층적으로 이루어져 있는데 HTML 요소에 이벤트가 발생할 경우 연쇄적 이벤트 흐름이 일어나게 되는 현상을 말합니다.

이벤트 버블링(Event Bubbling)란, 자식(하위) 요소에서 발생한 이벤트가 부모(상위) 요소로 전파되는 것을 말합니다. (버블링이 기본값입니다.)

이벤트 캡쳐링(Event Capturing)이란, 버블링과 반대로 자식(하위) 요소에서 발생한 이벤트가 부모(상위) 요소로부터 시작하여 자식(하위) 요소까지 도달하는 것을 말합니다.

DOM 이벤트의 전파 방향에는 세 가지가 있습니다. 캡처링 단계, 대상 단계, 버블링 단계입니다.

이벤트가 발생하면 먼저 캡쳐링 단계로 시작하여 최상위 요소에서부터 이벤트를 발생시킨 요소를 향해 내려가고, 그 후 대상 요소에서 이벤트가 발생하며 마지막으로 버블링 단계로 올라갑니다. 각 단계에서 이벤트 핸들러가 설정되어 있다면 실행됩니다.

대부분의 이벤트는 버블링 단계에서 처리됩니다. 하지만 이벤트 핸들러의 세 번째 인자로 useCapturetrue로 설정하여 캡처링 단계에서 이벤트를 처리할 수도 있습니다. (기본값은 false로 버블링이 기본값입니다.)

📌 모달 기능 구현 조건

  1. 모달창 내부에 위치한 < 버튼 혹은 모달창 바깥의 배경을 클릭했을 때만 모달창이 닫히도록 할 것.
  2. 위 1번 요소들을 제외한, 모달창 내부 요소들을 클릭했을 때는 모달창이 닫히지 않도록 할 것.

💡 해결 방법

모달창 요소에서 클릭 이벤트가 발생했을때 그 이벤트가 상위로 전달되지 않아야만 모달창 하위 요소들을 클릭해도 모달창이 닫히지 않게 됩니다. 즉 모달창 요소에서 상위(부모) 요소까지 이벤트 버블링되는 것을 막아야 합니다. 이를 위해 모달창 요소에event.stopPropagation()을 호출하여 상위 요소로 이벤트가 전파되는 것을 막을 수 있습니다.

event.stopPropagation(): 이 메서드를 통해 현재 이벤트가 캡처링/버블링 단계에서 더 이상 전파되지 않도록 방지할 수 있습니다.

✅ 예시 코드(컴포넌트와 스타일 코드)

// OrderCompletionModal.tsx

import Image from 'next/image';
import * as Styled from './OrderCompletionModal.styles';
import { prevArrowIcon } from '@/public/svgs';

type Props = {
  onClose: React.MouseEventHandler;
  orderNumber: string;
};

function OrderCompletionModal({ onClose, orderNumber }: Props) {
  return (
    <Styled.ModalContainer onClick={onClose}>
      <Styled.ModalContent onClick={(e) => e.stopPropagation()}>
        <Image src={prevArrowIcon} alt="prevArrowIcon" onClick={onClose} />
        <ul>
          <li>예약이 완료되었습니다!</li>
          <li>예약번호 {orderNumber}</li>
          <li>예약이 완료되었습니다.</li>
          <li>예약 내역은 [마이페이지 - 예약 내역 확인]에서 다시 확인할 수 있습니다.</li>
        </ul>
      </Styled.ModalContent>
    </Styled.ModalContainer>
  );
}
export default OrderCompletionModal;
// OrderCompletionModal.styles.ts

import styled from 'styled-components';
import Theme from '@/styles/theme';

export const ModalContainer = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
`;

export const ModalContent = styled.div`
  background: white;
  padding: 20px;
  border-radius: 15px;
  img {
    width: 13px;
    height: 13px;
  }
  ul {
    padding: 20px;
    text-align: center;
  }
`;

Reference.

profile
😸

0개의 댓글