Доступ к переменным и функциям, определённым в контексте страницы, из расширения
Я пытаюсь управлять плеером youtube.com с помощью моего расширения. Вот как выглядит мой manifest.json
:
{
"name": "MyExtension",
"version": "1.0",
"description": "Готов поймать события Youtube!",
"permissions": ["tabs", "http://*/*"],
"content_scripts" : [{
"matches" : [ "www.youtube.com/*"],
"js" : ["myScript.js"]
}]
}
И вот код в myScript.js
:
function state() { console.log("Состояние изменилось!"); }
var player = document.getElementById("movie_player");
player.addEventListener("onStateChange", "state");
console.log("Запуск!");
Проблема в том, что в консоли я вижу сообщение "Запуск!", но нет сообщения "Состояние изменилось!" при воспроизведении/пауза видео на YouTube.
Когда я выполняю этот код напрямую в консоли разработчика, он работает. Что я делаю не так?
3 ответ(ов)
Если вы хотите внедрить чистую функцию вместо текста, вы можете использовать следующий метод:
function inject(){
document.body.style.backgroundColor = 'blue';
}
// Здесь функция включена как текст, а скобки заставляют её выполниться.
var actualCode = "("+inject+")()";
document.documentElement.setAttribute('onreset', actualCode);
document.documentElement.dispatchEvent(new CustomEvent('reset'));
document.documentElement.removeAttribute('onreset');
Кроме того, вы можете передавать параметры (к сожалению, объекты и массивы не могут быть приведены к строке) в функции. Просто добавьте их в скобки, как показано ниже:
function inject(color){
document.body.style.backgroundColor = color;
}
// Здесь функция включена как текст, а скобки заставляют её выполниться.
var color = 'yellow';
var actualCode = "("+inject+")("+color+")";
Не забудьте, что передавать переменные нужно обязательно в виде строк, так как в приведённом примере параметр color
будет интерпретироваться как строка, а не как переменная.
Вот улучшенный вариант ответа на ваш вопрос, основанный на решении Али Зареи, но с использованием синтаксиса ES6.
Вы можете создать и вставить скрипт в контентном скрипте вашего расширения Chrome следующим образом:
// content-script.js
const scriptElement = document.createElement('script');
scriptElement.src = chrome.runtime.getURL(`injected-script.js?extensionId=${chrome.runtime.id}`);
scriptElement.type = 'module';
scriptElement.onload = () => {
scriptElement.remove(); // Удаляем элемент после загрузки
};
document.head.append(scriptElement); // Добавляем скрипт в head документа
А вот код для вашего инъектируемого скрипта:
// injected-script.js
const extensionId = new URL(import.meta.url).searchParams.get("extensionId"); // Получаем ID расширения из URL
Этот способ позволяет удобно использовать ES6 и современные возможности JavaScript при работе с расширениями для Chrome. Обратите внимание, что import.meta.url
возвращает URL текущего модуля, что делает его идеальным для получения параметров запроса.
Если вы хотите использовать динамические значения в вашем внедренном коде (Manifest V3), и тип вашего внедренного скрипта — модуль, вы не сможете использовать document.currentScript.dataset
, как описано в отличном ответе Роба. Вместо этого вы можете передать параметры в качестве URL-параметров и извлечь их в внедренном коде. Вот пример:
Скрипт контента:
var s = document.createElement('script');
s.src = chrome.runtime.getURL('../override-script.js?extensionId=' + chrome.runtime.id);
s.type = 'module';
s.onload = function () {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
Внедренный код (в моем случае это override-script.js
):
let extensionId = new URL(import.meta.url).searchParams.get("extensionId");
Таким образом, вы можете легко извлекать динамические значения, передавая их через параметры URL.
Цвета в консоли JavaScript
В чем разница между String.slice и String.substring?
Проверка соответствия строки регулярному выражению в JS
Существует ли ссылка на "последнюю" библиотеку jQuery в Google APIs?
Как создать диалог с кнопками "Ок" и "Отмена"