src/morph.js
function updateInput(newNode, oldNode) {
	const newValue = newNode.value;
	const oldValue = oldNode.value;

	updateAttribute(newNode, oldNode, 'checked');
	updateAttribute(newNode, oldNode, 'disabled');

	if (newValue !== oldValue) {
		oldNode.setAttribute('value', newValue);
		oldNode.value = newValue;
	}

	if (newValue === 'null') {
		oldNode.value = '';
		oldNode.removeAttribute('value');
	}

	if (!newNode.hasAttributeNS(null, 'value')) {
		oldNode.removeAttribute('value');
	} else if (oldNode.type === 'range') {
		// this is so elements like slider move their UI thingy
		oldNode.value = newValue;
	}
}
function updateInput(newNode, oldNode) {
	const newValue = newNode.value;
	const oldValue = oldNode.value;

	updateAttribute(newNode, oldNode, 'checked');
	updateAttribute(newNode, oldNode, 'disabled');

	if (newValue !== oldValue) {
		oldNode.setAttribute('value', newValue);
		oldNode.value = newValue;
	}

	if (newValue === 'null') {
		oldNode.value = '';
		oldNode.removeAttribute('value');
	}

	if (!newNode.hasAttributeNS(null, 'value')) {
		oldNode.removeAttribute('value');
	} else if (oldNode.type === 'range') {
		// this is so elements like slider move their UI thingy
		oldNode.value = newValue;
	}
}
The "value" attribute is special for the <input> element since it sets the initial value. Changing the "value" attribute without changing the "value" property will have no effect since it is only used to the set the initial value. Similar for the "checked" attribute, and "disabled".