binding: Inconsistent behavior between style.bind="{}" and style.bind="{ property: undefined }" in newer Chrome

I’m submitting a bug report

  • Library Version: 2.1.4

Please tell us about your environment:

  • Operating System: Linux (Ubuntu Bionic)

  • Node Version: Electron 3.0

  • NPM Version: 6.4.1
  • JSPM OR Webpack AND Version JSPM 0.17.0-beta.48
  • Browser: Electron 3.0 (Chrome 66)

  • Language: Typescript 3.0

Current behavior: Using style.bind=“{ property: undefined }” does not unset property if the previous binding had a value for that property.

Using style.bind=“{ }” does unset the property if the previous binding had a value for that property.

Expected/desired behavior: It should unset the property the same as if binding to an object without the property key.

In Electron 2.0, this was the behavior. I assume there was a change to style.setProperty() in Chromium where an undefined value no longer removes the style.

Can we call style.removeProperty (or replace undefined with null or “” when calling setProperty) instead to restore the previous behavior?

https://gist.run/?id=4044775d2efdb1733b55a0b9d92de378&sha=3acf777a662b49c4266c6449c2bf2d8413b250e6

  • What is the motivation / use case for changing the behavior? This is how it used to work in a previous browser version. More importantly, as shown in the demo, the same object bound to style (initially, and on the second button click) results in two different styles applied based on the value bound on the first button click.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Comments: 21 (10 by maintainers)

Most upvoted comments

---- Advertisement plug start ----- https://github.com/bigopon/aurelia-style-binding-command-plugin for two way binding with [style]:

<div background-color.style="bg"></div>
<!-- optinally, if needed, can be made to work: -->
<div style.background-color="bg"></div>

---- Advertisement plug end -----

In vCurrent there’s no true pluggable way of doing that (there will be in vNext), but it’s easy nonetheless: either monkey patch the StyleObserver or, if you want a more OO approach, derive a new implementation from it and the observerlocator and return your own version instead. I generally can’t think of a cleaner way to globally modify the behavior of an observer other than to simply modify that observer… 😃

const originalSetValue = StyleObserver.prototype.setValue;
StyleObserver.prototype.setValue = function(value) {
  // magic stuff
  originalSetValue.call(this, value);
  // more magic stuff
}

Something among those lines

Just curious, what happens if you try to two-way bind the style attribute?

StyleObserver.subscribe will throw an error. It doesn’t support two-way binding (same goes for any other element attribute that does not dispatch an event when it changes). This will be supported in vNext however (via MutationObservers).

I guess part of my concern was my sense that what the binding does is transform the object to an equivalent style string

It doesn’t. It’s literally as if you’re working directly with the style property of an element in javascript:

const el = document.createElement('div'); // el = HTMLDivElement
const style = el.style; // style = CSSStyleDeclaration
style.backgroundColor = 'green';
style.backgroundColor = undefined; // still green

This is identical to:

<div style.bind="myStyle"></div>
this.myStyle = { backgroundColor: 'green' };
this.taskQueue.queueMicroTask(() => {
    this.myStyle = { backgroundColor: undefined }; // still green
});

The difference is that the individual properties on the object are not observed, so you must always assign a new object if you want to change any property. Furthermore changes to the DOM are queued, so setting the style multiple times in a row from behind an observer is equivalent to setting it only once to the last value directly on the element (this is a general Aurelia thing that’s consistent throughout binding).

So to reiterate: an object is not translated to a string. Just pretend it’s a CSSStyleDeclaration. You can easily test stuff in the browser console and whatever happens there, you can expect to happen in binding.

Same thing applies to when you set it to a string. This is just as if you assigned that string to the .style property in javascript.

@krisdages @huochunpeng I just want to point out here that there is a big difference between how null and undefined are treated by the DOM. undefined is interpreted as “not a value”, e.g. it doesn’t set the value (not even to empty string). null on the other hand does translate to empty string. This is behaving as expected in that sense. Just a little thing you got to know about… 😃