Workaround bindings to textarea.placeholder in IE

Textareas have a very weird bug in IE, where the text of the placeholder
is copied to the content of the textarea when set.

This a creates two bugs:
1. An unintended binding is made to the textContent of the textarea's
   text child node, meaning updates to the `placeholder` will be an
   unnecessary binding process in the best case, or an exception thrown
   when updating the text child node in the worst case.
2. When `legacyOptimizations` is enabled, the child node of the text
   area is removed when the binding for `placeholder` is processed and
   removed, leaving a binding to a `null` node, and throwing exceptions.

Therefore, when we detect this placeholder behavior, we will remove the
textnode before template processing, preventing both bugs.
This commit is contained in:
Daniel Freedman
2019-07-30 14:03:45 -07:00
parent 69ee4688af
commit 61767da2c0
2 changed files with 83 additions and 3 deletions

View File

@@ -22,6 +22,49 @@ const templateExtensions = {
'dom-if': true,
'dom-repeat': true
};
let placeholderBugDetect = false;
let placeholderBug = false;
function hasPlaceholderBug() {
if (!placeholderBugDetect) {
const t = document.createElement('textarea');
t.placeholder = 'a';
placeholderBug = t.placeholder === t.textContent;
}
return placeholderBug;
}
/**
* Some browsers have a bug with textarea, where placeholder text is copied as
* a textnode child of the textarea.
*
* If the placeholder is a binding, this can break template stamping in two
* ways.
*
* One issue is that when the `placeholder` binding is removed, the textnode
* child of the textarea is deleted, and the template info tries to bind into
* that node.
*
* When `legacyOptimizations` is enabled, the node is removed from the textarea
* when the `placeholder` binding is processed, leaving an "undefined" cell in
* the binding metadata object.
*
* When `legacyOptimizations` is disabled, the template is cloned before
* processing, and has an extra binding to the textContent of the text node
* child of the textarea. This at best is an extra binding to process that has
* no useful effect, and at worst throws exceptions trying to update the text
* node.
*
* @param {!Node} node Check node for placeholder bug
* @return {boolean} True if placeholder is bugged
*/
function shouldFixPlaceholder(node) {
return hasPlaceholderBug()
&& node.localName === 'textarea' && node.placeholder
&& node.placeholder === node.textContent;
}
function wrapTemplateExtension(node) {
let is = node.getAttribute('is');
if (is && templateExtensions[is]) {
@@ -251,6 +294,9 @@ export const TemplateStamp = dedupingMixin(
// For ShadyDom optimization, indicating there is an insertion point
templateInfo.hasInsertionPoint = true;
}
if (shouldFixPlaceholder(node)) {
node.textContent = null;
}
if (element.firstChild) {
this._parseTemplateChildNodes(element, templateInfo, nodeInfo);
}