Skip to content

Commit 87adc04

Browse files
committed
add ignoreValue option to allow morphing without breaking all form inputs and fix bug causing invalid "true" boolean attributes
1 parent dc0a04c commit 87adc04

File tree

2 files changed

+122
-10
lines changed

2 files changed

+122
-10
lines changed

src/idiomorph.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* @property {'outerHTML' | 'innerHTML'} [morphStyle]
2929
* @property {boolean} [ignoreActive]
3030
* @property {boolean} [ignoreActiveValue]
31+
* @property {boolean} [ignoreValue]
3132
* @property {boolean} [restoreFocus]
3233
* @property {ConfigCallbacks} [callbacks]
3334
* @property {ConfigHead} [head]
@@ -69,6 +70,7 @@
6970
* @property {'outerHTML' | 'innerHTML'} morphStyle
7071
* @property {boolean} [ignoreActive]
7172
* @property {boolean} [ignoreActiveValue]
73+
* @property {boolean} [ignoreValue]
7274
* @property {boolean} [restoreFocus]
7375
* @property {ConfigCallbacksInternal} callbacks
7476
* @property {ConfigHeadInternal} head
@@ -106,6 +108,7 @@ var Idiomorph = (function () {
106108
* @property {ConfigInternal['morphStyle']} morphStyle
107109
* @property {ConfigInternal['ignoreActive']} ignoreActive
108110
* @property {ConfigInternal['ignoreActiveValue']} ignoreActiveValue
111+
* @property {ConfigInternal['ignoreValue']} ignoreValue
109112
* @property {ConfigInternal['restoreFocus']} restoreFocus
110113
* @property {Map<Node, Set<string>>} idMap
111114
* @property {Set<string>} persistentIds
@@ -614,7 +617,8 @@ var Idiomorph = (function () {
614617
);
615618
} else {
616619
morphAttributes(oldNode, newContent, ctx);
617-
if (!ignoreValueOfActiveElement(oldNode, ctx)) {
620+
if (!ignoreValueOfActiveElement(oldNode, ctx) &&
621+
(!ctx.ignoreValue || !(oldNode instanceof HTMLTextAreaElement))) {
618622
// @ts-ignore newContent can be a node here because .firstChild will be null
619623
morphChildren(ctx, oldNode, newContent);
620624
}
@@ -666,7 +670,7 @@ var Idiomorph = (function () {
666670
}
667671
}
668672

669-
if (!ignoreValueOfActiveElement(oldElt, ctx)) {
673+
if (!ctx.ignoreValue && !ignoreValueOfActiveElement(oldElt, ctx)) {
670674
syncInputValue(oldElt, newElt, ctx);
671675
}
672676
}
@@ -766,8 +770,7 @@ var Idiomorph = (function () {
766770
}
767771
if (newLiveValue) {
768772
if (!ignoreUpdate) {
769-
// TODO: do we really want this? tests say so but it feels wrong
770-
oldElement.setAttribute(attributeName, newLiveValue);
773+
oldElement.setAttribute(attributeName, '');
771774
}
772775
} else {
773776
if (!ignoreAttribute(attributeName, oldElement, "remove", ctx)) {
@@ -786,9 +789,8 @@ var Idiomorph = (function () {
786789
*/
787790
function ignoreAttribute(attr, element, updateType, ctx) {
788791
if (
789-
attr === "value" &&
790-
ctx.ignoreActiveValue &&
791-
element === document.activeElement
792+
attr === "value" && (ctx.ignoreValue ||
793+
(ctx.ignoreActiveValue && element === document.activeElement))
792794
) {
793795
return true;
794796
}
@@ -974,6 +976,7 @@ var Idiomorph = (function () {
974976
morphStyle: morphStyle,
975977
ignoreActive: mergedConfig.ignoreActive,
976978
ignoreActiveValue: mergedConfig.ignoreActiveValue,
979+
ignoreValue: mergedConfig.ignoreValue,
977980
restoreFocus: mergedConfig.restoreFocus,
978981
idMap: idMap,
979982
persistentIds: persistentIds,

test/core.js

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,51 @@ describe("Core morphing tests", function () {
366366
document.body.removeChild(parent);
367367
});
368368

369+
it("ignore input value change when ignoreValue is true", function () {
370+
let parent = make("<div><input value='foo'></div>");
371+
document.body.append(parent);
372+
373+
let initial = parent.querySelector("input");
374+
375+
// morph
376+
let finalSrc = '<input value="bar">';
377+
Idiomorph.morph(initial, finalSrc, { morphStyle: "outerHTML" });
378+
initial.outerHTML.should.equal('<input value="bar">');
379+
380+
document.activeElement.should.equal(document.body);
381+
382+
let finalSrc2 = '<input class="foo" value="doh">';
383+
Idiomorph.morph(initial, finalSrc2, {
384+
morphStyle: "outerHTML",
385+
ignoreValue: true,
386+
});
387+
initial.value.should.equal("bar");
388+
initial.classList.value.should.equal("foo");
389+
390+
document.body.removeChild(parent);
391+
});
392+
393+
it("ignores textarea value when ignoreValue is true", function () {
394+
let parent = make("<div><textarea>foo</textarea></div>");
395+
document.body.append(parent);
396+
let initial = parent.querySelector("textarea");
397+
398+
let finalSrc = "<textarea>bar</textarea>";
399+
Idiomorph.morph(initial, finalSrc, { morphStyle: "outerHTML" });
400+
initial.outerHTML.should.equal("<textarea>bar</textarea>");
401+
402+
document.activeElement.should.equal(document.body);
403+
404+
let finalSrc2 = '<textarea class="foo">doh</textarea>';
405+
Idiomorph.morph(initial, finalSrc2, {
406+
morphStyle: "outerHTML",
407+
ignoreValue: true,
408+
});
409+
initial.outerHTML.should.equal('<textarea class="foo">bar</textarea>');
410+
411+
document.body.removeChild(parent);
412+
});
413+
369414
it("can morph input value properly because value property is special and doesnt reflect", function () {
370415
let initial = make('<div><input value="foo"></div>');
371416
let final = make('<input value="foo">');
@@ -419,6 +464,70 @@ describe("Core morphing tests", function () {
419464
document.body.removeChild(parent);
420465
});
421466

467+
it("morph does not remove input checked with ignoreValue set", function () {
468+
let parent = make('<div><input type="checkbox"></div>');
469+
document.body.append(parent);
470+
let initial = parent.querySelector("input");
471+
initial.checked = true;
472+
473+
let finalSrc = '<input type="checkbox">';
474+
Idiomorph.morph(initial, finalSrc, { morphStyle: "outerHTML", ignoreValue: true });
475+
initial.outerHTML.should.equal('<input type="checkbox">');
476+
initial.checked.should.equal(true);
477+
document.body.removeChild(parent);
478+
});
479+
480+
it("morph does not reset input checked with ignoreValue set", function () {
481+
let parent = make('<div><input type="checkbox" checked></div>');
482+
document.body.append(parent);
483+
let initial = parent.querySelector("input");
484+
initial.checked = false;
485+
486+
let finalSrc = '<input type="checkbox" checked>';
487+
Idiomorph.morph(initial, finalSrc, { morphStyle: "outerHTML", ignoreValue: true });
488+
initial.outerHTML.should.equal('<input type="checkbox" checked="">');
489+
initial.checked.should.equal(false);
490+
document.body.removeChild(parent);
491+
});
492+
493+
it("morph does still set input checked when checked attribute added even with ignoreValue set", function () {
494+
let parent = make('<div><input type="checkbox" checked></div>');
495+
document.body.append(parent);
496+
let initial = parent.querySelector("input");
497+
498+
let MiddleSrc = '<input type="checkbox">';
499+
Idiomorph.morph(initial, MiddleSrc, { morphStyle: "outerHTML"});
500+
501+
initial.outerHTML.should.equal('<input type="checkbox">');
502+
initial.checked.should.equal(false);
503+
504+
let finalSrc = '<input type="checkbox" checked>';
505+
506+
Idiomorph.morph(initial, finalSrc, { morphStyle: "outerHTML", ignoreValue: true});
507+
initial.outerHTML.should.equal('<input type="checkbox" checked="">');
508+
initial.checked.should.equal(true);
509+
document.body.removeChild(parent);
510+
});
511+
512+
it("morph does still remove input checked when checked attribute removed even with ignoreValue set", function () {
513+
let parent = make('<div><input type="checkbox"></div>');
514+
document.body.append(parent);
515+
let initial = parent.querySelector("input");
516+
517+
let MiddleSrc = '<input type="checkbox" checked>';
518+
Idiomorph.morph(initial, MiddleSrc, { morphStyle: "outerHTML"});
519+
520+
initial.outerHTML.should.equal('<input type="checkbox" checked="">');
521+
initial.checked.should.equal(true);
522+
523+
let finalSrc = '<input type="checkbox">';
524+
525+
Idiomorph.morph(initial, finalSrc, { morphStyle: "outerHTML", ignoreValue: true});
526+
initial.outerHTML.should.equal('<input type="checkbox">');
527+
initial.checked.should.equal(false);
528+
document.body.removeChild(parent);
529+
});
530+
422531
it("can morph input checked properly, set checked property to true", function () {
423532
let parent = make('<div><input type="checkbox" checked></div>');
424533
document.body.append(parent);
@@ -427,7 +536,7 @@ describe("Core morphing tests", function () {
427536

428537
let finalSrc = '<input type="checkbox" checked>';
429538
Idiomorph.morph(initial, finalSrc, { morphStyle: "outerHTML" });
430-
initial.outerHTML.should.equal('<input type="checkbox" checked="true">');
539+
initial.outerHTML.should.equal('<input type="checkbox" checked="">');
431540
initial.checked.should.equal(true);
432541
document.body.removeChild(parent);
433542
});
@@ -474,7 +583,7 @@ describe("Core morphing tests", function () {
474583
// is this a problem at all?
475584
parent.innerHTML.should.equal(`
476585
<select>
477-
<option selected="true">0</option>
586+
<option selected="">0</option>
478587
<option>1</option>
479588
</select>
480589
`);
@@ -506,7 +615,7 @@ describe("Core morphing tests", function () {
506615
let finalSrc = `
507616
<select>
508617
<option>0</option>
509-
<option selected="true">1</option>
618+
<option selected="">1</option>
510619
</select>
511620
`;
512621
Idiomorph.morph(parent, finalSrc, { morphStyle: "innerHTML" });

0 commit comments

Comments
 (0)