Precise DOM morphing
morphing typescript dom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add support for textarea inputs

+220 -41
+27 -1
src/morphlex.ts
··· 72 72 element.setAttribute("morphlex-dirty", "") 73 73 } 74 74 } 75 + 76 + for (const element of node.querySelectorAll("textarea")) { 77 + if (element.value !== element.defaultValue) { 78 + element.setAttribute("morphlex-dirty", "") 79 + } 80 + } 75 81 } 76 82 77 83 function parseString(string: string): DocumentFragment { ··· 167 173 this.visitAttributes(pair) 168 174 } 169 175 170 - if (from.hasChildNodes() || to.hasChildNodes()) { 176 + if (isTextAreaElement(from) && isTextAreaElement(to)) { 177 + this.visitTextArea(pair as PairOfMatchingElements<HTMLTextAreaElement>) 178 + } else if (from.hasChildNodes() || to.hasChildNodes()) { 171 179 this.visitChildNodes(pair) 172 180 } 173 181 } ··· 252 260 } 253 261 } 254 262 } 263 + } 264 + 265 + private visitTextArea([from, to]: PairOfMatchingElements<HTMLTextAreaElement>): void { 266 + const newTextContent = to.textContent || "" 267 + const isModified = from.value !== from.defaultValue 268 + 269 + // Update text content (which updates defaultValue) 270 + if (from.textContent !== newTextContent) { 271 + from.textContent = newTextContent 272 + } 273 + 274 + if (this.options.preserveModified && isModified) return 275 + 276 + from.value = from.defaultValue 255 277 } 256 278 257 279 visitChildNodes([from, to]: PairOfMatchingElements<Element>): void { ··· 509 531 510 532 function isOptionElement(element: Element): element is HTMLOptionElement { 511 533 return element.localName === "option" 534 + } 535 + 536 + function isTextAreaElement(element: Element): element is HTMLTextAreaElement { 537 + return element.localName === "textarea" 512 538 } 513 539 514 540 function isParentNode(node: Node): node is ParentNode {
+193
test/inputs.browser.test.ts
··· 1 + import { test, expect, describe } from "vitest" 2 + import { morph } from "../src/morphlex" 3 + 4 + function parseHTML(html: string): HTMLElement { 5 + const tmp = document.createElement("div") 6 + tmp.innerHTML = html.trim() 7 + return tmp.firstChild as HTMLElement 8 + } 9 + 10 + describe("text input", () => { 11 + test("morphing a modified value with preserveModified enabled", () => { 12 + const a = parseHTML(`<input type="text" value="a">`) as HTMLInputElement 13 + const b = parseHTML(`<input type="text" value="b">`) as HTMLInputElement 14 + 15 + a.value = "c" 16 + morph(a, b, { preserveModified: true }) 17 + 18 + expect(a.outerHTML).toBe(`<input type="text" value="b">`) 19 + expect(a.value).toBe("c") 20 + }) 21 + 22 + test("morphing a modified value preserveModified disabled", () => { 23 + const a = parseHTML(`<input type="text" value="a">`) as HTMLInputElement 24 + const b = parseHTML(`<input type="text" value="b">`) as HTMLInputElement 25 + 26 + a.value = "c" 27 + morph(a, b, { preserveModified: false }) 28 + 29 + expect(a.outerHTML).toBe(`<input type="text" value="b">`) 30 + expect(a.value).toBe("b") 31 + }) 32 + 33 + test("morphing an unmodified value with preserveModified enabled", () => { 34 + const a = parseHTML(`<input type="text" value="a">`) as HTMLInputElement 35 + const b = parseHTML(`<input type="text" value="b">`) as HTMLInputElement 36 + 37 + morph(a, b, { preserveModified: true }) 38 + 39 + expect(a.outerHTML).toBe(`<input type="text" value="b">`) 40 + expect(a.value).toBe("b") 41 + }) 42 + }) 43 + 44 + describe("checkbox", () => { 45 + test("morphing a modified checkbox checked with preserveModified enabled", () => { 46 + const a = parseHTML(`<input type="checkbox">`) as HTMLInputElement 47 + const b = parseHTML(`<input type="checkbox" checked>`) as HTMLInputElement 48 + 49 + a.checked = true 50 + morph(a, b, { preserveModified: true }) 51 + 52 + expect(a.hasAttribute("checked")).toBe(true) 53 + expect(a.checked).toBe(true) 54 + }) 55 + 56 + test("morphing a modified checkbox checked with preserveModified disabled", () => { 57 + const a = parseHTML(`<input type="checkbox">`) as HTMLInputElement 58 + const b = parseHTML(`<input type="checkbox" checked>`) as HTMLInputElement 59 + 60 + a.checked = true 61 + morph(a, b, { preserveModified: false }) 62 + 63 + expect(a.hasAttribute("checked")).toBe(true) 64 + expect(a.checked).toBe(true) 65 + }) 66 + 67 + test("morphing an unmodified checkbox with preserveModified enabled", () => { 68 + const a = parseHTML(`<input type="checkbox">`) as HTMLInputElement 69 + const b = parseHTML(`<input type="checkbox" checked>`) as HTMLInputElement 70 + 71 + morph(a, b, { preserveModified: true }) 72 + 73 + expect(a.hasAttribute("checked")).toBe(true) 74 + expect(a.checked).toBe(true) 75 + }) 76 + 77 + test("morphing a modified checkbox unchecked with preserveModified enabled", () => { 78 + const a = parseHTML(`<input type="checkbox" checked>`) as HTMLInputElement 79 + const b = parseHTML(`<input type="checkbox">`) as HTMLInputElement 80 + 81 + a.checked = false 82 + morph(a, b, { preserveModified: true }) 83 + 84 + expect(a.hasAttribute("checked")).toBe(false) 85 + expect(a.checked).toBe(false) 86 + }) 87 + 88 + test("morphing a modified checkbox unchecked with preserveModified disabled", () => { 89 + const a = parseHTML(`<input type="checkbox" checked>`) as HTMLInputElement 90 + const b = parseHTML(`<input type="checkbox">`) as HTMLInputElement 91 + 92 + a.checked = false 93 + morph(a, b, { preserveModified: false }) 94 + 95 + expect(a.hasAttribute("checked")).toBe(false) 96 + expect(a.checked).toBe(false) 97 + }) 98 + }) 99 + 100 + describe("select", () => { 101 + test("morphing a modified select option with preserveModified enabled", () => { 102 + const a = parseHTML(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 103 + const b = parseHTML(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 104 + 105 + a.value = "b" 106 + morph(a, b, { preserveModified: true }) 107 + 108 + expect(a.options[1].hasAttribute("selected")).toBe(true) 109 + expect(a.value).toBe("b") 110 + expect(a.options[1].selected).toBe(true) 111 + }) 112 + 113 + test("morphing a modified select option with preserveModified disabled", () => { 114 + const a = parseHTML(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 115 + const b = parseHTML(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 116 + 117 + a.value = "b" 118 + morph(a, b, { preserveModified: false }) 119 + 120 + expect(a.options[1].hasAttribute("selected")).toBe(true) 121 + expect(a.value).toBe("b") 122 + expect(a.options[1].selected).toBe(true) 123 + }) 124 + 125 + test("morphing an unmodified select option with preserveModified enabled", () => { 126 + const a = parseHTML(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 127 + const b = parseHTML(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 128 + 129 + morph(a, b, { preserveModified: true }) 130 + 131 + expect(a.options[1].hasAttribute("selected")).toBe(true) 132 + expect(a.value).toBe("b") 133 + expect(a.options[1].selected).toBe(true) 134 + }) 135 + 136 + test("morphing a modified select option back to default with preserveModified enabled", () => { 137 + const a = parseHTML(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 138 + const b = parseHTML(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 139 + 140 + a.value = "a" 141 + morph(a, b, { preserveModified: true }) 142 + 143 + expect(a.options[1].hasAttribute("selected")).toBe(false) 144 + expect(a.value).toBe("a") 145 + expect(a.options[0].selected).toBe(true) 146 + }) 147 + 148 + test("morphing a modified select option back to default with preserveModified disabled", () => { 149 + const a = parseHTML(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 150 + const b = parseHTML(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 151 + 152 + a.value = "a" 153 + morph(a, b, { preserveModified: false }) 154 + 155 + expect(a.options[1].hasAttribute("selected")).toBe(false) 156 + expect(a.value).toBe("a") 157 + expect(a.options[0].selected).toBe(true) 158 + }) 159 + }) 160 + 161 + describe("textarea", () => { 162 + test("morphing a modified textarea value with preserveModified enabled", () => { 163 + const a = parseHTML(`<textarea>a</textarea>`) as HTMLTextAreaElement 164 + const b = parseHTML(`<textarea>b</textarea>`) as HTMLTextAreaElement 165 + 166 + a.value = "c" 167 + morph(a, b, { preserveModified: true }) 168 + 169 + expect(a.textContent).toBe("b") 170 + expect(a.value).toBe("c") 171 + }) 172 + 173 + test("morphing a modified textarea value with preserveModified disabled", () => { 174 + const a = parseHTML(`<textarea>a</textarea>`) as HTMLTextAreaElement 175 + const b = parseHTML(`<textarea>b</textarea>`) as HTMLTextAreaElement 176 + 177 + a.value = "c" 178 + morph(a, b, { preserveModified: false }) 179 + 180 + expect(a.textContent).toBe("b") 181 + expect(a.value).toBe("b") 182 + }) 183 + 184 + test("morphing an unmodified textarea value with preserveModified enabled", () => { 185 + const a = parseHTML(`<textarea>a</textarea>`) as HTMLTextAreaElement 186 + const b = parseHTML(`<textarea>b</textarea>`) as HTMLTextAreaElement 187 + 188 + morph(a, b, { preserveModified: true }) 189 + 190 + expect(a.textContent).toBe("b") 191 + expect(a.value).toBe("b") 192 + }) 193 + })
-40
test/new.browser.test.ts
··· 1 - import { test, expect } from "vitest" 2 - import { morph } from "../src/morphlex" 3 - 4 - function parseHTML(html: string): HTMLElement { 5 - const tmp = document.createElement("div") 6 - tmp.innerHTML = html.trim() 7 - return tmp.firstChild as HTMLElement 8 - } 9 - 10 - test("morphing a modified input value with preserveModified enabled", () => { 11 - const a = parseHTML(`<input type="text" value="a">`) as HTMLInputElement 12 - const b = parseHTML(`<input type="text" value="b">`) as HTMLInputElement 13 - 14 - a.value = "new" 15 - morph(a, b, { preserveModified: true }) 16 - 17 - expect(a.outerHTML).toBe(`<input type="text" value="b">`) 18 - expect(a.value).toBe("new") 19 - }) 20 - 21 - test("morphing a modified input value preserveModified disabled", () => { 22 - const a = parseHTML(`<input type="text" value="a">`) as HTMLInputElement 23 - const b = parseHTML(`<input type="text" value="b">`) as HTMLInputElement 24 - 25 - a.value = "new" 26 - morph(a, b, { preserveModified: false }) 27 - 28 - expect(a.outerHTML).toBe(`<input type="text" value="b">`) 29 - expect(a.value).toBe("b") 30 - }) 31 - 32 - test("morphing an unmodified input value with preserveModified enabled", () => { 33 - const a = parseHTML(`<input type="text" value="a">`) as HTMLInputElement 34 - const b = parseHTML(`<input type="text" value="b">`) as HTMLInputElement 35 - 36 - morph(a, b, { preserveModified: true }) 37 - 38 - expect(a.outerHTML).toBe(`<input type="text" value="b">`) 39 - expect(a.value).toBe("b") 40 - })