Parsing splits one text run into several Text nodes at each character reference (`<p>a & b</p>` gives 3 child nodes)
Los mantenedores suelen responder en 5 días
@johnhldiver-beep ya está trabajando en esto.
Desde el 30/10/2026.
- #361 de @johnhldiver-beep — abierto
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 68/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- html, javascript
Línea de trabajo
Empieza con el controlador ontext en esm/shared/parse-from-string.js, que, según indica el issue, añade un nodo Text por cada callback del parser. Compara su comportamiento con el DomHandler de htmlparser2 y luego añade una prueba de regresión para marcado que contenga referencias a caracteres. El trabajo estará terminado cuando al analizar el ejemplo se produzca un nodo Text con los datos "a & b" y la cobertura existente se mantenga en el 100 %.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Parsing <p>a & b</p> gives the <p> three Text nodes ("a ", "&", " b"). The HTML parsing algorithm appends each character to the previous Text node, and jsdom gives one Text node, "a & b". Code that walks childNodes and treats each Text node as one run sees extra runs wherever the source has an entity. normalize() merges them afterwards, but every consumer of parsed output has to know to call it on every subtree, and a spec parser never produces the split in the first place ("insert a character": https://html.spec.whatwg.org/multipage/parsing.html#insert-a-character).
Asks:
- Should the parser merge text into the previous Text node when the previous sibling under the same parent is a Text node, as the HTML parsing algorithm does?
- If yes, I can open a PR that changes
esm/only, with a test, keeping coverage at 100%.
Environment: linkedom 0.18.14 (npm, latest) and main at e2ee208, htmlparser2 10.1.0, Node 24.14.1, macOS.
Test case:
import { DOMParser } from 'linkedom'
const doc = new DOMParser().parseFromString('<!doctype html><html><body><p>a & b</p></body></html>', 'text/html')
const p = doc.querySelector('p')
console.log('textContent:', JSON.stringify(p.textContent))
console.log('childNodes.length:', p.childNodes.length)
console.log('childNodes data:', JSON.stringify([...p.childNodes].map((n) => n.data)))
Output:
textContent: "a & b"
childNodes.length: 3
childNodes data: ["a ","&"," b"]
Expected: childNodes.length: 1, data ["a & b"] (what jsdom 29.1.1 returns for the same markup).
| Input | linkedom Text nodes | jsdom Text nodes |
|---|---|---|
<p>a and b</p> |
1 | 1 |
<p>a & b</p> |
3 | 1 |
<p>x < y > z ©</p> |
6 | 1 |
parseHTML gives the same result as DOMParser (3 nodes for <p>a & b</p>).
Raw output (npm 0.18.14, main e2ee208, variants, htmlparser2)
$ node repro.mjs # linkedom 0.18.14 from npm
textContent: "a & b"
childNodes.length: 3
childNodes data: ["a ","&"," b"]
$ node repro-main.mjs # main e2ee208 esm/
textContent: "a & b"
childNodes.length: 3
childNodes data: ["a ","&"," b"]
$ node variants.mjs
no entity: 1 ["a and b"]
&: 3 ["a ","&"," b"]
< > ©: 6 ["x ","<"," y ",">"," z ","©"]
parseHTML: 3
$ node htmlparser2-ontext.mjs # htmlparser2 10.1.0, decodeEntities: true
htmlparser2 ontext calls: ["a ","&"," b"]
$ node jsdom-control.mjs
jsdom childNodes.length: 1 ["a & b"]
Where it comes from: htmlparser2 with decodeEntities: true calls ontext once per piece around each character reference. The ontext handler in parseFromString appends a new Text node for every call:
htmlparser2's own DomHandler merges each ontext piece into the previous Text node (domhandler 5.0.3, lib/index.js, ontext), so with the same callbacks it yields one node for <p>a & b</p>.
No existing issue found for this; the closest are #62 and #71, which are about serializing entities, not the node split.
- Lenguaje dominante
- HTML
- Estrellas
- 2.1k
- Forks
- 117
- Merge medio
- 4 d 13 h
- PR fusionados (30 d)
- 2
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de WebReflection/linkedom
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
WebReflection/linkedom#365 ·
Los mantenedores suelen responder en 5 días
-
SVG elements in an HTML document report an upper-cased tagName ("PATH" instead of "path")Posiblemente ocupada @jonasporto la tomó hace 9 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
WebReflection/linkedom#344 · 1 comentario ·
Los mantenedores suelen responder en 5 días
-
`TextElement.toString()` puts the element text back at the first `><` of the start tag, so an attribute value containing `><` swallows the text of `<style>`, `<script>`, `<title>` and `<textarea>`Posiblemente ocupada @vsolano9 la tomó hace 22 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
WebReflection/linkedom#341 · 1 comentario ·
Los mantenedores suelen responder en 5 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
WebReflection/linkedom#340 · 1 comentario ·
Los mantenedores suelen responder en 5 días
-
Element#matches() compiles the selector on every callPosiblemente ocupada @jonasporto la tomó hace 22 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 35/100
WebReflection/linkedom#348 · 1 comentario ·
Los mantenedores suelen responder en 5 días
Todos los issues de WebReflection/linkedom
Issues similares
-
`Time.at(t, in: nil)` returns UTC instead of the local time (or the time zone of `t`), unlike CRubyAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
PageParameters.hashCode is inconsistent with equals for named parameters in a different orderAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
bug implicit-state
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
PecanProject/sipnet#435 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100