Реализация Popup-интерфейса браузерного расширения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация Popup-интерфейса браузерного расширения
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация Popup-интерфейса браузерного расширения

Popup — это HTML-страница, которая открывается при клике на иконку расширения в тулбаре. Она живёт от нажатия до закрытия (потеря фокуса или явное закрытие) и не сохраняет состояние между открытиями — это нужно учитывать при проектировании.

Ограничения Popup

  • Максимальный размер: ~800×600 px, Chrome ограничивает высоту viewport
  • Жизненный цикл: создаётся при открытии, уничтожается при закрытии
  • Нет alert(), confirm() — заблокированы браузером
  • CSP запрещает inline scripts и eval — весь JS должен быть в .js файлах
  • Нельзя открывать по программе из content script без user gesture (в MV3)

Структура файлов

popup/
├── popup.html
├── popup.js      (или popup.tsx + сборка)
└── popup.css
<!-- popup.html -->
<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=350">
  <link rel="stylesheet" href="popup.css">
  <!-- NO inline scripts — нарушение CSP -->
</head>
<body>
  <div id="app"></div>
  <script src="popup.js"></script>
</body>
</html>

React в Popup

Popup с React — стандартная практика для сложных интерфейсов:

// popup/App.tsx
import { useEffect, useState } from 'react';
import browser from 'webextension-polyfill';

interface TabInfo {
  url: string;
  title: string;
}

export function App() {
  const [tab, setTab] = useState<TabInfo | null>(null);
  const [enabled, setEnabled] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function init() {
      // Получить данные активной вкладки
      const [activeTab] = await browser.tabs.query({ active: true, currentWindow: true });
      setTab({ url: activeTab.url ?? '', title: activeTab.title ?? '' });

      // Загрузить сохранённые настройки
      const { extensionEnabled } = await browser.storage.local.get('extensionEnabled');
      setEnabled(extensionEnabled ?? false);
      setLoading(false);
    }
    init();
  }, []);

  async function toggleEnabled() {
    const next = !enabled;
    setEnabled(next);
    await browser.storage.local.set({ extensionEnabled: next });

    // Уведомить content script на активной вкладке
    if (tab) {
      const [activeTab] = await browser.tabs.query({ active: true, currentWindow: true });
      if (activeTab.id) {
        browser.tabs.sendMessage(activeTab.id, { type: 'TOGGLE', enabled: next });
      }
    }
  }

  if (loading) return <div className="popup-loading">Загрузка...</div>;

  return (
    <div className="popup">
      <header className="popup__header">
        <img src="/icons/icon32.png" alt="" />
        <h1>My Extension</h1>
      </header>

      <section className="popup__body">
        <div className="popup__url" title={tab?.url}>{tab?.title}</div>

        <label className="toggle">
          <input
            type="checkbox"
            checked={enabled}
            onChange={toggleEnabled}
          />
          <span className="toggle__label">
            {enabled ? 'Включено' : 'Выключено'}
          </span>
        </label>
      </section>

      <footer className="popup__footer">
        <button onClick={() => browser.runtime.openOptionsPage()}>
          Настройки
        </button>
        <button onClick={() => browser.tabs.create({ url: 'https://example.com/help' })}>
          Помощь
        </button>
      </footer>
    </div>
  );
}

Размер и стилизация

Popup должен быть удобен в диапазоне 320–400 px ширины. Типичная вёрстка:

/* popup.css */
* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  width: 360px;
  min-height: 200px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 14px;
  color: #1a1a1a;
  background: #fff;
}

.popup {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.popup__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border-bottom: 1px solid #e5e7eb;
  background: #f9fafb;
}

.popup__header h1 {
  font-size: 15px;
  font-weight: 600;
}

.popup__body {
  flex: 1;
  padding: 16px;
}

.popup__footer {
  display: flex;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid #e5e7eb;
}

.popup__footer button {
  flex: 1;
  padding: 6px 12px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  background: #fff;
  cursor: pointer;
  font-size: 13px;
}

.popup__footer button:hover {
  background: #f3f4f6;
}

Связь Popup ↔ Background ↔ Content Script

// Паттерн запроса данных у content script через background
async function getPageData() {
  const [tab] = await browser.tabs.query({ active: true, currentWindow: true });

  if (!tab.id || !tab.url?.startsWith('http')) {
    return null; // расширение не может работать на chrome:// страницах
  }

  try {
    const data = await browser.tabs.sendMessage(tab.id, { type: 'GET_PAGE_DATA' });
    return data;
  } catch {
    // Content script не загружен на этой странице
    return null;
  }
}

Сохранение состояния между открытиями

Popup уничтожается при закрытии, поэтому состояние хранится в storage:

// Сохранять позицию скролла, открытый таб, введённый текст
async function saveUiState(state) {
  await browser.storage.session.set({ popupUiState: state }); // session — только до закрытия браузера
}

async function loadUiState() {
  const { popupUiState } = await browser.storage.session.get('popupUiState');
  return popupUiState ?? defaultState;
}

chrome.storage.session доступен с Chrome 102 и поддерживается в Edge/Opera. Firefox пока не поддерживает — там fallback на storage.local.

Badge на иконке

Popup часто дополняется badge — счётчиком или индикатором на иконке:

// Из background service worker
chrome.action.setBadgeText({ tabId: tab.id, text: '5' });
chrome.action.setBadgeBackgroundColor({ color: '#ef4444' });
chrome.action.setBadgeTextColor({ color: '#ffffff' }); // Chrome 110+

// Убрать badge
chrome.action.setBadgeText({ tabId: tab.id, text: '' });

Сроки

Popup с React, загрузкой данных вкладки, переключателями и навигацией к настройкам — 2–3 рабочих дня. Popup с комплексным UI (несколько вкладок, формы, реалтайм-данные из content script) — 4–6 дней.