Check If Clicked Element Is Descendant Close On Click Outside

3 menit baca.

Kamu lagi bangun modal, dropdown, atau menu. Mau tutup kalau user klik di luar. Ini cara bersih untuk melakukannya.

Konsepnya

Pakai Node.contains() untuk cek apakah elemen yang diklik ada di dalam elemen target kamu. Kalau tidak — berarti user klik di luar.

Implementasi dasar

import { useEffect, useRef } from 'react';

function Modal({ isOpen, onClose, children }) {
  const modalRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    function handleClickOutside(event: MouseEvent) {
      if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
        onClose();
      }
    }

    if (isOpen) {
      document.addEventListener('mousedown', handleClickOutside);
    }

    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return (
    <div ref={modalRef} className="modal">
      {children}
    </div>
  );
}

Cara kerjanya

  1. modalRef.current menunjuk ke elemen modal kamu
  2. event.target adalah apa yang di-klik user
  3. contains() return true kalau elemen yang diklik ada di dalam modal
  4. Kalau return false — klik di luar, jadi tutup modalnya

Handle device touch

Tambah touchstart untuk mobile:

useEffect(() => {
  function handleClickOutside(event: MouseEvent | TouchEvent) {
    if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
      onClose();
    }
  }

  if (isOpen) {
    document.addEventListener('mousedown', handleClickOutside);
    document.addEventListener('touchstart', handleClickOutside);
  }

  return () => {
    document.removeEventListener('mousedown', handleClickOutside);
    document.removeEventListener('touchstart', handleClickOutside);
  };
}, [isOpen, onClose]);

Jangan close saat klik di dalam

Check !modalRef.current.contains(event.target) sudah handle ini — klik di dalam modal diabaikan.

Selesai. Satu ref, satu useEffect, dan modal kamu tutup kalau user klik di mana pun di luar.

Latest Posts