Как закрыть модальное окно по клику на подложку, но не на содержимое?

Обработчик клика для модального окна на JavaScript: отличаем подложку от содержимого с помощью target и currentTarget, не ломая кнопки внутри окна.

Короткий ответ

Что ответить на собеседовании

Повесьте обработчик click на подложку .modal и вызывайте onClose только при event.target === event.currentTarget. Клик по содержимому всплывает до подложки, но его target остаётся внутренним элементом, поэтому окно не закрывается. Условие подходит, если вся область вне .modal-content принадлежит самой подложке.

Подробный разбор

Ответ с пояснениями

Условие

Есть подложка на весь экран и белое окно размером 300 × 300 пикселей. Вызовите onClose() по клику вне содержимого окна. Кнопка внутри должна продолжать работать без закрытия модального окна.

<div class="modal">
    <div class="modal-content">
        <button type="button">Click on me!</button>
    </div>
</div>

Предполагается, что скрипт выполняется после создания этих элементов.

Решение

const modal = document.querySelector('.modal');

function onClose() {
    console.log('Close modal');
}

function handleBackdropClick(event) {
    if (event.target === event.currentTarget) {
        onClose();
    }
}

modal.addEventListener('click', handleBackdropClick);

target — элемент, на котором возник клик. currentTarget — элемент, чей обработчик сейчас выполняется, то есть .modal. При клике на подложку они совпадают. При клике на кнопку или содержимое target другой, хотя событие всплывает к .modal.

Вызов stopPropagation() внутри окна здесь не нужен: проверка не препятствует другим обработчикам. При удалении компонента можно снять слушатель через modal.removeEventListener('click', handleBackdropClick).

Если разметка сложнее

Когда внутри подложки есть дополнительные декоративные элементы вне .modal-content, сравнение с подложкой их не охватит. Тогда проверяют принадлежность содержимому:

const content = modal.querySelector('.modal-content');
modal.addEventListener('click', (event) => {
    if (event.target instanceof Node && !content.contains(event.target)) {
        onClose();
    }
});

Выберите один из обработчиков, не подключайте оба одновременно. Проверку клика на подложку нельзя считать полной реализацией доступного диалога: управление фокусом, закрытие по Escape и возврат фокуса требуют отдельной обработки. Различие свойств события описано в MDN: currentTarget.

Практика в реальном времени

Подготовьтесь к следующему собеседованию

Interview Boost учитывает вакансию, резюме и технологии и помогает сформулировать ответ прямо во время интервью.

Начать подготовку