ФронтендJavaScriptHTML / CSS

Как сделать расширение для Chrome на JavaScript: первая кнопка в popup

Создаём расширение Chrome на Manifest V3: popup с кнопкой, временный доступ activeTab, выполнение скрипта на текущей странице и загрузка распакованной папки.

Кодик

Автор

6 мин чтения

Минимальное расширение Chrome состоит из manifest.json, окна popup.html и скрипта popup.js. Кнопка может менять текущую вкладку через chrome.scripting. Мы сделаем режим фокуса: нажатие приглушает изображения и боковые блоки открытой страницы, повторное нажатие возвращает их. Разрешение activeTab даст временный доступ только после действия пользователя.

Расширение удобно изучать на маленьком эффекте. Не нужен сервер, учётная запись или публикация в магазине: Chrome умеет загрузить папку прямо с диска. При этом вы сразу встречаете настоящую архитектуру платформы. Popup живёт в своём документе, а страница во вкладке в другом. Обычный document.querySelector() из popup ищет элементы только внутри popup, поэтому код для страницы нужно выполнить через API расширений.

1Описываем

Manifest сообщает Chrome имя, версию, разрешения и файл popup.

2Показываем

Popup содержит одну кнопку и короткий статус для пользователя.

3Выполняем

chrome.scripting запускает небольшую функцию в активной вкладке после клика.

Из каких файлов состоит первое расширение

Создайте пустую папку focus-extension. Manifest V3 является точкой входа: Chrome сначала читает JSON и узнаёт, какой интерфейс открыть. Мы запросим scripting для выполнения функции и activeTab для временного доступа к вкладке после жеста пользователя. Широкое разрешение на все сайты для такого примера не требуется.

Файл или разрешениеРольЧто проверить
manifest.jsonМетаданные и возможностиКорректный JSON без комментариев
popup.htmlМаленькое окно по значкуЕсть кнопка с id focus
popup.jsОбработчик и вызов APIСкрипт подключён отдельным файлом
scriptingВыполнение функции во вкладкеУказан в массиве permissions
activeTabВременный доступ после кликаНет лишнего доступа ко всем URL

Схема файлов расширения Chrome manifest popup и JavaScript
Manifest связывает окно popup с разрешениями, а JavaScript выполняет действие после клика.

JSON не поддерживает комментарии. Строка // объяснение делает manifest невалидным. Пояснения держите в README, а ошибки загрузки читайте на карточке расширения.

Пишем manifest, popup и функцию для вкладки

Создайте три файла из примера. В manifest укажите Manifest V3 и popup. В HTML подключите JavaScript через src: встроенный обработчик в атрибуте кнопки нарушает ограничения безопасности расширений. Функция toggleFocusMode не зависит от переменных popup, потому что будет сериализована и выполнена в контексте страницы.

{
  "manifest_version": 3,
  "name": "Режим фокуса",
  "version": "1.0.0",
  "action": { "default_popup": "popup.html" },
  "permissions": ["activeTab", "scripting"]
}

