mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
<meta charset="utf-8">
|
||||
<script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
|
||||
<script src="wct-browser-config.js"></script>
|
||||
<script src="wct-browser-config.js"></script>
|
||||
<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
|
||||
<script type="module" src="../../polymer-element.js"></script>
|
||||
<script type="module" src="../../lib/mixins/gesture-event-listeners.js"></script>
|
||||
@@ -282,9 +281,10 @@ customElements.define(XBinding.is, XBinding);
|
||||
</dom-module>
|
||||
|
||||
<script type="module">
|
||||
import '../../polymer-element.js';
|
||||
import {PolymerElement, html} from '../../polymer-element.js';
|
||||
import '../../lib/mixins/gesture-event-listeners.js';
|
||||
import '../../lib/elements/dom-if.js';
|
||||
import {setLegacyOptimizations} from '../../lib/utils/settings.js';
|
||||
|
||||
suite('runtime template stamping', function() {
|
||||
|
||||
@@ -694,7 +694,6 @@ suite('runtime template stamping', function() {
|
||||
'x-runtime'
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('template parsing hooks', () => {
|
||||
@@ -818,6 +817,41 @@ suite('template parsing hooks', () => {
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
});
|
||||
|
||||
suite('textarea placeholder bug', function() {
|
||||
class PlaceholderBase extends PolymerElement {
|
||||
static get template() {
|
||||
return html`<textarea id="textarea" placeholder="[[value]]"></textarea>`;
|
||||
}
|
||||
static get properties() {
|
||||
return {value: {type: String}};
|
||||
}
|
||||
}
|
||||
test('placeholder binding does not leak to textContent', function() {
|
||||
customElements.define('placeholder-duplicate', class extends PlaceholderBase {});
|
||||
const el = document.createElement('placeholder-duplicate');
|
||||
document.body.appendChild(el);
|
||||
const textarea = el.$.textarea;
|
||||
el.value = 'before';
|
||||
textarea.value = 'Hello!';
|
||||
el.value = 'after';
|
||||
assert.equal(textarea.value, 'Hello!');
|
||||
});
|
||||
suite('legacyOptimizations', function() {
|
||||
suiteSetup(function() {
|
||||
setLegacyOptimizations(true);
|
||||
});
|
||||
suiteTeardown(function() {
|
||||
setLegacyOptimizations(false);
|
||||
});
|
||||
test('textarea placeholder binding works with legacyOptimizations', function() {
|
||||
customElements.define('placeholder-bug', class extends PlaceholderBase {});
|
||||
const el = document.createElement('placeholder-bug');
|
||||
document.body.appendChild(el);
|
||||
assert.doesNotThrow(() => {el.value = 'bar';});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user