Jsdom: implementación de texto interno

Creado en 25 sept. 2015  ·  26Comentarios  ·  Fuente: jsdom/jsdom

jsdom es una gran herramienta para web scraping. Sin embargo, el textContent es una forma muy inconveniente de obtener texto legible para la conversión de html2text.

Hay un artículo maravilloso sobre la utilidad de innerText insignificantes en muchos casos:

http://perfectionkills.com/the-poor-misunderstood-innerText/

El autor sugiere getSelection().toString() como una solución alternativa muy lenta, pero getSelection no está implementado en jsdom .

¿Podría considerar una implementación de innerText en el jsdom ? El autor ha hecho una gran exploración al respecto, incluso ha agregado una especificación simple al final.

feature layout

Comentario más útil

En caso de que alguien más se encuentre con este problema, lo di un paso más y usé el paquete sanitize-html para obtener básicamente lo que está haciendo el navegador (tenga en cuenta que no importé la configuración de JSDOM porque descubrí que no era necesario al poner esto en mi archivo de configuración de Jest, pero si no está usando Jest, querrá usar la configuración de global.Element = (new JSDOM()).window.Element que @bennypowers recomendó):

Object.defineProperty(global.Element.prototype, 'innerText', {
  get() {
    return sanitizeHtml(this.textContent, {
      allowedTags: [], // remove all tags and return text content only
      allowedAttributes: {}, // remove all tags and return text content only
    });
  },
  configurable: true, // make it so that it doesn't blow chunks on re-running tests with things like --watch
});

Todos 26 comentarios

Y qué pena que la biblioteca Selection y innerText no sea compatible con jsdom : https://github.com/timdown/rangy/issues/348

Por lo tanto, innerText no es estándar y no está implementado en al menos un motor importante (Firefox). Sin un estándar, no creo que debamos implementarlo.

Parece que hay algo de movimiento en todo esto con un borrador de especificación aquí . Ver también todas las referencias . Sin embargo, no hay problemas en el repositorio, así que me pregunto qué tan completo ya está / qué tan rápido será el progreso.

Según la especificación, parece que no podemos implementar innerText correctamente sin soporte de diseño básico.

