> Как реализовать показ попапа через 3 секунды после захода на сайт с возможностью закрытия и запоминанием закрытия между сессиями? (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Tilda

Стек: JavaScript

> Пример ответа

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

Попап показываем через 3 секунды после загрузки страницы с помощью setTimeout. Для запоминания закрытия между сессиями используем localStorage - при закрытии попапа сохраняем флаг popupClosed: true с временной меткой. При повторном визите проверяем наличие флага и, если он есть и не истёк (например, 24 часа), не показываем попап. Закрытие реализуем через обработчик клика на кнопку или оверлей.

Подробное объяснение

Основная задача - показать попап с задержкой, не блокируя взаимодействие пользователя с сайтом, и запомнить факт закрытия на клиентской стороне. Ключевые моменты:

  • Таймер: setTimeout с 3000 мс запускается после события DOMContentLoaded или load. Важно очищать таймер при закрытии попапа, чтобы избежать утечек памяти.
  • Хранение состояния: localStorage подходит для простого флага, но нужно учитывать, что пользователь может очистить хранилище. Для более надёжного решения можно добавить временную метку и проверять актуальность (например, показывать попап раз в сутки).
  • Закрытие: обработчик на кнопку закрытия и/или клик вне попапа (overlay). После закрытия устанавливаем флаг в localStorage и очищаем таймер.
  • Сброс при повторном визите: при загрузке страницы проверяем localStorage. Если флаг есть и время не истекло - не показываем. Если истекло - удаляем флаг и показываем снова.

На практике

В реальном проекте стоит добавить:

  • Анимацию появления (CSS transition или animation) для плавного UX.
  • Обработку случая, когда пользователь быстро закрывает страницу до срабатывания таймера - таймер нужно очищать в beforeunload.
  • Возможность показать попап только на определённых страницах (через URL-паттерн).
  • Для A/B-тестов или персонализации - хранить не просто флаг, а объект с версией попапа и временем показа.

Пример кода

JAVASCRIPT
const POPUP_DELAY = 3000;
const STORAGE_KEY = 'popup_closed';
const EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 часа
function initPopup() {
const popup = document.getElementById('popup');
const closeBtn = document.getElementById('popup-close');
let timerId = null;
// Проверяем, не закрывал ли пользователь попап ранее
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
const { timestamp } = JSON.parse(stored);
if (Date.now() - timestamp < EXPIRY_MS) {
return; // Не показываем
}
localStorage.removeItem(STORAGE_KEY);
}
function showPopup() {
popup.classList.add('popup--visible');
}
function closePopup() {
popup.classList.remove('popup--visible');
localStorage.setItem(STORAGE_KEY, JSON.stringify({ timestamp: Date.now() }));
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
}
// Запускаем таймер после загрузки DOM
document.addEventListener('DOMContentLoaded', () => {
timerId = setTimeout(showPopup, POPUP_DELAY);
});
// Закрытие по кнопке
closeBtn.addEventListener('click', closePopup);
// Закрытие по клику вне попапа (на overlay)
popup.addEventListener('click', (e) => {
if (e.target === popup) {
closePopup();
}
});
// Очищаем таймер при уходе со страницы
window.addEventListener('beforeunload', () => {
if (timerId) clearTimeout(timerId);
});
}
initPopup();

Как отвечать на собеседовании

Начни с краткого описания подхода: setTimeout + localStorage. Затем углубись в детали: почему localStorage, а не sessionStorage (межсессионное хранение), как обрабатываешь edge cases (очистка таймера, истечение срока). Упомяни про анимации и доступность (aria-атрибуты для попапа). Если спросят про альтернативы - можно использовать IndexedDB для больших данных или куки с HttpOnly для серверного контроля, но для простого флага localStorage оптимален.

Что проверяет интервьюер

  • Понимание асинхронности в браузере (event loop, таймеры).
  • Умение работать с Web Storage API и учитывать его ограничения (синхронность, размер 5-10 МБ).
  • Навыки обработки событий и управления состоянием UI.
  • Внимание к UX: задержка, анимация, закрытие без блокировки.
  • Знание best practices: очистка таймеров, проверка на существование элементов.

Типичные ошибки

  • Использование sessionStorage вместо localStorage - флаг сбрасывается при закрытии вкладки.
  • Отсутствие очистки таймера при закрытии попапа - попап может появиться после закрытия.
  • Неучёт истечения срока хранения - пользователь никогда не увидит попап снова.
  • Показ попапа до полной загрузки DOM - элементы могут отсутствовать.
  • Игнорирование доступности: попап без focus trap или aria-атрибутов.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью