From d4cef98e9aeecec9e80a76767c3f55f43bb6ba76 Mon Sep 17 00:00:00 2001 From: Jay Phelps Date: Fri, 29 May 2015 13:05:42 -0700 Subject: [PATCH] [BUGFIX] Adds the ability to blacklist props that should use setAttribute because of browser compliance issues, fixes emberjs/ember.js#11112 --- packages/dom-helper/lib/prop.js | 32 ++++++++++++++++++-- packages/dom-helper/tests/dom-helper-test.js | 23 ++++++++++++++ packages/dom-helper/tests/prop-test.js | 26 ++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 packages/dom-helper/tests/prop-test.js diff --git a/packages/dom-helper/lib/prop.js b/packages/dom-helper/lib/prop.js index 8268cfad..1c3014bf 100644 --- a/packages/dom-helper/lib/prop.js +++ b/packages/dom-helper/lib/prop.js @@ -2,6 +2,8 @@ export function isAttrRemovalValue(value) { return value === null || value === undefined; } +function UNDEFINED() {} + // TODO should this be an o_create kind of thing? export var propertyCaches = {}; @@ -13,11 +15,37 @@ export function normalizeProperty(element, attrName) { // TODO should this be an o_create kind of thing? cache = {}; for (key in element) { - cache[key.toLowerCase()] = key; + key = key.toLowerCase(); + if (isSettable(element, key)) { + cache[key] = key; + } else { + cache[key] = UNDEFINED; + } } propertyCaches[tagName] = cache; } // presumes that the attrName has been lowercased. - return cache[attrName]; + var value = cache[attrName]; + return value === UNDEFINED ? undefined : value; +} + +// elements with a property that does not conform to the spec in certain +// browsers. In these cases, we'll end up using setAttribute instead +var badPairs = [{ + // phantomjs < 2.0 lets you set it as a prop but won't reflect it + // back to the attribute. button.getAttribute('type') === null + tagName: 'BUTTON', + propName: 'type' +}]; + +function isSettable(element, attrName) { + for (let i = 0, l = badPairs.length; i < l; i++) { + let pair = badPairs[i]; + if (pair.tagName === element.tagName && pair.propName === attrName) { + return false; + } + } + + return true; } diff --git a/packages/dom-helper/tests/dom-helper-test.js b/packages/dom-helper/tests/dom-helper-test.js index 937221e3..3dd77969 100644 --- a/packages/dom-helper/tests/dom-helper-test.js +++ b/packages/dom-helper/tests/dom-helper-test.js @@ -194,6 +194,29 @@ test('#setProperty removes attr with undefined', function(){ equalHTML(node, '
', 'undefined attribute removes the attribute'); }); +test('#setProperty uses setAttribute for special non-compliant element props', function() { + expect(3); + + var badPairs = [ + { tagName: 'BUTTON', key: 'type', value: 'submit' } + ]; + + badPairs.forEach(function (pair) { + var node = dom.createElement(pair.tagName); + var setAttribute = node.setAttribute; + + node.setAttribute = function (attrName, value) { + equal(attrName, pair.key, 'setAttribute called with correct attrName'); + equal(value, pair.value, 'setAttribute called with correct value'); + return setAttribute.call(this, attrName, value); + }; + + dom.setProperty(node, pair.key, pair.value); + // e.g. + equalHTML(node, '<' + pair.tagName + ' ' + pair.key + '="' + pair.value + '">', 'output html is correct'); + }); +}); + test('#addClasses', function(){ var node = dom.createElement('div'); dom.addClasses(node, ['super-fun']); diff --git a/packages/dom-helper/tests/prop-test.js b/packages/dom-helper/tests/prop-test.js new file mode 100644 index 00000000..d27d7954 --- /dev/null +++ b/packages/dom-helper/tests/prop-test.js @@ -0,0 +1,26 @@ +import { normalizeProperty } from 'dom-helper/prop'; + +QUnit.module('dom-helper prop'); + +test('returns `undefined` for special element properties that are non-compliant in certain browsers', function() { + expect(1); + + var badPairs = [ + { tagName: 'BUTTON', key: 'type' } + ]; + + badPairs.forEach(function (pair) { + var element = { + tagName: pair.tagName + }; + + Object.defineProperty(element, pair.key, { + set: function () { + throw new Error('I am a bad browser!'); + } + }); + + var actual = normalizeProperty(element, pair.key); + equal(actual, undefined); + }); +}); \ No newline at end of file