Check If Clicked Element Is Descendant Close On Click Outside

3 min read.

You're building a modal, dropdown, or menu. You want it to close when users click outside of it. Here's the clean way to do it.

The concept

Use Node.contains() to check if the clicked element is inside your target element. If it's not — the user clicked outside.

Basic implementation

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>
  );
}

How it works

  1. modalRef.current points to your modal element
  2. event.target is what the user clicked
  3. contains() returns true if the clicked element is inside the modal
  4. If it returns false — click was outside, so close the modal

Handle touch devices

Add touchstart for 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]);

Don't close when clicking inside

The !modalRef.current.contains(event.target) check already handles this — clicks inside the modal are ignored.

That's it. One ref, one useEffect, and your modal closes when users click anywhere outside.

Latest Posts