insertar - llamar javascript desde html
Insertar código en el contexto de la página usando un script de contenido (5)
Estoy aprendiendo cómo crear extensiones de Chrome. Acabo de empezar a desarrollar uno para capturar eventos de YouTube. Quiero usarlo con el reproductor flash de YouTube (más adelante trataré de hacerlo compatible con HTML5).
manifest.json:
{
"name": "MyExtension",
"version": "1.0",
"description": "Gotta catch Youtube events!",
"permissions": ["tabs", "http://*/*"],
"content_scripts" : [{
"matches" : [ "www.youtube.com/*"],
"js" : ["myScript.js"]
}]
}
myScript.js:
function state() { console.log("State Changed!"); }
var player = document.getElementById("movie_player");
player.addEventListener("onStateChange", "state");
console.log("Started!");
El problema es que la consola me da el mensaje "¡Iniciado!" , pero no hay "Estado cambiado!" Cuando reproduzco / pausa videos de YouTube.
Cuando este código se pone en la consola, funcionó. ¿Qué estoy haciendo mal?
La única cosa desaparecido La excelente respuesta de la fila W oculta es cómo llamar desde el script inyectado al script de contenido y viceversa (especialmente si tiene objetos que no se pueden clasificar).
En el script inyectado o en el contenido, agregue un detector de eventos:
document.addEventListener(''yourCustomEvent'', function (e)
{
var data=e.detail;
console.log("received "+data);
});
En el otro lado (contenido o script inyectado) llame al evento:
var data="anything";
// updated: this works with Chrome 30:
var evt=document.createEvent("CustomEvent");
evt.initCustomEvent("yourCustomEvent", true, true, data);
document.dispatchEvent(evt);
// the following stopped working in Chrome 30 (Windows), detail was
// not received in the listener:
// document.dispatchEvent(new CustomEvent(''yourCustomEvent'', { detail: data }));
Los scripts de contenido se ejecutan en un entorno de "mundo aislado" . Tienes que inyectar tu método state()
en la propia página.
Cuando desee utilizar una de las API de chrome.*
En el script, debe implementar un controlador de eventos especial, como se describe en esta respuesta: Extensión de Chrome: recuperación del mensaje original de Gmail .
De lo contrario, si no tiene que usar las API de chrome.*
, Le recomiendo que inyecte todo su código JS en la página agregando una etiqueta <script>
:
Tabla de contenido
- Método 1: inyectar otro archivo
- Método 2: inyectar código incrustado
- Método 2b: Usando una función
- Método 3: Uso de un evento en línea
- Valores dinámicos en el código inyectado.
Método 1: inyectar otro archivo
Este es el método más fácil / mejor cuando tienes mucho código. Incluya su código JS real en un archivo dentro de su extensión, digamos script.js
. Luego, deje que su secuencia de comandos de contenido sea como sigue (se explica aquí: Javascript personalizado de "Aplicación de acceso directo" de Google Chome ):
var s = document.createElement(''script'');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.extension.getURL(''script.js'');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
Nota: Si utiliza este método, el archivo script.js
inyectado debe agregarse a la sección "web_accessible_resources"
( example ). Si no lo hace, Chrome se negará a cargar su script y mostrará el siguiente error en la consola:
Negando la carga de la extensión de cromo: // [EXTENSIONID] /script.js. Los recursos se deben enumerar en la clave de manifiesto web_accessible_resources para poder ser cargados por páginas fuera de la extensión.
Método 2: inyectar código incrustado
Este método es útil cuando desea ejecutar rápidamente un pequeño fragmento de código. (Ver también: ¿Cómo deshabilitar las teclas de acceso rápido de Facebook con la extensión de Chrome? ).
var actualCode = `// Code here.
// If you want to use a variable, use $ and curly braces.
// For example, to use a fixed random number:
var someFixedRandomValue = ${ Math.random() };
// NOTE: Do not insert unsafe variables in this way, see below
// at "Dynamic values in the injected code"
`;
var script = document.createElement(''script'');
script.textContent = actualCode;
(document.head||document.documentElement).appendChild(script);
script.remove();
Nota: los literales de plantilla solo son compatibles con Chrome 41 y superior. Si quieres que la extensión funcione en Chrome 40-, usa:
var actualCode = [''/* Code here. Example: */'' + ''alert(0);'',
''// Beware! This array have to be joined'',
''// using a newline. Otherwise, missing semicolons'',
''// or single-line comments (//) will mess up your'',
''// code ----->''].join(''/n'');
Método 2b: Usando una función
Para una gran parte del código, no es posible citar la cadena. En lugar de usar una matriz, se puede usar una función, y se puede clasificar:
var actualCode = ''('' + function() {
// All code is executed in a local scope.
// For example, the following does NOT overwrite the global `alert` method
var alert = null;
// To overwrite a global variable, prefix `window`:
window.alert = null;
} + '')();'';
var script = document.createElement(''script'');
script.textContent = actualCode;
(document.head||document.documentElement).appendChild(script);
script.remove();
Este método funciona, porque el operador +
en cadenas y una función convierte todos los objetos en una cadena. Si pretende utilizar el código más de una vez, es aconsejable crear una función para evitar la repetición del código. Una implementación podría verse como:
function injectScript(func) {
var actualCode = ''('' + func + '')();''
...
}
injectScript(function() {
alert("Injected script");
});
Nota: como la función está serializada, el alcance original y todas las propiedades enlazadas se pierden.
var scriptToInject = function() {
console.log(typeof scriptToInject);
};
injectScript(scriptToInject);
// Console output: "undefined"
Método 3: Uso de un evento en línea
A veces, desea ejecutar algo de código inmediatamente, por ejemplo, para ejecutar algo de código antes de crear el elemento <head>
. Esto se puede hacer insertando una etiqueta <script>
con textContent
(ver método 2 / 2b).
Una alternativa, pero no recomendada, es usar eventos en línea. No se recomienda porque si la página define una política de seguridad de contenido que prohíbe los scripts en línea, se bloquean los escuchas de eventos en línea. Los scripts en línea inyectados por la extensión, por otro lado, todavía se ejecutan. Si aún desea utilizar eventos en línea, así es como:
var actualCode = ''// Some code example /n'' +
''console.log(document.documentElement.outerHTML);'';
document.documentElement.setAttribute(''onreset'', actualCode);
document.documentElement.dispatchEvent(new CustomEvent(''reset''));
document.documentElement.removeAttribute(''onreset'');
Nota: este método asume que no hay otros detectores de eventos globales que manejen el evento de reset
. Si lo hay, también puede elegir uno de los otros eventos globales. Simplemente abra la consola de JavaScript (F12), escriba document.documentElement.on
y seleccione los eventos disponibles.
Valores dinámicos en el código inyectado.
Ocasionalmente, debe pasar una variable arbitraria a la función inyectada. Por ejemplo:
var GREETING = "Hi, I''m ";
var NAME = "Rob";
var scriptToInject = function() {
alert(GREETING + NAME);
};
Para inyectar este código, debe pasar las variables como argumentos a la función anónima. ¡Asegúrate de implementarlo correctamente! Lo siguiente no funcionará:
var scriptToInject = function (GREETING, NAME) { ... };
var actualCode = ''('' + scriptToInject + '')('' + GREETING + '','' + NAME '')'';
// The previous will work for numbers and booleans, but not strings.
// To see why, have a look at the resulting string:
var actualCode = "(function(GREETING, NAME) {...})(Hi I''m,Rob)";
// ^^^^^^ ^^^ No string literals!
La solución es usar JSON.stringify
antes de pasar el argumento. Ejemplo:
var actualCode = ''('' + function(greeting, name) { ...
} + '')('' + JSON.stringify(GREETING) + '','' + JSON.stringify(NAME) + '')'';
Si tiene muchas variables, vale la pena usar JSON.stringify
una vez, para mejorar la legibilidad, de la siguiente manera:
...
} + '')('' + JSON.stringify([arg1, arg2, arg3, arg4]) + '')'';
Si desea inyectar función pura, en lugar de texto, puede usar este método:
function inject(){
document.body.style.backgroundColor = ''blue'';
}
// this includes the function as text and the barentheses make it run itself.
var actualCode = "("+inject+")()";
document.documentElement.setAttribute(''onreset'', actualCode);
document.documentElement.dispatchEvent(new CustomEvent(''reset''));
document.documentElement.removeAttribute(''onreset'');
Y puede pasar parámetros (desafortunadamente, no se pueden clasificar objetos y matrices) a las funciones. Añádelo a las barétesis, así:
function inject(color){
document.body.style.backgroundColor = color;
}
// this includes the function as text and the barentheses make it run itself.
var color = ''yellow'';
var actualCode = "("+inject+")("+color+")";
También he enfrentado el problema de ordenar los scripts cargados, que se resolvió mediante la carga secuencial de los scripts. La carga se basa en la respuesta de Rob W.
function scriptFromFile(file) {
var script = document.createElement("script");
script.src = chrome.extension.getURL(file);
return script;
}
function scriptFromSource(source) {
var script = document.createElement("script");
script.textContent = source;
return script;
}
function inject(scripts) {
if (scripts.length === 0)
return;
var otherScripts = scripts.slice(1);
var script = scripts[0];
var onload = function() {
script.parentNode.removeChild(script);
inject(otherScripts);
};
if (script.src != "") {
script.onload = onload;
document.head.appendChild(script);
} else {
document.head.appendChild(script);
onload();
}
}
El ejemplo de uso sería:
var formulaImageUrl = chrome.extension.getURL("formula.png");
var codeImageUrl = chrome.extension.getURL("code.png");
inject([
scriptFromSource("var formulaImageUrl = ''" + formulaImageUrl + "'';"),
scriptFromSource("var codeImageUrl = ''" + codeImageUrl + "'';"),
scriptFromFile("EqEditor/eq_editor-lite-17.js"),
scriptFromFile("EqEditor/eq_config.js"),
scriptFromFile("highlight/highlight.pack.js"),
scriptFromFile("injected.js")
]);
En realidad, soy un poco nuevo para JS, así que siéntete libre de mandarme un vistazo a las mejores maneras.
en la secuencia de comandos de Contenido, agrego una etiqueta de secuencia de comandos a la cabeza que une un controlador ''onmessage'', dentro del controlador que uso, eval para ejecutar el código. En el script de contenido de stand, también uso el controlador de mensajes, así que obtengo comunicación bidireccional. Documentos de cromo
//Content Script
var pmsgUrl = chrome.extension.getURL(''pmListener.js'');
$("head").first().append("<script src=''"+pmsgUrl+"'' type=''text/javascript''></script>");
//Listening to messages from DOM
window.addEventListener("message", function(event) {
console.log(''CS :: message in from DOM'', event);
if(event.data.hasOwnProperty(''cmdClient'')) {
var obj = JSON.parse(event.data.cmdClient);
DoSomthingInContentScript(obj);
}
});
pmListener.js es un oyente de url de mensajes post
//pmListener.js
//Listen to messages from Content Script and Execute Them
window.addEventListener("message", function (msg) {
console.log("im in REAL DOM");
if (msg.data.cmnd) {
eval(msg.data.cmnd);
}
});
console.log("injected To Real Dom");
De esta manera, puedo tener una comunicación bidireccional entre CS y Real Dom. Es muy útil, por ejemplo, si necesita escuchar eventos webscoket, o cualquiera de las variables o eventos en la memoria.