Parsing splits one text run into several Text nodes at each character reference (`<p>a &amp; b</p>` gives 3 child nodes)

オープン 初心者向け
#353 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 5 日以内に返信

@johnhldiver-beep がすでに取り組んでいます。

2026年10月30日 から。

  • #361 @johnhldiver-beep による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
68/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
活発
技術スタック
html, javascript
領域
backend, web-dev

調査の方向性

esm/shared/parse-from-string.js の ontext ハンドラーから始めます。issue によると、このハンドラーはパーサーのコールバックごとに Text ノードを追加します。その動作を htmlparser2 の DomHandler と比較し、文字参照を含むマークアップの回帰テストを追加します。例を解析したときにデータが "a & b" の Text ノードが 1 つ生成され、既存のカバレッジが 100% のままであれば完了です。

索引モデルが issue の本文から書いたものです。

説明

Parsing <p>a &amp; 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:

  1. 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?
  2. 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 &amp; 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 &amp; b</p> 3 1
<p>x &lt; y &gt; z &#169;</p> 6 1

parseHTML gives the same result as DOMParser (3 nodes for <p>a &amp; 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"]
&amp;:           3 ["a ","&"," b"]
&lt; &gt; &#169;: 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:

https://github.com/WebReflection/linkedom/blob/e2ee20813e4b3ae946d7ba41621ddf3698431989/esm/shared/parse-from-string.js#L92-L98

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 &amp; b</p>.

No existing issue found for this; the closest are #62 and #71, which are about serializing entities, not the node split.

主要言語
HTML
スター
2.1k
フォーク
117
平均マージ
4日 13時間
マージ済み PR(30日)
2

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

WebReflection/linkedom のほかの issue

WebReflection/linkedom の issue をすべて見る

似ている issue

Backend & API Design の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。