111 lines
4.3 KiB
JavaScript
111 lines
4.3 KiB
JavaScript
function syncBooleanAttrProp(fromEl, toEl, name) {
|
|
if (fromEl[name] !== toEl[name]) {
|
|
fromEl[name] = toEl[name];
|
|
if (fromEl[name]) {
|
|
fromEl.setAttribute(name, '');
|
|
} else {
|
|
fromEl.removeAttribute(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
export default {
|
|
OPTION: function(fromEl, toEl) {
|
|
var parentNode = fromEl.parentNode;
|
|
if (parentNode) {
|
|
var parentName = parentNode.nodeName.toUpperCase();
|
|
if (parentName === 'OPTGROUP') {
|
|
parentNode = parentNode.parentNode;
|
|
parentName = parentNode && parentNode.nodeName.toUpperCase();
|
|
}
|
|
if (parentName === 'SELECT' && !parentNode.hasAttribute('multiple')) {
|
|
if (fromEl.hasAttribute('selected') && !toEl.selected) {
|
|
// Workaround for MS Edge bug where the 'selected' attribute can only be
|
|
// removed if set to a non-empty value:
|
|
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12087679/
|
|
fromEl.setAttribute('selected', 'selected');
|
|
fromEl.removeAttribute('selected');
|
|
}
|
|
// We have to reset select element's selectedIndex to -1, otherwise setting
|
|
// fromEl.selected using the syncBooleanAttrProp below has no effect.
|
|
// The correct selectedIndex will be set in the SELECT special handler below.
|
|
parentNode.selectedIndex = -1;
|
|
}
|
|
}
|
|
syncBooleanAttrProp(fromEl, toEl, 'selected');
|
|
},
|
|
/**
|
|
* 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".
|
|
*/
|
|
INPUT: function(fromEl, toEl) {
|
|
syncBooleanAttrProp(fromEl, toEl, 'checked');
|
|
syncBooleanAttrProp(fromEl, toEl, 'disabled');
|
|
|
|
if (fromEl.value !== toEl.value) {
|
|
fromEl.value = toEl.value;
|
|
}
|
|
|
|
if (!toEl.hasAttribute('value')) {
|
|
fromEl.removeAttribute('value');
|
|
}
|
|
},
|
|
|
|
TEXTAREA: function(fromEl, toEl) {
|
|
var newValue = toEl.value;
|
|
if (fromEl.value !== newValue) {
|
|
fromEl.value = newValue;
|
|
}
|
|
|
|
var firstChild = fromEl.firstChild;
|
|
if (firstChild) {
|
|
// Needed for IE. Apparently IE sets the placeholder as the
|
|
// node value and vise versa. This ignores an empty update.
|
|
var oldValue = firstChild.nodeValue;
|
|
|
|
if (oldValue == newValue || (!newValue && oldValue == fromEl.placeholder)) {
|
|
return;
|
|
}
|
|
|
|
firstChild.nodeValue = newValue;
|
|
}
|
|
},
|
|
SELECT: function(fromEl, toEl) {
|
|
if (!toEl.hasAttribute('multiple')) {
|
|
var selectedIndex = -1;
|
|
var i = 0;
|
|
// We have to loop through children of fromEl, not toEl since nodes can be moved
|
|
// from toEl to fromEl directly when morphing.
|
|
// At the time this special handler is invoked, all children have already been morphed
|
|
// and appended to / removed from fromEl, so using fromEl here is safe and correct.
|
|
var curChild = fromEl.firstChild;
|
|
var optgroup;
|
|
var nodeName;
|
|
while(curChild) {
|
|
nodeName = curChild.nodeName && curChild.nodeName.toUpperCase();
|
|
if (nodeName === 'OPTGROUP') {
|
|
optgroup = curChild;
|
|
curChild = optgroup.firstChild;
|
|
} else {
|
|
if (nodeName === 'OPTION') {
|
|
if (curChild.hasAttribute('selected')) {
|
|
selectedIndex = i;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
curChild = curChild.nextSibling;
|
|
if (!curChild && optgroup) {
|
|
curChild = optgroup.nextSibling;
|
|
optgroup = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
fromEl.selectedIndex = selectedIndex;
|
|
}
|
|
}
|
|
};
|