jsdom es una implementación puramente JavaScript de muchos estándares web, en particular los estándares WHATWG DOM y HTML, para usar con Node.js. En general, el objetivo del proyecto es emular un subconjunto suficiente de un navegador web para que sea útil para probar y extraer aplicaciones web del mundo real.
Las últimas versiones de jsdom requieren Node.js v18 o posterior. (Las versiones de jsdom inferiores a v23 aún funcionan con versiones anteriores de Node.js, pero no son compatibles).
const jsdom = requerir("jsdom"); const {JSDOM} = jsdom;
Para usar jsdom, utilizará principalmente el constructor JSDOM
, que es una exportación con nombre del módulo principal de jsdom. Pase una cadena al constructor. Obtendrá un objeto JSDOM
, que tiene varias propiedades útiles, en particular window
:
const dom = new JSDOM(`<!DOCTYPE html><p>Hola mundo</p>`);console.log(dom.window.document.querySelector("p").textContent); // "Hola Mundo"
(Tenga en cuenta que jsdom analizará el HTML que le pase tal como lo hace un navegador, incluidas las etiquetas <html>
, <head>
y <body>
implícitas).
El objeto resultante es una instancia de la clase JSDOM
, que contiene una serie de propiedades y métodos útiles además de window
. En general, se puede utilizar para actuar sobre el jsdom desde el "exterior", haciendo cosas que no son posibles con las API DOM normales. Para casos simples, donde no necesita ninguna de estas funciones, recomendamos un patrón de codificación como
const { ventana } = nuevo JSDOM(`...`);// o incluso const { documento } = (nuevo JSDOM(`...`)).ventana;
La documentación completa sobre todo lo que puede hacer con la clase JSDOM
se encuentra a continuación, en la sección " JSDOM
Object API".
El constructor JSDOM
acepta un segundo parámetro que puede usarse para personalizar su jsdom de las siguientes maneras.
const dom = nuevo JSDOM(``, { URL: "https://ejemplo.org/", referente: "https://example.com/", tipo de contenido: "texto/html", incluirNodeLocations: verdadero, cuota de almacenamiento: 10000000});
url
establece el valor devuelto por window.location
, document.URL
y document.documentURI
, y afecta cosas como la resolución de URL relativas dentro del documento y las restricciones del mismo origen y la referencia utilizada al buscar subrecursos. El valor predeterminado es "about:blank"
.
referrer
solo afecta el valor leído de document.referrer
. El valor predeterminado no es ninguna referencia (lo que se refleja como una cadena vacía).
contentType
afecta el valor leído de document.contentType
, así como también la forma en que se analiza el documento: como HTML o XML. Se generarán valores que no sean de tipo HTML MIME o XML MIME. El valor predeterminado es "text/html"
. Si hay un parámetro charset
, puede afectar el procesamiento de datos binarios.
includeNodeLocations
conserva la información de ubicación producida por el analizador HTML, lo que le permite recuperarla con el método nodeLocation()
(descrito a continuación). También garantiza que los números de línea informados en los seguimientos de la pila de excepciones para el código que se ejecuta dentro de los elementos <script>
sean correctos. El valor predeterminado es false
para ofrecer el mejor rendimiento y no se puede utilizar con un tipo de contenido XML ya que nuestro analizador XML no admite información de ubicación.
storageQuota
es el tamaño máximo en unidades de código para las áreas de almacenamiento separadas utilizadas por localStorage
y sessionStorage
. Los intentos de almacenar datos mayores que este límite provocarán que se genere una DOMException
. De forma predeterminada, está configurado en 5.000.000 de unidades de código por origen, inspirado en la especificación HTML.
Tenga en cuenta que tanto url
como referrer
se canonicalizan antes de usarse, por lo que, por ejemplo, si ingresa "https:example.com"
, jsdom lo interpretará como si hubiera ingresado "https://example.com/"
. Si pasa una URL que no se puede analizar, la llamada se realizará. (Las URL se analizan y serializan de acuerdo con el estándar de URL).
La habilidad más poderosa de jsdom es que puede ejecutar scripts dentro de jsdom. Estos scripts pueden modificar el contenido de la página y acceder a todas las API de la plataforma web que implementa jsdom.
Sin embargo, esto también es muy peligroso cuando se trata de contenido que no es de confianza. El sandbox jsdom no es infalible, y el código que se ejecuta dentro de los <script>
DOM puede, si se esfuerza lo suficiente, obtener acceso al entorno Node.js y, por lo tanto, a su máquina. Como tal, la capacidad de ejecutar scripts incrustados en HTML está deshabilitada de forma predeterminada:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`);// El script no se ejecutará, de forma predeterminada:console.log(dom.window.document.getElementById("content").children.length); // 0
Para habilitar la ejecución de scripts dentro de la página, puede usar la opción runScripts: "dangerously"
:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`, { runScripts: "dangerfully" });// El script se ejecutará y modificará el DOM:console.log(dom.window.document.getElementById("content").children.length); // 1
Nuevamente enfatizamos que solo se use esto cuando se proporcione código jsdom que usted sepa que es seguro. Si lo usa en código arbitrario proporcionado por el usuario o en código de Internet, en realidad está ejecutando código Node.js que no es de confianza y su máquina podría verse comprometida.
Si desea ejecutar scripts externos , incluidos a través de <script src="">
, también deberá asegurarse de que los carguen. Para hacer esto, agregue la opción resources: "usable"
como se describe a continuación. (Es probable que también desee configurar la opción url
, por los motivos que se explican allí).
Los atributos del controlador de eventos, como <div onclick="">
, también se rigen por esta configuración; no funcionarán a menos que runScripts
esté configurado en "dangerously"
. (Sin embargo, las propiedades del controlador de eventos, como div.onclick = ...
, funcionarán independientemente de runScripts
).
Si simplemente está intentando ejecutar un script "desde el exterior", en lugar de permitir que los elementos <script>
y los atributos de los controladores de eventos se ejecuten "desde el interior", puede usar la opción runScripts: "outside-only"
, que permite copias nuevas de todos los globales proporcionados por las especificaciones de JavaScript se instalarán en window
. Esto incluye cosas como window.Array
, window.Promise
, etc. También incluye, en particular, window.eval
, que permite ejecutar scripts, pero con la window
jsdom como global:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`, { runScripts: "outside-only" });// ejecuta un script fuera de JSDOM:dom.window.eval('document.getElementById("content").append(document.createElement("p"));');console.log(dom.window.document.getElementById("content"). niños.longitud); // 1console.log(dom.window.document.getElementsByTagName("hr").length); // 0console.log(dom.window.document.getElementsByTagName("p").length); // 1
Esto está desactivado de forma predeterminada por motivos de rendimiento, pero es seguro habilitarlo.
Tenga en cuenta que en la configuración predeterminada, sin configurar runScripts
, los valores de window.Array
, window.eval
, etc. serán los mismos que los proporcionados por el entorno externo de Node.js. Es decir, window.eval === eval
se mantendrá, por lo que window.eval
no ejecutará scripts de manera útil.
Recomendamos encarecidamente no intentar "ejecutar scripts" combinando los entornos globales jsdom y Node (por ejemplo, haciendo global.window = dom.window
) y luego ejecutar scripts o código de prueba dentro del entorno global de Node. En su lugar, debe tratar jsdom como lo haría con un navegador y ejecutar todos los scripts y pruebas que necesiten acceso a un DOM dentro del entorno jsdom, usando window.eval
o runScripts: "dangerously"
. Esto podría requerir, por ejemplo, crear un paquete de browserify para ejecutarlo como un elemento <script>
, tal como lo haría en un navegador.
Finalmente, para casos de uso avanzados, puede utilizar el método dom.getInternalVMContext()
, que se documenta a continuación.
jsdom no tiene la capacidad de representar contenido visual y actuará como un navegador sin cabeza de forma predeterminada. Proporciona sugerencias a las páginas web a través de API como document.hidden
de que su contenido no es visible.
Cuando la opción pretendToBeVisual
se establece en true
, jsdom pretenderá que está procesando y mostrando contenido. Lo hace mediante:
Cambiando document.hidden
para devolver false
en lugar de true
Cambiar document.visibilityState
para que devuelva "visible"
en lugar de "prerender"
Habilitar los métodos window.requestAnimationFrame()
y window.cancelAnimationFrame()
, que de otro modo no existirían
ventana const = (new JSDOM(``, { pretendToBeVisual: true })).window;window.requestAnimationFrame(timestamp => { console.log(marca de tiempo > 0);});
Tenga en cuenta que jsdom todavía no realiza ningún diseño ni renderizado, por lo que en realidad se trata solo de pretender ser visual, no de implementar las partes de la plataforma que implementaría un navegador web visual real.
De forma predeterminada, jsdom no cargará ningún subrecurso como scripts, hojas de estilo, imágenes o iframes. Si desea que jsdom cargue dichos recursos, puede pasar la opción resources: "usable"
, que cargará todos los recursos utilizables. Esos son:
Marcos e iframes, mediante <frame>
y <iframe>
Hojas de estilo, a través de <link rel="stylesheet">
Scripts, a través de <script>
, pero solo si runScripts: "dangerously"
también está configurado
Imágenes, a través de <img>
, pero solo si el paquete canvas
npm también está instalado (consulte "Soporte de Canvas" a continuación)
Al intentar cargar recursos, recuerde que el valor predeterminado para la opción url
es "about:blank"
, lo que significa que cualquier recurso incluido a través de URL relativas no se cargará. (El resultado de intentar analizar la URL /something
con respecto a la URL about:blank
es un error). Por lo tanto, es probable que desees establecer un valor no predeterminado para la opción url
en esos casos, o usar una de las opciones más convenientes. API que lo hacen automáticamente.
Para personalizar más completamente el comportamiento de carga de recursos de jsdom, puede pasar una instancia de la clase ResourceLoader
como valor de la opción de resources
:
const ResourceLoader = nuevo jsdom.ResourceLoader ({ proxy: "http://127.0.0.1:9001", SSL estricto: falso, userAgent: "Mellblomenator/9000",}); const dom = new JSDOM(``, { recursos: ResourceLoader });
Las tres opciones para el constructor ResourceLoader
son:
proxy
es la dirección de un proxy HTTP que se utilizará.
strictSSL
se puede establecer en falso para deshabilitar el requisito de que los certificados SSL sean válidos.
userAgent
afecta el encabezado User-Agent
enviado y, por lo tanto, el valor resultante para navigator.userAgent
. El valor predeterminado es `Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${jsdomVersion}`
.
Puede personalizar aún más la obtención de recursos subclasificando ResourceLoader
y anulando el método fetch()
. Por ejemplo, aquí hay una versión que anula la respuesta proporcionada para una URL específica:
clase CustomResourceLoader extiende jsdom.ResourceLoader { fetch(url, opciones) {// Anula el contenido de este script para hacer algo inusual.if (url === "https://example.com/some-specific-script.js") { return Promise.resolve( Buffer.from("window.someGlobal = 5;"));}return super.fetch(url, opciones); }}
jsdom llamará al método fetch()
de su cargador de recursos personalizado cada vez que encuentre un recurso "utilizable", según la sección anterior. El método toma una cadena URL, así como algunas opciones que debes pasar sin modificar si llamas super.fetch()
. Debe devolver una promesa para un objeto Buffer
de Node.js o devolver null
si el recurso no se va a cargar intencionalmente. En general, en la mayoría de los casos será necesario delegar en super.fetch()
, como se muestra.
Una de las opciones que recibirá en fetch()
será el elemento (si corresponde) que está recuperando un recurso.
clase CustomResourceLoader extiende jsdom.ResourceLoader { fetch(url, opciones) {if (options.element) { console.log(`Element ${options.element.localName} está solicitando la URL ${url}`);}return super.fetch(url, options); }}
Al igual que los navegadores web, jsdom tiene el concepto de "consola". Esto registra tanto la información enviada directamente desde la página, a través de scripts que se ejecutan dentro del documento, como la información de la propia implementación de jsdom. A la consola controlable por el usuario la llamamos "consola virtual" para distinguirla de la API de la console
Node.js y de la API window.console
dentro de la página.
De forma predeterminada, el constructor JSDOM
devolverá una instancia con una consola virtual que reenvía toda su salida a la consola Node.js. Para crear su propia consola virtual y pasarla a jsdom, puede anular este valor predeterminado haciendo
const virtualConsole = nuevo jsdom.VirtualConsole(); const dom = nuevo JSDOM(``, { virtualConsole });
Un código como este creará una consola virtual sin comportamiento. Puedes darle comportamiento agregando detectores de eventos para todos los métodos de consola posibles:
virtualConsole.on("error", () => { ... });virtualConsole.on("advertir", () => { ... });virtualConsole.on("info", () => { ... });virtualConsole.on("dir", () => { ... });// ... etc. Consulte https://console.spec.whatwg.org/#logging
(Tenga en cuenta que probablemente sea mejor configurar estos detectores de eventos antes de llamar new JSDOM()
, ya que pueden ocurrir errores o secuencias de comandos que invocan la consola durante el análisis).
Si simplemente desea redirigir la salida de la consola virtual a otra consola, como la predeterminada de Node.js, puede hacerlo
virtualConsole.sendTo(consola);
También hay un evento especial, "jsdomError"
, que se activará con objetos de error para informar errores del propio jsdom. Esto es similar a cómo suelen aparecer mensajes de error en las consolas de los navegadores web, incluso si no son iniciados por console.error
. Hasta ahora, los siguientes errores se generan de esta manera:
Errores al cargar o analizar subrecursos (scripts, hojas de estilo, marcos e iframes)
Errores de ejecución de script que no son manejados por un controlador de eventos onerror
de ventana que devuelve true
o llama event.preventDefault()
Errores no implementados resultantes de llamadas a métodos, como window.alert
, que jsdom no implementa, pero se instala de todos modos por compatibilidad web.
Si está utilizando sendTo(c)
para enviar errores a c
, de forma predeterminada llamará c.error(errorStack[, errorDetail])
con información de los eventos "jsdomError"
. Si prefiere mantener una asignación estricta uno a uno de eventos a llamadas a métodos, y tal vez manejar los "jsdomError"
usted mismo, entonces puede hacerlo
virtualConsole.sendTo(c, {omitirJSDOMErrors: verdadero});
Al igual que los navegadores web, jsdom tiene el concepto de un recipiente de cookies que almacena cookies HTTP. Se puede acceder a las cookies que tienen una URL en el mismo dominio que el documento y que no están marcadas como solo HTTP a través de la API document.cookie
. Además, todas las cookies en el tarro de galletas afectarán la recuperación de subrecursos.
De forma predeterminada, el constructor JSDOM
devolverá una instancia con un recipiente de cookies vacío. Para crear su propio tarro de galletas y pasarlo a jsdom, puede anular este valor predeterminado haciendo
const cookieJar = nuevo jsdom.CookieJar(tienda, opciones); const dom = nuevo JSDOM(``, { cookieJar });
Esto es especialmente útil si desea compartir el mismo tarro de galletas entre varios jsdoms o preparar el tarro de galletas con ciertos valores con anticipación.
Los frascos de galletas se proporcionan en el paquete de galletas resistentes. El constructor jsdom.CookieJar
es una subclase del tarro de galletas resistente que establece de forma predeterminada la opción looseMode: true
, ya que coincide mejor con el comportamiento de los navegadores. Si desea utilizar las utilidades y clases de hard-cookie usted mismo, puede utilizar la exportación del módulo jsdom.toughCookie
para obtener acceso a la instancia del módulo de hard-cookie empaquetada con jsdom.
jsdom le permite intervenir en la creación de un jsdom muy temprano: después de que se crean los objetos Window
y Document
, pero antes de que se analice cualquier HTML para llenar el documento con nodos:
const dom = nuevo JSDOM(`<p>Hola</p>`, { beforeParse(ventana) {ventana.document.childNodes.length === 0;window.someCoolAPI = () => { /* ... */ }; }});
Esto es especialmente útil si desea modificar el entorno de alguna manera, por ejemplo, agregando ajustes para las API de la plataforma web que jsdom no admite.
JSDOM
Una vez que haya construido un objeto JSDOM
, tendrá las siguientes capacidades útiles:
La window
de propiedades recupera el objeto Window
que se creó para usted.
Las propiedades virtualConsole
y cookieJar
reflejan las opciones que usted pasa o los valores predeterminados creados para usted si no se pasó nada para esas opciones.
serialize()
El método serialize()
devolverá la serialización HTML del documento, incluido el tipo de documento:
const dom = new JSDOM(`<!DOCTYPE html>hola`);dom.serialize() === "<!DOCTYPE html><html><head></head><body>hola</body></ html>";// Contraste con:dom.window.document.documentElement.outerHTML === "<html><head></head><body>hola</body></html>";
nodeLocation(node)
El método nodeLocation()
encontrará dónde se encuentra un nodo DOM dentro del documento fuente y devolverá la información de ubicación de parse5 para el nodo:
const dom = nuevo JSDOM( `<p>Hola <img src="foo.jpg"> </p>`, { includeNodeLocations: verdadero });const documento = dom.window.document;const bodyEl = document.body; // creado implícitamenteconst pEl = document.querySelector("p");const textNode = pEl.firstChild;const imgEl = document.querySelector("img");console.log(dom.nodeLocation(bodyEl)); // nulo; no está en sourceconsole.log(dom.nodeLocation(pEl)); // { startOffset: 0, endOffset: 39, startTag: ..., endTag: ... }console.log(dom.nodeLocation(textNode)); // { startOffset: 3, endOffset: 13 }console.log(dom.nodeLocation(imgEl)); // { startOffset: 13, endOffset: 32 }
Tenga en cuenta que esta característica solo funciona si ha configurado la opción includeNodeLocations
; Las ubicaciones de los nodos están desactivadas de forma predeterminada por motivos de rendimiento.
vm
de Node.js usando getInternalVMContext()
El módulo vm
integrado de Node.js es lo que sustenta la magia de ejecución de scripts de jsdom. Algunos casos de uso avanzados, como precompilar un script y luego ejecutarlo varias veces, se benefician del uso del módulo vm
directamente con una Window
creada por jsdom.
Para obtener acceso al objeto global contextualizado, adecuado para su uso con las API vm
, puede utilizar el método getInternalVMContext()
:
const { Script } = require("vm");const dom = new JSDOM(``, { runScripts: "solo para exteriores" }); const script = new Script(` if (!this.ran) { this.ran = 0; } ++this.ran;`);const vmContext = dom.getInternalVMContext();script.runInContext(vmContext);script.runInContext(vmContext);script.runInContext(vmContext);console.assert(dom.window.ran === 3);
Esta es una funcionalidad algo avanzada y le recomendamos que se ciña a las API DOM normales (como window.eval()
o document.createElement("script")
) a menos que tenga necesidades muy específicas.
Tenga en cuenta que este método generará una excepción si la instancia JSDOM
se creó sin ejecutar runScripts
configurados o si está utilizando jsdom en un navegador web.
reconfigure(settings)
La propiedad top
en window
está marcada como [Unforgeable]
en la especificación, lo que significa que es una propiedad propia no configurable y, por lo tanto, no puede ser anulada ni sombreada por el código normal que se ejecuta dentro de jsdom, incluso usando Object.defineProperty
.
De manera similar, actualmente jsdom no maneja la navegación (como configurar window.location.href = "https://example.com/"
); Al hacerlo, la consola virtual emitirá un "jsdomError"
que explica que esta característica no está implementada y nada cambiará: no habrá ningún nuevo objeto Window
o Document
, y el objeto location
de window
existente seguirá teniendo la misma valores de propiedad.
Sin embargo, si estás actuando desde fuera de la ventana, por ejemplo, en algún marco de prueba que crea jsdoms, puedes anular uno o ambos usando el método especial reconfigure()
:
const dom = new JSDOM();dom.window.top === dom.window;dom.window.location.href === "about:blank";dom.reconfigure({ windowTop: myFakeTopForTesting, url: "https: //ejemplo.com/" });dom.window.top === myFakeTopForTesting;dom.window.location.href === "https://ejemplo.com/";
Tenga en cuenta que cambiar la URL de jsdom afectará a todas las API que devuelven la URL del documento actual, como window.location
, document.URL
y document.documentURI
, así como a la resolución de las URL relativas dentro del documento y las comprobaciones del mismo origen. y referencia utilizada al buscar subrecursos. Sin embargo, no realizará navegación hacia el contenido de esa URL; el contenido del DOM permanecerá sin cambios y no se crearán nuevas instancias de Window
, Document
, etc.
fromURL()
Además del constructor JSDOM
en sí, jsdom proporciona un método de fábrica que devuelve promesas para construir un jsdom a partir de una URL:
JSDOM.fromURL("https://ejemplo.com/", opciones).luego(dom => { console.log(dom.serialize());});
La promesa devuelta se cumplirá con una instancia JSDOM
si la URL es válida y la solicitud se realiza correctamente. Cualquier redireccionamiento se seguirá hasta su destino final.
Las opciones proporcionadas a fromURL()
son similares a las proporcionadas al constructor JSDOM
, con las siguientes restricciones y consecuencias adicionales:
No se pueden proporcionar las opciones url
y contentType
.
La opción referrer
se utiliza como encabezado de solicitud Referer
HTTP de la solicitud inicial.
La opción resources
también afecta a la solicitud inicial; esto es útil si desea, por ejemplo, configurar un proxy (ver arriba).
La URL, el tipo de contenido y la referencia del jsdom resultante se determinan a partir de la respuesta.
Cualquier cookie configurada a través de los encabezados de respuesta HTTP Set-Cookie
se almacena en el contenedor de cookies del jsdom. De manera similar, cualquier cookie que ya esté en un contenedor de cookies proporcionado se envía como encabezados de solicitud Cookie
HTTP.
fromFile()
Similar a fromURL()
, jsdom también proporciona un método de fábrica fromFile()
para construir un jsdom a partir de un nombre de archivo:
JSDOM.fromFile("cosas.html", opciones).luego(dom => { console.log(dom.serialize());});
La promesa devuelta se cumplirá con una instancia JSDOM
si se puede abrir el archivo proporcionado. Como es habitual en las API de Node.js, el nombre del archivo se proporciona en relación con el directorio de trabajo actual.
Las opciones proporcionadas a fromFile()
son similares a las proporcionadas al constructor JSDOM
, con los siguientes valores predeterminados adicionales:
La opción url
será la URL de un archivo correspondiente al nombre de archivo dado, en lugar de "about:blank"
.
La opción contentType
será predeterminada "application/xhtml+xml"
si el nombre de archivo dado termina en .xht
, .xhtml
o .xml
; de lo contrario, seguirá siendo el valor predeterminado "text/html"
.
fragment()
Para los casos más simples, es posible que no necesite una instancia JSDOM
completa con todo su poder asociado. ¡Es posible que ni siquiera necesites una Window
o Document
! En su lugar, sólo necesita analizar algo de HTML y obtener un objeto DOM que pueda manipular. Para eso, tenemos fragment()
, que crea un DocumentFragment
a partir de una cadena determinada:
const frag = JSDOM.fragment(`<p>Hola</p><p><strong>¡Hola!</strong>`);frag.childNodes.length === 2;frag.querySelector("strong"). textContent === "¡Hola!";// etc.
Aquí frag
es una instancia DocumentFragment
, cuyo contenido se crea analizando la cadena proporcionada. El análisis se realiza utilizando un elemento <template>
, por lo que puede incluir cualquier elemento allí (incluidos aquellos con reglas de análisis extrañas como <td>
). También es importante tener en cuenta que el DocumentFragment
resultante no tendrá un contexto de navegación asociado: es decir, ownerDocument
de los elementos tendrá una propiedad defaultView
nula, los recursos no se cargarán, etc.
Todas las invocaciones de la fábrica fragment()
dan como resultado DocumentFragment
s que comparten el mismo propietario de plantilla Document
. Esto permite muchas llamadas a fragment()
sin gastos generales adicionales. Pero también significa que las llamadas a fragment()
no se pueden personalizar con ninguna opción.
Tenga en cuenta que la serialización no es tan fácil con DocumentFragment
como con objetos JSDOM
completos. Si necesita serializar su DOM, probablemente debería utilizar el constructor JSDOM
de forma más directa. Pero para el caso especial de un fragmento que contiene un solo elemento, es bastante fácil hacerlo por medios normales:
const frag = JSDOM.fragment(`<p>Hola</p>`);console.log(frag.firstChild.outerHTML); // registra "<p>Hola</p>"
jsdom incluye soporte para usar el paquete canvas
para extender cualquier elemento <canvas>
con la API canvas. Para que esto funcione, debe incluir canvas
como una dependencia en su proyecto, como un par de jsdom
. Si jsdom puede encontrar el paquete canvas
, lo usará, pero si no está presente, los elementos <canvas>
se comportarán como <div>
s. Desde jsdom v13, se requiere la versión 2.x de canvas
; La versión 1.x ya no es compatible.
Además de proporcionar una cadena, al constructor JSDOM
también se le pueden proporcionar datos binarios, en forma de un Buffer
Node.js o un tipo de datos binarios JavaScript estándar como ArrayBuffer
, Uint8Array
, DataView
, etc. Cuando se hace esto, jsdom detectará la codificación de los bytes proporcionados, buscando etiquetas <meta charset>
tal como lo hace un navegador.
Si la opción contentType
proporcionada contiene un parámetro charset
, esa codificación anulará la codificación rastreada, a menos que esté presente una lista de materiales UTF-8 o UTF-16, en cuyo caso tienen prioridad. (Nuevamente, esto es como un navegador).
Este rastreo de codificación también se aplica a JSDOM.fromFile()
y JSDOM.fromURL()
. En el último caso, cualquier encabezado Content-Type
enviado con la respuesta tendrá prioridad, de la misma manera que la opción contentType
del constructor.
Tenga en cuenta que, en muchos casos, proporcionar bytes de esta manera puede ser mejor que proporcionar una cadena. Por ejemplo, si intenta utilizar la API buffer.toString("utf-8")
de Node.js, Node.js no eliminará ninguna lista de materiales principal. Si luego le da esta cadena a jsdom, la interpretará palabra por palabra, dejando la lista de materiales intacta. Pero el código de decodificación de datos binarios de jsdom eliminará las listas de materiales principales, como un navegador; en tales casos, el suministro directo buffer
dará el resultado deseado.
Los temporizadores en jsdom (establecidos por window.setTimeout()
o window.setInterval()
) ejecutarán, por definición, código en el futuro en el contexto de la ventana. Dado que no hay forma de ejecutar código en el futuro sin mantener vivo el proceso, los excelentes temporizadores jsdom mantendrán vivo su proceso Node.js. De manera similar, dado que no hay forma de ejecutar código en el contexto de un objeto sin mantenerlo vivo, los temporizadores jsdom pendientes evitarán la recolección de basura de la ventana en la que están programados.
Si desea asegurarse de cerrar una ventana jsdom, use window.close()
, que finalizará todos los temporizadores en ejecución (y también eliminará cualquier detector de eventos en la ventana y el documento).
En Node.js puedes depurar programas usando Chrome DevTools. Consulte la documentación oficial para saber cómo empezar.
De forma predeterminada, los elementos jsdom están formateados como objetos JS antiguos en la consola. Para facilitar la depuración, puede utilizar jsdom-devtools-formatter, que le permite inspeccionarlos como elementos DOM reales.
Las personas suelen tener problemas con la carga de scripts asíncronos cuando usan jsdom. Muchas páginas cargan scripts de forma asincrónica, pero no hay forma de saber cuándo terminan de hacerlo y, por lo tanto, cuándo es un buen momento para ejecutar su código e inspeccionar la estructura DOM resultante. Ésta es una limitación fundamental; No podemos predecir qué harán los scripts en la página web y, por lo tanto, no podemos decirle cuándo terminarán de cargar más scripts.
Esto se puede solucionar de varias maneras. La mejor manera, si controlas la página en cuestión, es utilizar cualquier mecanismo proporcionado por el cargador de scripts para detectar cuándo finaliza la carga. Por ejemplo, si estás usando un cargador de módulos como RequireJS, el código podría verse así:
// En el lado de Node.js:const window = (new JSDOM(...)).window;window.onModulesLoaded = () => { console.log("¡listo para funcionar!");};
<!-- Dentro del HTML que proporcionas a jsdom --><script>requirejs(["entry-module"], () => { ventana.onModulesLoaded();});</script>
Si no controla la página, puede probar soluciones alternativas, como sondear la presencia de un elemento específico.
Para obtener más detalles, consulte la discusión en el n.° 640, especialmente el interesante comentario de @matthewkastor.
Aunque disfrutamos agregando nuevas funciones a jsdom y manteniéndolo actualizado con las últimas especificaciones web, le faltan muchas API. No dude en presentar un problema si falta algo, pero somos un equipo pequeño y ocupado, por lo que una solicitud de extracción podría funcionar aún mejor.
Algunas características de jsdom las proporcionan nuestras dependencias. La documentación notable a ese respecto incluye la lista de selectores de CSS compatibles con nuestro motor de selección de CSS, nwsapi
.
Más allá de las características a las que aún no hemos llegado, hay dos características principales que actualmente están fuera del alcance de jsdom. Estos son:
Navegación : la capacidad de cambiar el objeto global y todos los demás objetos al hacer clic en un enlace o asignar location.href
o similar.
Diseño : la capacidad de calcular dónde se distribuirán visualmente los elementos como resultado de CSS, lo que afecta a métodos como getBoundingClientRects()
o propiedades como offsetTop
.
Actualmente, jsdom tiene comportamientos ficticios para algunos aspectos de estas características, como enviar un " "jsdomError"
a la consola virtual para la navegación o devolver ceros para muchas propiedades relacionadas con el diseño. A menudo puede solucionar estas limitaciones en su código, por ejemplo, creando nuevas instancias JSDOM
para cada página a la que "navegue" durante un rastreo, o usando Object.defineProperty()
para cambiar lo que devuelven varios métodos y captadores relacionados con el diseño.
Tenga en cuenta que otras herramientas en el mismo espacio, como PhantomJS, admiten estas funciones. En la wiki, tenemos un artículo más completo sobre jsdom vs. PhantomJS.
jsdom es un proyecto impulsado por la comunidad mantenido por un equipo de voluntarios. Podrías apoyar a jsdom mediante:
Obtener soporte profesional para jsdom como parte de una suscripción a Tidelift. Tidelift ayuda a que el código abierto sea sostenible para nosotros y, al mismo tiempo, brinda a los equipos garantías de mantenimiento, licencias y seguridad.
Contribuyendo directamente al proyecto.
Si necesita ayuda con jsdom, no dude en utilizar cualquiera de los siguientes lugares:
La lista de correo (mejor para preguntas de "cómo hago")
El rastreador de problemas (mejor para informes de errores)
La sala Matrix: #jsdom:matrix.org