Повесьте обработчик click на подложку .modal и вызывайте onClose только при event.target === event.currentTarget. Клик по содержимому всплывает до подложки, но его target остаётся внутренним элементом, поэтому окно не закрывается. Условие подходит, если вся область вне .modal-content принадлежит самой подложке.
Как закрыть модальное окно по клику на подложку, но не на содержимое?
Обработчик клика для модального окна на JavaScript: отличаем подложку от содержимого с помощью target и currentTarget, не ломая кнопки внутри окна.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Есть подложка на весь экран и белое окно размером 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.