jsdom ist ein großartiges Tool zum Web-Scraping. Allerdings ist textContent
ein sehr unpraktischer Weg, um lesbaren Text für die html2Text-Konvertierung zu erhalten.
Es gibt einen wunderbaren Artikel über die Nützlichkeit von vernachlässigbaren innerText
in vielen Fällen:
http://perfectionkills.com/the-poor-misunderstood-innerText/
Der Autor schlägt getSelection().toString()
als sehr langsamen Workaround vor, aber getSelection
ist noch nicht in jsdom implementiert.
Könnten Sie eine Implementierung von innerText
im jsdom in
Und wie schade, dass die ausgeklügelte Selection
und innerText
Bibliothek nicht mit jsdom kompatibel https://github.com/timdown/rangy/issues/348
InnerText ist also kein Standard und nicht in mindestens einer wichtigen Engine (Firefox) implementiert. Ohne einen Standard sollten wir ihn meiner Meinung nach nicht implementieren.
Sieht so aus, als ob die ganze Sache mit einer Entwurfsspezifikation hier etwas in Bewegung Referenzen . Es gibt jedoch keine Probleme mit dem Repo, daher frage ich mich, wie vollständig es bereits ist / wie schnell der Fortschritt sein wird.
Firefox hat implementiert: https://bugzilla.mozilla.org/show_bug.cgi?id=264412
WHATWG scheint zu genehmigen: https://github.com/whatwg/compat/issues/5#issuecomment -168049752
Aus der Spezifikation geht hervor, dass wir innerText
ohne grundlegende Layoutunterstützung nicht richtig implementieren können.
Ja, das wird in jsdom sowieso nicht wirklich umsetzbar sein, ohne viel Infrastrukturarbeit... niemand macht sich Hoffnungen :(.
Was die Anforderungen an die Layoutunterstützung betrifft: https://github.com/rocallahan/innerText-spec/issues/2
Gibt es einen Plan, dies aufgrund der Einführung der WHATWG zu implementieren?
Ja ... Obwohl die Spezifikation eine Menge Dinge erfordert, die jsdom nicht hat, um CSS-Boxen :(. Nicht sicher, was zu tun ist.
Gibt es eine Bibliothek dafür, die mit jsdom verbunden werden kann?
@domenic möchte ich wissen, warum dies eine solche Infrastrukturüberholung ist? Wir dachten, der 800 Pfund schwere Gorilla im Zimmer würde den Schlüssel verlassen. Aber es sieht so aus, als ob es nirgendwo hinführt. Wie Sie wissen, habe ich meinen Kopf in die Innereien von jsdom gewickelt. Wo wäre ein großartiger Ort im Repository, um mit der Überprüfung von Code für einen jsdom-Neuling zu beginnen?
Vielen Dank im Voraus 🙏 /cc @vsemozhetbyt
Das Hauptproblem ist die Tatsache, dass sich innerText
zur Orientierung auf die Layout-Engine stützt und jsdom keine Layout-Engine hat. Siehe https://html.spec.whatwg.org/multipage/dom.html#the -innertext-idl-attribute und
http://perfectionkills.com/the-poor-misunderstood-innerText/ . Aus dem zweiten Link:
Beachten Sie, dass innerText fast genau die Darstellung von Text auf der Seite darstellt. textContent hingegen macht etwas Seltsames – es ignoriert Zeilenumbrüche, die von . erstellt wurden
und um als Block gestylte Elemente ( in diesem Fall).
Immer noch außerhalb des Geltungsbereichs und kein Workaround?
Offenbar sagt die Spezifikation:
Wenn dieses Element nicht gerendert wird oder wenn der Benutzeragent ein Nicht-CSS-Benutzeragent ist, gibt [Hervorhebung hinzugefügt] denselben Wert wie das textContent-IDL-Attribut für dieses Element zurück.
Ich denke, ein Workaround wäre, einfach textContent
.
Wir implementieren genug CSS, das meiner Meinung nach nicht zutrifft. Wir implementieren nur die Layout-Teile nicht...
Hallo Leute, gibt es hierzu Neuigkeiten?
Verwenden Sie einfach kopfloses Chrom :)
@domenic aus dieser Spezifikation, die @coreh erwähnt hat:
https://html.spec.whatwg.org/multipage/dom.html#the -innertext-idl-attribute
Wenn dieses Element nicht gerendert wird oder wenn der Benutzeragent ein Nicht-CSS-Benutzeragent ist, geben Sie denselben Wert wie das
textContent
IDL-Attribut für dieses Element zurück.
https://html.spec.whatwg.org/multipage/rendering.html#being -rendered
Ein Element wird gerendert, wenn es über verknüpfte CSS-Layout-Boxen, SVG-Layout-Boxen oder ein Äquivalent in anderen Stilsprachen verfügt.
Wenn jsdom
Layoutteile nicht implementiert, bedeutet das nicht, dass "nicht gerendert" zutrifft?
Diese Nachricht richtet sich an alle, die diesen Github-Thread erreichen und nur eine Möglichkeit suchen, ihre Tests erfolgreich zu machen, ohne ihre Funktionsimplementierungen zu ändern.
Copypasta für den Anfang Ihrer Testdateien:
// 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;
},
});
Natürlich gelten die Vorbehalte aus der obigen Diskussion.
Für den Fall, dass noch jemand auf dieses Problem stößt, bin ich noch einen Schritt weiter gegangen und habe das Paket sanitize-html
, um im Wesentlichen zu erfahren, was der Browser tut (beachten Sie, dass ich das JSDOM-Setup nicht importiert habe, da ich festgestellt habe, dass es nicht benötigt wird Wenn Sie dies in meine Jest-Setup-Datei einfügen, aber wenn Sie Jest nicht verwenden, sollten Sie das global.Element = (new JSDOM()).window.Element
Setup verwenden, das @bennypowers empfohlen hat):
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
});
Ich hatte ein ähnliches Bedürfnis, wollte aber etwas weiter gehen, als nur textContent
- auch hier wird dies keine genaue Darstellung dessen sein, was Browser tatsächlich tun, insbesondere in Bezug auf Elemente, die von CSS ausgeblendet werden, aber es ist gut genug für meinen Anwendungsfall:
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
}
Was für eine Schande!
Offenbar sagt die Spezifikation:
Wenn dieses Element nicht gerendert wird oder wenn der Benutzeragent ein Nicht-CSS-Benutzeragent ist , gibt [Hervorhebung hinzugefügt] denselben Wert wie das textContent-IDL-Attribut für dieses Element zurück.
Ich denke, ein Workaround wäre dann, einfach textContent zurückzugeben.
Wir implementieren genug CSS, das meiner Meinung nach nicht zutrifft. Wir implementieren nur die Layout-Teile nicht...
@domenic bitte erwägen Sie eine liberalere Interpretation der Spezifikation
textContent
ist explizit als Fallback erlaubt, wenn die Anwendung von CSS-Regeln zu teuer ist
außerdem wird innerText als getter und setter angegeben
Da ich der Spezifikationseditor bin, kann ich mit Sicherheit sagen, dass "wenn die Anwendung von CSS-Regeln zu teuer ist" nicht das ist, was die Spezifikation sagt.
.. das war meine Interpretation von "wenn der Benutzeragent ein Nicht-CSS-Benutzeragent ist"
Was ist der Unterschied zwischen einem "CSS-Benutzeragenten" und einem "Nicht-CSS-Benutzeragenten"?
wie wäre es mit:
ein CSS-Benutzeragent kann "CSS-Regeln anwenden" und das Ergebnis ausgeben (grafisch oder textuell)
ein Nicht-CSS-Benutzeragent ist zu dumm, um "CSS-Regeln anzuwenden"
Wir implementieren genug CSS, das meiner Meinung nach nicht zutrifft.
Was meinen Sie? window.getComputedStyle?
ein Fallback auf textContent ist immer noch besser, als keine Standardschnittstelle zu implementieren
Vielleicht können wir einfach den textContent
verwenden, um das Ergebnis von innerText
beim Ausführen von Tests durch jsdom
zu ersetzen. Beispielsweise:
describe('mytest', () => {
beforeAll(() => {
Object.defineProperty(HTMLElement.prototype, 'innerText', {
get() {
return this.textContent;
}
});
});
it('should ok', () => {
// test assertions
});
});
Hilfreichster Kommentar
Für den Fall, dass noch jemand auf dieses Problem stößt, bin ich noch einen Schritt weiter gegangen und habe das Paket
sanitize-html
, um im Wesentlichen zu erfahren, was der Browser tut (beachten Sie, dass ich das JSDOM-Setup nicht importiert habe, da ich festgestellt habe, dass es nicht benötigt wird Wenn Sie dies in meine Jest-Setup-Datei einfügen, aber wenn Sie Jest nicht verwenden, sollten Sie dasglobal.Element = (new JSDOM()).window.Element
Setup verwenden, das @bennypowers empfohlen hat):