<!-- popup.html -->
<!doctype html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <style>
    body { width: 240px; padding: 16px; font: 15px system-ui; }
    button { width: 100%; padding: 12px; border: 0; border-radius: 12px;
      color: white; background: #4f46e5; cursor: pointer; }
    #status { min-height: 22px; }
  </style>
</head>
<body>
  <button id="focus">Переключить режим фокуса</button>
  <p id="status" aria-live="polite"></p>
  <script src="popup.js"></script>
</body>
</html>

// popup.js
const button = document.querySelector('#focus');
const status = document.querySelector('#status');

function toggleFocusMode() {
  const styleId = 'codik-focus-mode';
  const oldStyle = document.getElementById(styleId);
  if (oldStyle) {
    oldStyle.remove();
    return 'Режим фокуса выключен';
  }
  const style = document.createElement('style');
  style.id = styleId;
  style.textContent = 'img, video, aside { opacity: .12 !important; }';
  document.head.append(style);
  return 'Режим фокуса включён';
}

button.addEventListener('click', async () => {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  const [{ result }] = await chrome.scripting.executeScript({
    target: { tabId: tab.id },
    func: toggleFocusMode,
  });
  status.textContent = result;
});
Что происходит после нажатия
  • Popup находит активную вкладку в текущем окне и получает её tab.id.
  • executeScript выполняет функцию внутри страницы и возвращает её текстовый результат.
  • Первый клик добавляет стиль с постоянным id, второй находит этот стиль и удаляет его.

Поток события от кнопки popup через chrome scripting к активной вкладке
DOM popup и DOM страницы разделены, поэтому действие проходит через API расширений.

Почему activeTab безопаснее широкого доступа

Расширение может запросить доступ к определённым сайтам через host permissions, но для кнопки, которую пользователь сознательно нажимает на текущей странице, достаточно activeTab. Такое разрешение выдаёт временный доступ после жеста, например нажатия на значок или команды. Доступ заканчивается при закрытии вкладки или переходе на другой origin. Системные страницы браузера всё равно защищены.

ПодходДоступКогда подходит
activeTabТекущая вкладка после действияРазовая кнопка и локальный эффект
Host permissionЗаранее указанные адресаФункция должна работать на них постоянно
<all_urls>Почти все страницыТолько при ясной необходимости и объяснении
Системная страница ChromeВнедрение запрещеноПокажите понятное сообщение вместо падения
Логика должна лежать внутри пакета. Правила Manifest V3 запрещают загружать удалённый исполняемый код. Не подключайте JavaScript с CDN и не получайте строку с кодом для последующего выполнения.

Практика: загружаем распакованное расширение

Откройте страницу управления расширениями, включите режим разработчика и загрузите всю папку проекта. После правки manifest или JavaScript нажимайте кнопку перезагрузки на карточке расширения. Popup закрывается при потере фокуса, поэтому постоянные данные позже стоит хранить отдельно, но наш стиль уже живёт в самой вкладке до обновления страницы.

Первый полный тест
  1. Скопируйте JSON до маркера popup.html в manifest и сохраните три файла в одной папке.
  2. Откройте chrome://extensions, включите Developer mode и нажмите Load unpacked.
  3. Выберите папку focus-extension и закрепите новый значок на панели.
  4. Откройте обычную статью по HTTP или HTTPS, нажмите значок и кнопку режима фокуса.
  5. Убедитесь, что изображения и боковые элементы стали прозрачнее, а статус сообщил о включении.
  6. Нажмите второй раз и проверьте возврат страницы в исходный вид.
  7. Откройте ошибки карточки расширения и консоль popup. Там не должно быть красных сообщений.
Признаки рабочего расширения
  • Chrome принимает manifest и показывает значок расширения без предупреждения о невалидном JSON.
  • Popup открывается, кнопка реагирует и не требует обновлять саму страницу.
  • Один и тот же клик попеременно добавляет и удаляет стиль только в активной вкладке.
  • Расширение не запрашивает постоянный доступ ко всей истории сайтов ради одной кнопки.

Матрица диагностики ошибок расширения Chrome по контекстам
Manifest, popup, разрешения и страница оставляют ошибки в разных местах.

Почему кнопка popup ничего не делает

Диагностика расширения разделена на два контекста. Ошибки кнопки и вызова API ищите в консоли popup. Ошибки функции, выполненной во вкладке, могут появиться в DevTools самой страницы. Кроме того, Chrome не разрешает внедрение в некоторые внутренние страницы. Поэтому тестируйте на обычном сайте и оборачивайте пользовательский сценарий понятным сообщением об ошибке.

popup.js не подключён

Кнопка видна, но обработчика нет. Проверьте имя файла, тег script и ошибку 404 в инспекторе popup.

В manifest забыто scripting

Вызов chrome.scripting.executeScript завершается ошибкой разрешения.

Ищут элементы страницы из popup

У каждого документа свой DOM. Передайте функцию через scripting, если нужно изменить открытую вкладку.

Тестируют на chrome://extensions

Защищённая системная страница не разрешает обычное внедрение. Откройте публичный сайт.

После правки не перезагрузили расширение

Chrome продолжает использовать старый пакет. Нажмите Reload на карточке и заново откройте popup.

Запрашивайте минимальные разрешения. Если функция работает с activeTab, не заменяйте его на широкий доступ «на всякий случай». Пользователь видит набор разрешений и принимает решение об установке. Технические детали сверены с документацией Chrome по chrome.scripting.
Короткие ответы
Зачем нужен manifest.json?

Он сообщает Chrome версию формата, имя, версию расширения, popup и разрешения.

Почему document.querySelector из popup не видит страницу?

Popup и вкладка являются разными документами с отдельными DOM.

Что даёт activeTab?

Временный доступ к активной вкладке после осознанного действия пользователя.

Нужен ли доступ ко всем сайтам?

Нет. Для разового эффекта после клика достаточно activeTab и scripting.

Почему пример не работает на chrome://?

Chrome защищает внутренние страницы и не разрешает обычным расширениям внедрять туда скрипт.

Превратите одну кнопку в полезный инструмент

События, функции и работа с DOM подробно закрепляются в курсе JavaScript. Если связь файлов пока путается, сначала откройте инструкцию подключения JavaScript к HTML.

Следующий шаг удобно построить на манипуляциях с DOM: меняйте размер текста, скрывайте выбранные элементы или считайте заголовки. Для хранения настроек используйте идеи из проекта списка задач, а маршрут по базе фронтенда держите в хабе HTML, CSS и JavaScript.