From 8134c51a4870de1eab9da71887c98773c6721901 Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Mon, 3 Dec 2018 14:31:08 -0500 Subject: [PATCH] Fix style updates to avoid unnecessary allocation (#2312) * Fix style updates to avoid unnecessary allocation - Drive-by: properly censor `cssFloat` -> css `float:` to match DOM behavior * Document `style` syntax + custom property support. [skip ci] * Add a missing test * Add a few more descriptive comments. * Update changelog [skip ci] --- docs/change-log.md | 2 +- docs/hyperscript.md | 2 ++ render/render.js | 55 +++++++++++++++++------------- render/tests/test-createElement.js | 7 ++++ 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/docs/change-log.md b/docs/change-log.md index e6b73d92..a9b46161 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -72,7 +72,7 @@ - docs: tweaks: ([#2104](https://github.com/MithrilJS/mithril.js/pull/2104) [@mikeyb](https://github.com/mikeyb), [#2205](https://github.com/MithrilJS/mithril.js/pull/2205), [@cavemansspa](https://github.com/cavemansspa), [#2250](https://github.com/MithrilJS/mithril.js/pull/2250) [@isiahmeadows](https://github.com/isiahmeadows), [#2265](https://github.com/MithrilJS/mithril.js/pull/2265), [@isiahmeadows](https://github.com/isiahmeadows)) - render/core: avoid touching `Object.prototype.__proto__` setter with `key: "__proto__"` in certain situations ([#2251](https://github.com/MithrilJS/mithril.js/pull/2251)) - render/core: Vnodes stored in the dom node supplied to `m.render()` are now normalized [#2266](https://github.com/MithrilJS/mithril.js/pull/2266) -- render/core: CSS vars can now be specified in `{style}` attributes [#2192](https://github.com/MithrilJS/mithril.js/pull/2192) +- render/core: CSS vars can now be specified in `{style}` attributes ([#2192](https://github.com/MithrilJS/mithril.js/pull/2192) [@barneycarroll](https://github.com/barneycarroll)), ([#2311](https://github.com/MithrilJS/mithril.js/pull/2311) [@porsager](https://github.com/porsager)), ([#2312](https://github.com/MithrilJS/mithril.js/pull/2312) [@isiahmeadows](https://github.com/isiahmeadows)) - request: don't modify params, call `extract`/`serialize`/`deserialize` with correct `this` value ([#2288](https://github.com/MithrilJS/mithril.js/pull/2288)) --- diff --git a/docs/hyperscript.md b/docs/hyperscript.md index c2f440e7..07808bdc 100644 --- a/docs/hyperscript.md +++ b/docs/hyperscript.md @@ -262,6 +262,8 @@ m("div[style=background:red]") Using a string as a `style` would overwrite all inline styles in the element if it is redrawn, and not only CSS rules whose values have changed. +You can use both hyphenated CSS property names (like `background-color`) and camel cased DOM `style` property names (like `backgroundColor`). You can also define [CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables), if your browser supports them. + Mithril does not attempt to add units to number values. It simply stringifies them. --- diff --git a/render/render.js b/render/render.js index 83c5aecc..f364d731 100644 --- a/render/render.js +++ b/render/render.js @@ -775,38 +775,45 @@ module.exports = function($window) { ) && key in vnode.dom } - var matchUpperCase = /[A-Z]/g - function prependDashAndLowerCase(string){ - return "-" + string.toLowerCase() - } - - function normalizeProp(prop) { - return "-" && prop[1] === "-" - ? prop - : prop.replace(matchUpperCase, prependDashAndLowerCase) - } - //style + var uppercaseRegex = /[A-Z]/g + function toLowerCase(capital) { return "-" + capital.toLowerCase() } + function normalizeKey(key) { + return key[0] === "-" && key[1] === "-" ? key : + key === "cssFloat" ? "float" : + key.replace(uppercaseRegex, toLowerCase) + } function updateStyle(element, old, style) { - if (old != null && style != null && typeof old === "object" && typeof style === "object" && style !== old) { + if (old === style) { + // Styles are equivalent, do nothing. + } else if (style == null) { + // New style is missing, just clear it. + element.style.cssText = "" + } else if (typeof style !== "object") { + // New style is a string, let engine deal with patching. + element.style.cssText = style + } else if (old == null || typeof old !== "object") { + // `old` is missing or a string, `style` is an object. + element.style.cssText = "" + // Add new style properties + for (var key in style) { + var value = style[key] + if (value != null) element.style.setProperty(normalizeKey(key), String(value)) + } + } else { // Both old & new are (different) objects. // Update style properties that have changed for (var key in style) { - if (style[key] !== old[key]) element.style.setProperty(normalizeProp(key), style[key]) + var value = style[key] + if (value != null && (value = String(value)) !== String(old[key])) { + element.style.setProperty(normalizeKey(key), value) + } } // Remove style properties that no longer exist for (var key in old) { - if (!(key in style)) element.style.removeProperty(normalizeProp(key)) - } - return - } - if (old === style) element.style.cssText = "", old = null - if (style == null) element.style.cssText = "" - else if (typeof style === "string") element.style.cssText = style - else { - if (typeof old === "string") element.style.cssText = "" - for (var key in style) { - element.style.setProperty(normalizeProp(key), style[key]) + if (old[key] != null && style[key] == null) { + element.style.removeProperty(normalizeKey(key)) + } } } } diff --git a/render/tests/test-createElement.js b/render/tests/test-createElement.js index e40f9186..54119c68 100644 --- a/render/tests/test-createElement.js +++ b/render/tests/test-createElement.js @@ -45,6 +45,13 @@ o.spec("createElement", function() { o(vnode.dom.style["--cssVar"]).equals("red") }) + o("censors cssFloat to float", function() { + var vnode = {tag: "a", attrs: {style: {cssFloat: "left"}}} + + render(root, [vnode]) + + o(vnode.dom.style.float).equals("left") + }) o("creates children", function() { var vnode = {tag: "div", children: [{tag: "a"}, {tag: "b"}]} render(root, [vnode])