on master help sign in
Fix a bug where "volatile" attributes weren't getting properly updated
5837d53Nick Fitzgerald
js/change-list.js+28−2
@@ -49,7 +49,20 @@ const OP_TABLE = [
49 const pointer2 = mem32[i++];
49 const pointer2 = mem32[i++];
50 const length2 = mem32[i++];
50 const length2 = mem32[i++];
51 const value = string(mem8, pointer2, length2);
51 const value = string(mem8, pointer2, length2);
52+ const node = top(changeList.stack);
52− top(changeList.stack).setAttribute(name, value);
53+ node.setAttribute(name, value);
54+
55+ // Some attributes are "volatile" and don't work through `setAttribute`.
56+ if (name === "value") {
57+ node.value = value;
58+ }
59+ if (name === "checked") {
60+ node.checked = true;
61+ }
62+ if (name === "selected") {
63+ node.selected = true;
64+ }
65+
53 return i;
66 return i;
54 },
67 },
55
68
@@ -58,7 +71,20 @@ const OP_TABLE = [
58 const pointer = mem32[i++];
71 const pointer = mem32[i++];
59 const length = mem32[i++];
72 const length = mem32[i++];
60 const name = string(mem8, pointer, length);
73 const name = string(mem8, pointer, length);
74+ const node = top(changeList.stack);
61− top(changeList.stack).removeAttribute(name);
75+ node.removeAttribute(name);
76+
77+ // Some attributes are "volatile" and don't work through `removeAttribute`.
78+ if (name === "value") {
79+ node.value = null;
80+ }
81+ if (name === "checked") {
82+ node.checked = false;
83+ }
84+ if (name === "selected") {
85+ node.selected = false;
86+ }
87+
62 return i;
88 return i;
63 },
89 },
64
90
src/node.rs+14
@@ -70,6 +70,20 @@ impl fmt::Debug for Listener<'_> {
70 }
70 }
71}
71}
72
72
73+impl<'a> Attribute<'a> {
74+ /// Certain attributes are considered "volatile" and can change via user
75+ /// input that we can't see when diffing against the old virtual DOM. For
76+ /// these attributes, we want to always re-set the attribute on the physical
77+ /// DOM node, even if the old and new virtual DOM nodes have the same value.
78+ #[inline]
79+ pub(crate) fn is_volatile(&self) -> bool {
80+ match self.name {
81+ "value" | "checked" | "selected" => true,
82+ _ => false,
83+ }
84+ }
85+}
86+
73impl<'a> Node<'a> {
87impl<'a> Node<'a> {
74 /// Construct a new element node with the given tag name and children.
88 /// Construct a new element node with the given tag name and children.
75 #[inline]
89 #[inline]
src/vdom.rs+13−8
@@ -364,17 +364,22 @@ impl VdomInnerExclusive {
364 // Do O(n^2) passes to add/update and remove attributes, since
364 // Do O(n^2) passes to add/update and remove attributes, since
365 // there are almost always very few attributes.
365 // there are almost always very few attributes.
366 'outer: for new_attr in new {
366 'outer: for new_attr in new {
367+ if new_attr.is_volatile() {
368+ self.change_list
369+ .emit_set_attribute(new_attr.name, new_attr.value);
370+ } else {
367− for old_attr in old {
371+ for old_attr in old {
368− if old_attr.name == new_attr.name {
372+ if old_attr.name == new_attr.name {
369− if old_attr.value != new_attr.value {
373+ if old_attr.value != new_attr.value {
370− self.change_list
374+ self.change_list
371− .emit_set_attribute(new_attr.name, new_attr.value);
375+ .emit_set_attribute(new_attr.name, new_attr.value);
376+ }
377+ continue 'outer;
372 }
378 }
373− continue 'outer;
374 }
379 }
380+ self.change_list
381+ .emit_set_attribute(new_attr.name, new_attr.value);
375 }
382 }
376− self.change_list
377− .emit_set_attribute(new_attr.name, new_attr.value);
378 }
383 }
379
384
380 'outer2: for old_attr in old {
385 'outer2: for old_attr in old {