Независимо от реализации, задача «закрыть портал» должна быть простая, предсказуемая и доступная для пользователя. Ниже описаны основные подходы к закрытию портала, их преимущества, а также практические примеры.
Что такое портал?
- Модальное окно (modal) – окно, которое блокирует взаимодействие с остальной страницей.
- Тултип, всплывающее меню, слайдер, диалоговое окно – все они могут рассматриваться как порталы.
- Портал обычно создаётся в отдельном контейнере
<div id="portal">, который находится вне родительского DOM‑дерева.
Почему порталы нужны?
- Изолировать контент от основной страницы.
- Управлять фокусом и доступностью.
- Облегчить стилизацию (чистый CSS без конфликтов).
Методы закрытия портала
Кнопка «Закрыть»
Самый очевидный способ – добавить в портал кнопку с атрибутом data-close (или любой другой). При клике на неё выполняется скрипт, который удаляет портал из DOM.
<div id="portal" class="modal">
<button data-close>✖️ Закрыть</button>
<div class="content">…</div>
</div>
JS‑обработчик:
document.body.addEventListener('click', function (e) {
if (e.target.matches('[data-close]')) {
closePortal;
}
});
function closePortal {
const portal = document.getElementById('portal');
if (portal) {
portal.remove; // либо portal.style.display = 'none';
}
}
Клик вне портала (overlay)
Многие модальные окна закрываются, когда пользователь кликает по «тёмной» маске за пределами окна. Событие click можно обрабатывать на самом overlay‑контейнере.
<div id="overlay" class="overlay">
<div id="portal" class="modal">…</div>
</div>
document.getElementById('overlay').addEventListener('click', function (e) {
if (e.target === this) {
closePortal;
}
}); Клавиша Esc
Для удобства необходимо поддерживать закрытие портала по клавише Escape. Это делается через обработчик события keydown.
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
closePortal;
}
}); Закрытие через скрипт (программно)
Иногда портал нужно закрыть из другого компонента или в ответ на событие, не связанное с UI. Для этого создаём функцию closePortal и вызываем её из любого места.
Использование <dialog>
Нативный элемент <dialog> имеет встроенные методы show, close и событие cancel. Это самый простой способ, но поддержка браузеров ограничена (Safari пока не поддерживает).
<dialog id="myDialog">
<button id="closeBtn">✖️ Закрыть</button>
<div class="content">…</div>
</dialog>
Ниже приведён минимальный пример модального окна, закрываемого кнопкой, кликом по overlay и клавишей Esc. Код полностью автономный и готов к вставке в любой проект.
<html lang=»ru»>
<head>
<meta charset=»UTF-8″>
<meta name=»viewport» content=»width=device-width, initial-scale=1.0″>
<title>Портал – как закрыть?</title>
<style>
.overlay{
position: fixed; top:0; left:0; right:0; bottom:0;
background: rgba(0,0,0,0.6);
display:flex; justify-content:center; align-items:center;
z-index:1000;
}
.modal{
background:#fff; padding:20px; border-radius:8px; min-width:300px; position:relative;
}
.modal button[data-close]{
position:absolute; top:10px; right:10px; background:none; border:none; cursor:pointer; font-size:18px;
}
</style>
</head>
<body>
<button id=»open»>Открыть портал</button>
<div id=»overlay» class=»overlay» style=»display:none;»>
<div class=»modal»>
<button data-close>✖️</button>
<h2>Заголовок портала</h2>
<p>Это пример контента внутри портала. Он можно закрыть несколькими способами.</p>
</div>
</div>
<script>
const overlay = document.getElementById(‘overlay’);
const openBtn = document.getElementById(‘open’);
openBtn.onclick = => overlay.style.display = ‘flex’;
overlay.addEventListener(‘click’, e=>{
if(e.target === overlay) overlay.style.display = ‘none’;
});
overlay.addEventListener(‘click’, e=>{
if(e.target.matches(‘[data-close]’)) overlay.style.display = ‘none’;
});
document.addEventListener(‘keydown’, e=>{
if(e.key === ‘Escape’) overlay.style.display = ‘none’;
});
</script>
</body>
Работа с популярными библиотеками
React – react-dom.createPortal
Создаём компонент Modal, который рендерится в #modal-root.
import React from 'react';
import ReactDOM from 'react-dom';
function Modal({children, onClose}) {
return ReactDOM.createPortal(
<div className="overlay" onClick={onClose}>
<div className="modal" onClick={e => e.stopPropagation}>
<button onClick={onClose} aria-label="Закрыть">✖️</button>
{children}
</div>
</div>,
document.getElementById('modal-root')
);
}
Vue – portal-vue
В Vue можно использовать <portal> компонент.
<template>
<portal to="modals">
<div class="modal">
<button @click="close">✖️</button>
<slot />
</div>
</portal>
</template>
Angular – CDK Overlay
Angular CDK предоставляет Overlay сервис. Закрытие реализуется через OverlayRef.close.
import {Overlay, OverlayRef} from '@angular/cdk/overlay';
import {ComponentPortal} from '@angular/cdk/portal';
...
const overlayRef: OverlayRef = this.overlay.create;
overlayRef.attach(new ComponentPortal(ModalComponent));
overlayRef.backdropClick.subscribe( => overlayRef.dispose);
Учитываем доступность (a11y)
- Обеспечить правильный
aria-modal="true"иrole="dialog"на контейнере. - Сохранять фокус внутри портала, возвращать фокус после закрытия.
- Поддерживать закрытие через клавишу Esc и “Backspace” (если применимо).
- Проверять контрастность цветов и размеры кликабельных элементов.
Частые проблемы и решения
- Портал закрывается, но не удаляется из DOM – используйте
removeвместоstyle.display='none', если не нужен повторный рендер. - Клик по кнопке закрытия не реагирует – убедитесь, что событие
stopPropagationне блокирует. - Esc не работает – проверьте, что скрипт слушает событие на
documentи не конфликтует с другими обработчиками. - Портал не закрывается после вызова
closeв<dialog>– убедитесь, что элемент действительно является<dialog>и имеет методclose.
Закрыть портал в теле можно разными способами, но ключевые принципы остаются одинаковыми: упростить пользовательский опыт, обеспечить доступность и предсказуемость поведения.
Выбирайте метод, который лучше всего вписывается в ваш стек технологий, и не забывайте про обработку клавиши Esc и клика вне портала – это стандарт, который ожидают пользователи.
Надеемся, что эта статья поможет вам быстро реализовать закрытие портала в любом проекте!