Accediendo a `ventana`/DOM/HTML de la página web desde la extensión
Estoy escribiendo una extensión de Chrome e intentando superponerla <div>
a la página web actual tan pronto como se hace clic en un botón en el archivo popup.html.
Cuando accedo al document.body.insertBefore
método desde popup.html, se superpone <div>
en la ventana emergente, en lugar de en la página web actual.
¿Tengo que utilizar mensajes entre background.html y popup.html para poder acceder al DOM de la página web? Me gustaría hacer todo en popup.html y usar jQuery también, si es posible.
Problema: las páginas de extensión (ventana emergente, opciones, página de fondo en MV2, etc.) están separadas de la página web y tienen su propio DOM, document
, window
y una chrome-extension://
URL.
- Tenga en cuenta que el trabajador del servicio no tiene ningún DOM/documento/ventana.
- Para inspeccionar cada contexto de la extensión, utilice sus propias herramientas de desarrollo .
Solución: utilizar un script de contenido para acceder a la página web o interactuar con su contenido.
- Los scripts de contenido se ejecutan en la página web, no en la extensión.
- Los scripts de contenido están aislados de forma predeterminada; consulte cómo ejecutar código en el contexto de la página (también conocido como mundo PRINCIPAL).
- No cargue sus scripts de contenido en la página de extensión.
Método 1. Declarativo
manifiesto.json:
"content_scripts": [{
"matches": ["*://*.example.com/*"],
"js": ["contentScript.js"]
}],
Se ejecutará una vez cuando se cargue la página. Después de que eso suceda, utilice la mensajería .
¡Advertencia! No puede enviar elementos DOM, Map, Set, ArrayBuffer, clases, funciones, etc. Solo puede enviar objetos y tipos simples compatibles con JSON, por lo que deberá extraer manualmente los datos requeridos y pasarlos como una matriz u objeto simple.
Método 2. Programático
Manifiesto V3 :
Utilice chrome.scripting.executeScript en el script de extensión (como la ventana emergente) para inyectar un script/función de contenido en una pestaña a pedido.
El resultado de este método es la última expresión del script de contenido, por lo que puede usarse para extraer datos. Los datos deben ser compatibles con JSON; consulte la advertencia anterior.
Requerido
permissions
en manifest.json:"scripting"
- obligatorio;"activeTab"
- escenario ideal, adecuado para una respuesta a una acción del usuario (normalmente un clic en el icono de extensión en la barra de herramientas). No muestra ninguna advertencia de permiso al instalar la extensión.
Si el escenario ideal es imposible, agregue los sitios permitidos
host_permissions
en manifest.json:"*://*.example.com/"
además de cualquier otro sitio que desee."<all_urls>"
o"*://*/"
estos colocarán su extensión en una cola de revisión súper lenta en Chrome Web Store debido a los amplios permisos de host.
Diferencias de ManifestV2 con lo anterior:
- Utilice chrome.tabs.executeScript .
- Especifique sitios en
permissions
.
Algunos ejemplos del script emergente de extensión que utilizan inyección programática para agregar ese div.
ManifiestoV3
No olvide agregar los permisos en manifest.json; consulte la otra respuesta para obtener más información.
llamada sencilla:
(async () => { const [tab] = await chrome.tabs.query({active: true, currentWindow: true}); const [{result}] = await chrome.scripting.executeScript({ target: {tabId: tab.id}, func: () => document.querySelector('foo')?.textContent, }); document.body.textContent = result; })();
Llamada sencilla 2:
(async () => { const [tab] = await chrome.tabs.query({active: true, currentWindow: true}); await chrome.scripting.executeScript({ target: {tabId: tab.id}, func: inContent1, }); })(); // executeScript runs this code inside the tab function inContent1() { const el = document.createElement('div'); el.style.cssText = 'position:fixed; top:0; left:0; right:0; background:red'; el.textContent = 'DIV'; document.body.appendChild(el); }
Nota: en Chrome 91 o versiones anteriores
func:
debería serfunction:
.Llamar con parámetros y recibir un resultado.
Requiere Chrome 92 tal como se implementó
args
.Ejemplo 1:
res = await chrome.scripting.executeScript({ target: {tabId: tab.id}, func: (a, b) => { return [window[a], window[b]]; }, args: ['foo', 'bar'], });
Ejemplo 2:
(async () => { const [tab] = await chrome.tabs.query({active: true, currentWindow: true}); let res; try { res = await chrome.scripting.executeScript({ target: {tabId: tab.id}, func: inContent2, args: [{ foo: 'bar' }], // arguments must be JSON-serializable }); } catch (e) { console.warn(e.message || e); return; } // res[0] contains results for the main page of the tab document.body.textContent = JSON.stringify(res[0].result); })(); // executeScript runs this code inside the tab function inContent2(params) { const el = document.createElement('div'); el.style.cssText = 'position:fixed; top:0; left:0; right:0; background:red'; el.textContent = params.foo; document.body.appendChild(el); return { success: true, html: document.body.innerHTML, }; }
ManifiestoV2
llamada sencilla:
// uses inContent1 from ManifestV3 example above chrome.tabs.executeScript({ code: `(${ inContent1 })()` });
Llamar con parámetros y recibir un resultado:
// uses inContent2 from ManifestV3 example above chrome.tabs.executeScript({ code: `(${ inContent2 })(${ JSON.stringify({ foo: 'bar' }) })` }, ([result] = []) => { if (!chrome.runtime.lastError) { console.log(result); // shown in devtools of the popup window } });
Este ejemplo utiliza la conversión automática del
inContent
código de la función a cadena; el beneficio aquí es que el IDE puede aplicar resaltado de sintaxis y linting. El inconveniente obvio es que el navegador pierde tiempo analizando el código, pero normalmente tarda menos de 1 milisegundo, por lo que es insignificante.