Sí, esto realmente no se va a implementar en jsdom de todos modos, sin mucho trabajo de infraestructura... nadie se hace ilusiones :(.

En cuanto al requisito de soporte de diseño: https://github.com/rocallahan/innerText-spec/issues/2

¿Hay algún plan para implementarlo debido a la adopción de WHATWG?

Sí... Aunque la especificación requiere muchas cosas que jsdom no tiene, alrededor de cuadros CSS :(. No estoy seguro de qué hacer.

¿Hay alguna biblioteca para que esto se conecte junto con jsdom?

@domenic se preocupa por dejar algunos conocimientos sobre por qué se trata de una revisión de infraestructura de este tipo. Pensamos que el gorila de 800 lb en la habitación se iría bajo llave. Pero parece que no va a ninguna parte. Como saben, he estado envolviendo mi cabeza en las entrañas de jsdom. ¿Dónde sería un buen lugar en el repositorio para comenzar a revisar el código para un jsdom newb?

Gracias de antemano 🙏 /cc @vsemozhetbyt

El problema principal es el hecho de que innerText apoya en el motor de diseño como guía, y jsdom no tiene motor de diseño. Consulte https://html.spec.whatwg.org/multipage/dom.html#the -innertext-idl-attribute y
http://perfectionkills.com/the-poor-misunderstood-innerText/ . Del segundo enlace:

Observe cómo innerText representa casi con precisión cómo aparece el texto en la página. textContent, por otro lado, hace algo extraño: ignora las nuevas líneas creadas por
y alrededor de elementos con estilo como bloque ( en este caso).

¿Aún está fuera del alcance y no hay una solución alternativa?

Aparentemente, la especificación dice:

Si este elemento no se representa, o si el agente de usuario no es un agente de usuario CSS, [énfasis agregado] devuelve el mismo valor que el atributo IDL textContent en este elemento.

Creo que una solución alternativa sería simplemente devolver textContent .

Implementamos suficiente CSS que no creo que se aplique. Simplemente no implementamos las partes del diseño...

Hola chicos, alguna noticia sobre esto?

Solo usa cromo sin cabeza :)

@domenic de esa especificación que mencionó @coreh :
https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute

Si este elemento no se representa , o si el agente de usuario no es un agente de usuario CSS, devuelva el mismo valor que el atributo IDL textContent en este elemento.

https://html.spec.whatwg.org/multipage/rendering.html# being -rendering

Un elemento se representa si tiene cuadros de diseño CSS asociados, cuadros de diseño SVG o algún equivalente en otros lenguajes de estilo.

Si jsdom no implementa las partes del diseño, ¿no significa que se aplica "no se representa"?

Este mensaje es para cualquiera que llegue a este hilo de github que solo quiere una forma de aprobar sus pruebas sin cambiar las implementaciones de sus funciones.

copypasta para la parte superior de sus archivos de prueba:

// Expose JSDOM Element constructor
global.Element = (new JSDOM()).window.Element;
// 'Implement' innerText in JSDOM: https://github.com/jsdom/jsdom/issues/1245
Object.defineProperty(global.Element.prototype, 'innerText', {
  get() {
    return this.textContent;
  },
});

Naturalmente, se aplican las advertencias de la discusión anterior.

En caso de que alguien más se encuentre con este problema, lo di un paso más y usé el paquete sanitize-html para obtener básicamente lo que está haciendo el navegador (tenga en cuenta que no importé la configuración de JSDOM porque descubrí que no era necesario al poner esto en mi archivo de configuración de Jest, pero si no está usando Jest, querrá usar la configuración de global.Element = (new JSDOM()).window.Element que @bennypowers recomendó):

Object.defineProperty(global.Element.prototype, 'innerText', {
  get() {
    return sanitizeHtml(this.textContent, {
      allowedTags: [], // remove all tags and return text content only
      allowedAttributes: {}, // remove all tags and return text content only
    });
  },
  configurable: true, // make it so that it doesn't blow chunks on re-running tests with things like --watch
});

tenía una necesidad similar, pero quería ir un poco más allá que solo usar textContent ; nuevamente, esta no será una representación precisa de lo que realmente hacen los navegadores, especialmente con respecto a los elementos ocultos por css, pero es bueno suficiente para mi caso de uso:

function innerText(el)
  el = el.cloneNode(true) // can skip if mutability isn't a concern
  el.querySelectorAll('script,style').forEach(s => s.remove())
  return el.textContent
}

¡Qué pena!

Aparentemente, la especificación dice:

Si este elemento no se está representando, o si el agente de usuario no es un agente de usuario CSS , [énfasis agregado] devuelve el mismo valor que el atributo IDL textContent en este elemento.

Creo que una solución alternativa sería simplemente devolver textContent.

Implementamos suficiente CSS que no creo que se aplique. Simplemente no implementamos las partes del diseño...

@domenic , considere una interpretación más liberal de la especificación

textContent se permite explícitamente como respaldo, cuando la aplicación de reglas CSS es demasiado costosa

además, texto interno se especifica como getter y setter

Dado que soy el editor de especificaciones, puedo afirmar con certeza que "cuando la aplicación de las reglas CSS es demasiado costosa" no es lo que dice la especificación.

.. esa fue mi interpretación de "si el agente de usuario no es un agente de usuario CSS"

¿Cuál es la diferencia entre un "agente de usuario de CSS" y un "agente de usuario sin CSS"?

qué pasa:
un agente de usuario de CSS puede "aplicar reglas de CSS" y mostrar el resultado (gráfico o textual)
un agente de usuario que no es CSS es demasiado tonto para "aplicar reglas CSS"

Implementamos suficiente CSS que no creo que se aplique.

¿Qué quieres decir? ventana.getComputedStyle?

una alternativa a textContent sigue siendo mejor que no implementar una interfaz estándar

Tal vez podamos usar el textContent para reemplazar el resultado de innerText mientras ejecutamos las pruebas con jsdom . Por ejemplo:

describe('mytest', () => {
  beforeAll(() => {
    Object.defineProperty(HTMLElement.prototype, 'innerText', {
      get() {
        return this.textContent;
      }
    });
  });
  it('should ok', () => {
  // test assertions
  });
});
¿Fue útil esta página
0 / 5 - 0 calificaciones