Мне всегда хотелось рассказывать людям об интересных возможностях (технологиях), которые сейчас могут быть доступны каждому, но почему-то не доступны каждому. Да, получилось тавтология, но она в полной мере отображает моё внутреннее негодование на эту животрепещущую для меня тему. Как бы там ни было, речь сейчас будет не о том, как говорится. А поговорим мы сегодня о создании расширений для браузера Google Chrome (далее Хром).
Расширение, которое мы будем разрабатывать на протяжении всей статьи можно найти в магазине Google Chrome Web Store, с той лишь разницей, что там присутствует расширенный функционал. Помимо этого, присутствует исходный код на GitHub, опять таки с оговоркой на то, что там всё написано на CoffeeScript, а здесь будет вестись повествование с JavaScript. Кстати, я не поклонник и не сторонник CoffeeScript, но штука довольно интересная и полезная — советую попробовать.
Если вы когда-нибудь рассматривали идею создания расширения для Chrome, Firefox, Maxthon и прочих браузеров, то, наверное, уже успели заметить, что минимум усилий нужно приложить как раз таки для Хрома. Убедиться в этом можно, взглянув на документацию у соответствующих браузеров.
Постановка задачи
Написание расширения начинается с его описания и постановки задач, которое оно будет решать. Так как я сам себе хозяин и дедлайны срывать мне можно столько раз, сколько хочется, то ТЗ мне писать не нужно — достаточно уяснить, что:
- Расширение должно скрывать все комментарии в социальной сети «VK»;
- Расширение должно иметь возможность отображать комментарии;
- Расширение должно иметь возможность отображать комментарии на конкретных страницах;
С первого взгляда всё просто и нам по силам. Однако, в рамках статьи мы реализуем лишь первые два пункта.
Предвижу вопросы, содержание которых может быть примерно таким: «Нафига скрывать комментарии, если в этом вся суть социальной сети?!». Что же, справедливый вопрос, заслуживающий развёрнутого ответа:
Так сложились обстоятельства, что последнее время, когда я вижу комментарии в VK, мне хочется дарить горы фейспалмов комментаторам. Я подписан на большое количество различных пабликов, тематических (веб-разработка) и не очень. И как бы это не казалось странным, самым щедрым я становлюсь именно в группах с интересным для меня содержанием, а не котиками (в моём случае с пандами). Такого количества непрофессионального и безобразного потока информации в комментариях я не видел ещё нигде, да ещё и спорить думают. Помимо этого, комментарии в новостной ленте смотрятся не эстетично. В общем, сказано — сделано.
Каркас расширения
Наиприятнейшим для меня сюрпризом стало то, что в самом начале пути нас встречает описательный характер действий. Проще говоря, нам нужно описать наше приложение, его права и возможности — для этого служит файл manifest.json
.
Первым делом нужно заполнить три обязательных поля:
{
"manifest_version": 2, // Начиная с Chrome 18 ставим 2, иначе 1 (не поддерживается такое старьё)
"name": "My Extension", // Название расширения
"version": "versionString" // Версия расширения
}
Если с названием всё понятно, а с версией манифеста всё ещё проще, то поподробнее нужно остановиться с версией расширения.
Итак, все мы привыкли, что версия чего-либо состоит из трёх чисел, разделённых точками — Мажорное.Минорное.Патч (Имеется в виду число). С npm, bower и прочими прелестями разговор короткий: либо так, либо никак. А вот Google предлагает следующие варианты:
"version": "1"
"version": "1.0"
"version": "2.10.2"
"version": "3.1.2.4567"
Тем не менее, я сторонник семантического версионирования и советую вам так же придерживаться этой идеи, но не настаиваю.
Далее рекомендуемыми полями идут:
"default_locale": "en", // Язык по умолчанию
"description": "A plain text description", // Описание приложения
"icons": {...}, // Иконки 16 & 48 & 128, дополнительно можно 32 & 64 & 96 & 256 и т.д.
"short_name": "Short Name", // Короткое название расширения (~12 символов) для тех мест, где мало места для вывода полного названия
Просто так от Хрома ничего не получить — нужны разрешения. Что же, прочим их:
"permissions": [
"tabs", // Доступ к объекту Tabs
"activeTab" // Доступ к объекту ActiveTabs
],
Перечислять все разрешения, которые можно получить смысла я не вижу, поэтому переходим по этой ссылке и удивляемся, как хорошо оформлена документация.
Помимо прочего, нужно указать, что наше приложение хочет иметь иконку справа вверху:
"browser_action": {
"default_title": "Default Title", // Заголовок, который отображается при наведении
"default_icon": "icon48.png", // Иконка для отображения
"default_popup": "popup.html" // страница (окно), которая будет отображаться при клике на иконку
},
Если приложение имеет страницу настроек, то её тоже следует указать:
"options_page": "options.html",
Также можно перманентно вешать на различные страницы какие-то ресурсы (css или js), в зависимости от адреса:
"content_scripts": [
{
"matches": [
"http://site.com/*",
"https://site.com/*"
],
"css": [ "styles/styles.css" ]
}
],
И, напоследок, скажу, что можно указать фоновую страницу:
"background": {
"page": "background.html",
"scripts": ["scripts.js"],
"persistent": false
},
Фоновая страница — это невидимая страница, которая нужна для того, чтобы объединять все вкладки. Ссылки на ресурсы можно указывать как в файле манифеста, так и на самой странице background.html
.
Расширению часто требуется общий для всех вкладок скрипт, который обеспечивает их взаимодействие, хранит состояние. В конце концов, объединяет в одном месте весь используемый код, вместо того чтобы загружать его для каждой вкладки индивидуально.
Фоновая страница – это невидимая страница, которая как раз и содержит такой скрипт. Их бывает два типа: persistent (постоянные) и event (управляемые событиями). Persistent, как можно догадаться, загружена постоянно, а event-страница загружается только при необходимости, когда возникает связанное с ней событие.
— Alexandr Kozlov
Обязательно прочитайте статьи Александра в его блоге.
Подробнее обо всех полях файла манифеста можно узнать из документации.
В нашем случае, файл манифеста будет выглядеть следующим образом:
{
"manifest_version": 2,
"name": "__MSG_app_name__",
"short_name": "VKCommentBlocker",
"description": "__MSG_app_description__",
"version": "0.1.0",
"default_locale": "ru",
"permissions": [ "activeTab" ],
"browser_action": {
"default_icon": "icon_16.png",
"default_title": "__MSG_browser_action__"
},
"icons": {
"16": "icon_16.png",
"48": "icon_48.png",
"128": "icon_128.png"
},
"background": {
"persistent": false,
"page": "background.html"
},
"content_scripts": [
{
"matches": [
"http://vk.com/*",
"https://vk.com/*"
],
"css": [ "styles/commentblocker.css" ]
}
],
"web_accessible_resources": [ "styles/commentblocker_on.css" ]
}
Из того, что ранее не рассматривалось
- __MSG_key__ — это вариация Chrome на тему интернационализации приложений (i18n). Можно применять как в файле манифеста, так и в других файлах (даже CSS).
- web_accessible_resources — массив путей ресурсов, которые будут впоследствии использоваться в контексте веб-страниц. Без указания в нём пути — ничего не получится использовать на страницах сайтов, если такое поведение предполагается.
Ресурсы расширения
Огромный плюс в карму Chrome — мы уже сейчас можем подключить расширение, конечно, если созданы все ресурсы, указанные в manifest.json
.
Не думаю, что стоит заострять внимание на том, что содержится в файле commentblocker.css
и commentblocker_on.css
. Приведу лишь первый, в котором указаны все селекторы, в которых заключены комментарии:
@charset "utf-8";
.wall_module .reply_link_wrap .reply_link {
visibility: hidden !important;
}
.wall_module .replies_wrap,
#wl_replies_wrap,
#wl_reply_form_wrap,
#mv_comments_wrap,
#mv_your_comment,
#pv_comments,
#pv_comments_header,
#pv_your_comment {
display: none !important;
visibility: hidden !important;
}
body:after {
position: fixed;
content: "__MSG_mode_enable__";
top: 5px;
right: 5px;
padding: 6px 12px;
background-color: #ffc;
border: 1px solid #ddd;
z-index: 9999;
}
В файле commentblocker_on.css
, как не трудно догадаться, всё наоборот. Обратите внимание, что прямо в CSS я использую строку с языковом ключом content: "__MSG_mode_enable__"
. Самое время создать такой файл, где эти ключи будут храниться.
В корне нашего расширения создаём директорию _locales
и вложенные в неё директории en
и ru
. Далее в файле messages.json
описываем наши ключи.
{
"app_name": {
"message": "VK Comment Blocker"
},
"app_description": {
"message": "Удобный способ скрыть комментарии в новостной ленте и группах."
},
"browser_action": {
"message": "Переключить вид комментариев"
},
"mode_enable": {
"message": "Без комментариев!"
},
"mode_disable": {
"message": "С комментариями!"
}
}
Помимо поля message есть и другие поля, о которых можно узнать из документации.
Теперь создаём файлы background.html
, для начала так:
<!doctype html>
<html>
<head>
<title>Background</title>
</head>
<body>
</body>
</html>
Тут всё так, как в обычном HTML — ничего необычного. Кстати, файл background.html можно не создавать, так как он генерируется автоматически, на основе полей в manifest.json
.
Запускаем расширение
Запустить расширение можно, не написав ни одной строчки JavaScript. Чтобы сделать это, нужно пройтись по меню:
- Настройка и управление Google Chrome (Гамбургер)
- Дополнительные инструменты
- Расширения
- Поставить галочку напротив «режим разработчика»
- Загрузить распакованное расширение
- Выбрать папку с расширением
Расширение загрузилось и показалось в меню. Да, да, это вот это вот «В».
Казалось бы, у только что созданного нами расширения ничего нет в голове (нет никакой логики), а все комментарии на страницах социальной сети на букву «В» теперь скрыты. Ответ кроется в manifest.json
, где в поле "content_scripts": {}
мы указали на каких страницах (http://vk.com/*
и https://vk.com/*
) будет автоматически подключаться файл commentblocker.css
, который и скрывает все комментарии. Советую подробнее почитать про mathes patterns. Он лишь с виду так прост, а под капотом чуть ли не сивый мерин, да с прибамбасами.
Вот так, не написав ни одной строчки кода, у нас уже получилось расширение, выполняющее основную возложенную на него задачу.
Оживляем расширение
Осталось выполнить второй пункт задачи, а именно реализовать возможность отображения комментариев. Вкратце, нам нужно запихнуть файл commentblocker_on.css
, который отменит правила файла commentblocker.css
. И тут к нам на помощь спешит наш всемогущий JavaScript.
Помните, что я говорил про background.html
? Да, да, про то, что его можно не создавать. Давайте слегка изменим manifest.json
:
...
"background": {
"persistent": false,
"scripts": [ "scripts/commentblocker.js" ]
},
...
Просто подключили JavaScript файл. Ничего особенного. Переходим к этому файлу.
Просто так запихнуть JS на страницу нельзя. И такая же проблема имеется не только со скриптами. Поэтому нам нужно воспользоваться специальной инъекцией executeScript
.
Сначала нужно добавить обработчик события клика на иконку расширения:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, {
code: "(" + toggleComments.toString() + ")();"
});
});
Где toggleComments
— это функция, которая и будет производить инъекцию нашего CSS файла на страницу:
var toggleComments = function() {
var extensionLink;
(document.getElementById("extension") == null) ?
(
extensionLink = document.createElement("link"),
extensionLink.href = chrome.extension.getURL("/styles/commentblocker_on.css"),
extensionLink.id = "extension",
extensionLink.type = "text/css",
extensionLink.rel = "stylesheet",
document.getElementsByTagName("head")[0].appendChild(extensionLink)
)
: (document.getElementsByTagName("head")[0].removeChild(document.getElementById("extension")))
};
Думаю, что слов о том, что этот кусок кода проверяет наличие подключения нашего CSS на странице и делает выводы о необходимости его подключения или отключения, будет достаточно.
Между прочим, доступно не так много событий, которые покрывают различный спектр потребностей. Например, есть такие события:
- onCreated — создание вкладки.
- onUpdated — обновление вкладки.
- onRemoved — закрытие вкладки.
Стоит заметить, что событие onUpdated вызывается дважды:
- Обновление страницы;
- Полная загрузка страницы;
На StackOverflow советуют проверять статус страницы:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo && changeInfo.status === 'complete') {
...
}
});
Теперь при нажатии на иконку будет происходить подключение файла стилей, которые будут отображать комментарии, а повторный клик на иконку — вновь их скрывать.
Выводы
Как нельзя кстати, стоит упомянуть мою полную версию расширения VK Comment Blocker, которая доступна в Chrome Web Store. Помимо этого, так же доступен полный исходный код на GitHub.
Как можно заметить, разрабатывать расширения для Chrome очень просто. Можно обладать довольно скудными знаниями в этой области, но в тоже время сделать что-то своё. Разумеется, что это лишь песчинка в океане того, что можно сделать сейчас в браузере и, возможно, сделаете вы, если начнете копать глубже, чем рассказано в этой статье.
Ссылки
А как же без дополнительного материала? Думали, что я вот так просто вас отпущу?
- Целый блог, посвящённый разработке расширений для Chrome.
- Создание расширения для Chrome за пару часов.
- Chrome Extensions Box. Стартовый набор для создания расширений Google Chrome.
- Создание простого Chrome приложения.
- Создание своих собственных расширений для браузера.
- Делаем расширение для Google Chrome.
- Пишем расширение под Google Chrome.
Почему, если перейти по ссылкам указанным в конце твоей статьи, можно узнать намного больше и подробнее о том, о чем ты рассказывал в своей статье?
Ответ очень прост:
Мои статьи не должны давать тебе полные знания от и до, иначе какой смысл в том, что ты прочитал? Ты не захочешь узнать больше, если я дам тебе ответы на все твои вопросы в этой статье. А если я тебя обману или дам плохой совет, который ты примешь как должное? Шучу.
На самом деле, я не люблю углубляться в своих статьях из-за того, что их попросту будет не интересно читать. Моё дело показать, что можно сделать используя ту, или иную технологию, как это просто сделать и какие вообще есть причины рассматривать или изучать то, о чём шла речь в статье. Конечно, есть темы, которые мне очень интересны и я рассказываю о них намного больше, чем просто «обзорно».