···185 }
186187 private visitAttributes([from, to]: PairOfMatchingElements<Element>): void {
188- const fromAttrs = from.attributes
00189190 // First pass: update/add attributes from reference (iterate forwards)
191 for (const { name, value } of to.attributes) {
192- console.log(from, name)
193- const oldValue = from.getAttribute(name)
194-195- // This attribute was only added to trigger attribute morphing.
196- if (name === "morphlex-dirty") {
197- from.removeAttribute(name)
198- continue
199- }
200-201 if (name === "value") {
202 if (isInputElement(from) && from.value !== value) {
203 if (!this.options.preserveModified || from.value === from.defaultValue) {
···222 }
223 }
22400225 if (oldValue !== value && (this.options.beforeAttributeUpdated?.(from, name, value) ?? true)) {
226 from.setAttribute(name, value)
227 this.options.afterAttributeUpdated?.(from, name, oldValue)
228 }
229 }
00230231 // Second pass: remove excess attributes (iterate backwards for efficiency)
232 for (let i = fromAttrs.length - 1; i >= 0; i--) {
···185 }
186187 private visitAttributes([from, to]: PairOfMatchingElements<Element>): void {
188+ if (from.hasAttribute("morphlex-dirty")) {
189+ from.removeAttribute("morphlex-dirty")
190+ }
191192 // First pass: update/add attributes from reference (iterate forwards)
193 for (const { name, value } of to.attributes) {
000000000194 if (name === "value") {
195 if (isInputElement(from) && from.value !== value) {
196 if (!this.options.preserveModified || from.value === from.defaultValue) {
···215 }
216 }
217218+ const oldValue = from.getAttribute(name)
219+220 if (oldValue !== value && (this.options.beforeAttributeUpdated?.(from, name, value) ?? true)) {
221 from.setAttribute(name, value)
222 this.options.afterAttributeUpdated?.(from, name, oldValue)
223 }
224 }
225+226+ const fromAttrs = from.attributes
227228 // Second pass: remove excess attributes (iterate backwards for efficiency)
229 for (let i = fromAttrs.length - 1; i >= 0; i--) {
+5-5
test/morphlex-morphdom.test.ts
···115116 morph(from, to)
117118- // Input values are no longer updated by morphlex
119- expect((from as HTMLInputElement).value).toBe("Hello")
120 })
121122 it("should add disabled attribute to input", () => {
···182183 morph(from, to)
184185- // Selected options are no longer updated by morphlex
186 const select = from as HTMLSelectElement
187- expect(select.value).toBe("1")
188- expect(select.options[1].selected).toBe(false)
189 })
190 })
191
···115116 morph(from, to)
117118+ // Input values are updated by default when not modified
119+ expect((from as HTMLInputElement).value).toBe("World")
120 })
121122 it("should add disabled attribute to input", () => {
···182183 morph(from, to)
184185+ // Selected options are updated by default when not modified
186 const select = from as HTMLSelectElement
187+ expect(select.value).toBe("2")
188+ expect(select.options[1].selected).toBe(true)
189 })
190 })
191
+5-4
test/morphlex.browser.test.ts
···601602 morph(select, referenceSelect)
603604- // Selected attributes are no longer updated
605- expect(select.selectedOptions.length).toBe(1)
606 expect(select.selectedOptions[0].value).toBe("2")
0607 })
608609 it("should handle script tags safely", () => {
···688 morph(form, referenceForm)
689690 const radioB = form.querySelector("#radio-b") as HTMLInputElement
691- // Checked attribute is removed but not added - radioA loses its checked state
692 expect(radioA.checked).toBe(false)
693- expect(radioB.checked).toBe(false)
694 })
695696 it("should handle contenteditable elements", () => {
···601602 morph(select, referenceSelect)
603604+ // Selected attributes are updated by default when not modified
605+ expect(select.selectedOptions.length).toBe(2)
606 expect(select.selectedOptions[0].value).toBe("2")
607+ expect(select.selectedOptions[1].value).toBe("3")
608 })
609610 it("should handle script tags safely", () => {
···689 morph(form, referenceForm)
690691 const radioB = form.querySelector("#radio-b") as HTMLInputElement
692+ // Checked attributes are updated by default when not modified
693 expect(radioA.checked).toBe(false)
694+ expect(radioB.checked).toBe(true)
695 })
696697 it("should handle contenteditable elements", () => {
+40
test/new.browser.test.ts
···0000000000000000000000000000000000000000
···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+})