DOMを改行で分割する
code:splitDOM.ts
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
export function* splitDOM(element: Node): Generator<Node, void, unknown> {
if (!element.textContent.includes("\n")) {
yield element;
return;
}
if (!(element instanceof Element) &&
!(element instanceof Document) &&
!(element instanceof DocumentFragment)) {
yield* element.textContent.split("\n").map((text) => document.createTextNode(text));
}
let prev: Node | undefined;
for (const node of element.childNodes) {
let last: Node | undefined;
for (const splitted of splitDOM(node)) {
if (prev) {
prev.appendChild(splitted);
yield prev;
prev = undefined;
continue;
}
const dom = document.createElement(element.tagName);
for (const attr of element.attributes) {
dom.attributes.setNamedItem(attr);
}
dom.appendChild(node);
last = dom;
yield dom;
}
prev = last;
}
if (prev) yield prev;
}
だいぶ複雑だ
簡単なケースから考える
<node>text</node>の場合
code:js
if (!element.textContent.includes("\n")) return element;
return element.textContent.split("\n").map((line) => {
const newElement = document.createElement(element.tagName);
newElement.textContent = line;
return newElement;
});
<node>text<node>text</node></node>の場合
code:test.ts
import { DOMParser, nodesFromString } from "https://deno.land/x/deno_dom@v0.1.36-alpha/deno-dom-wasm.ts";
import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts";
globalThis.document = new DOMParser().parseFromString(
`<!DOCTYPE html>
<html>
<head>
<title>Hello from Deno</title>
</head>
<body></body>
</html>`,
"text/html",
);
await t.step("改行なし", async (t) => {
const cases = [
html<div></div>,
htmltext,
html<div>text</div>,
html<div class="sample" id="test">text</div>,
html<div><span>text</span></div>,
html<div>てきすと<span>text</span></div>,
html<div><span>text</span>テキスト</div>,
html<div>てきすと<span>text</span>テキスト</div>,
];
for (const case of cases) {
await t.step(case, () => assertEquals(...splitDOM(nodesFromString(case))0.outerHTML, case));
}
});
await t.step("改行あり", async (t) => {
const cases = [
htmlline1\nline2,
html<div>line1\nline2</div>,
html<div>line1\n\nline2</div>,
html<div>\nline1\nline2</div>,
html<div>line1\nline2\n</div>,
html<div>line1\nline2\nline3</div>,
html<div class="sample" id="test">text</div>,
html<div><span>text</span></div>,
html<div>てきすと<span>text</span></div>,
html<div><span>text</span>テキスト</div>,
html<div>てきすと<span>text</span>テキスト</div>,
];
for (const before, after of cases) {
await t.step(before.replaceAll("\n", "\\n"), () => assertEquals(...splitDOM(nodesFromString(before)), after));
}
});
#2023-02-17 06:06:49
#2023-02-11 10:12:49
#2023-02-10 17:04:18