Defining a setter for individual style property - javascript

According to the current CSS specification CSSStyleDeclaration.setProperty() has a shorthand of writing directly to the style property. Like in the code below, both lines have the same functionality:
element.style.color = "#fff";
element.style.setProperty("color", "#fff");
Though there is an interesting and unclear situation to me here:
I couldn't find the way to hook a custom setter for individual style properties, since these don't have an explicit setter. Changing the setProperty method only works for the direct call of the method element.style.setProperty() and not the shorthand (i.e. element.style.color).
Both seem to refer to CSSStyleDeclaration interface, but I can't find a way to define a setter for a specific style property (i.e. element.style.color).
MDN says the following:
While this property is considered read-only, it is possible to set an inline style by assigning a string directly to the style property. In this case the string is forwarded to CSSStyleDeclaration.cssText. Using style in this manner will completely overwrite all inline styles on the element.
According to this, changing a setter for cssText could possibly intercept the setter for specific style properties, but it only does for inline style definition itself (i.e. element.style = 'color:#fff;')
Is there a way to define a custom setter for individual style properties, such as element.style.color, or at least have a generic setter for any of them?

As you have explained, there are two ways to set a style property:
e.style.property=
e.style.setProperty(property,..)
Depending on implementation, these two ways can be handled either independently, or using each other. It is very unprobably, that setProperty calls the way 1. The way 1. can have a setter, which may or may not call the setProperty.
The property may exist (have a property descriptor), or the way 1. may be handled another way (e.g. by catch the exception non-existent property.)
This does mean, that you can define/redefine the property as an accessor, which's getter calls getPropertyValue, and which's setter calls setProperty. Then, you can inject any call of (even instance-defined) function.
Object.defineProperty(CSSStyleDeclaration.prototype, "color", {configurable:true, enumerable:true, get:function() {return this.getPropertyValue("color");}, set:function(c) {let tweakedC=tweakColor(c); this.setProperty("color", tweakedC); handleNewPropertyValue(c, tweakedC);}});
function handleNewPropertyValue(c, tweakedC) {...};
I have used this way in some version of Chrome, but I believe, that it will do in most implementations, except of those, where setProperty depends on existing the property per se (unprobably).
Tweaking the prototype to create a new instance property on first access is very easy, but it is another question.

Related

Why doesn't DOM style object set CSS variables directly?

My understanding is there are two equivalent ways to set CSS rules via JavaScript:
#1: element.style.setProperty(propertyName, value)
#2: element.style.propertyName = value
I have always favored the second, shorter method.
When it comes to CSS variables, I find I have to use an explicit setProperty call:
element.style.setProperty('--varName', value)
This approach has no effect on variables:
element.style['--varName'] = value
Why is this?
It's because DOM's Style function only understands HTML properties and not CSS properties. Defining CSS properties are listed in style's setProperty Function.
The .style[propertyName] expects a property name inside but does not support a custom property like .setProperty() does. If you pass --varName, you are passing the value assigned to the brackets.
For example, if --varName: 'blue', by saying .style['--varName'] = value, you are saying change the blue property to value. Since blue is not a property, it will not work.
You need to retrieve it from getComputedStyle
see : https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties#Values_in_JavaScript
To use the values of custom properties in JavaScript, it is just like standard properties.
// get variable from inline style
element.style.getPropertyValue("--my-var");
// get variable from wherever
getComputedStyle(element).getPropertyValue("--my-var");
// set variable on inline style
element.style.setProperty("--my-var", jsVar + 4);
document.body.style.setProperty('--varName', 'see i got stored even i`m a useless value');
let root = window.getComputedStyle(document.body );
let varNameValue= root.getPropertyValue('--varName');
console.log(varNameValue);

Using setters in Javascript to "override" properties

The MDN page about set seems to state that an
[ECMAScript 2015] setter must not appear in an object literal ... with a
data entry for the same property.
However when using the super keyword this no longer seems to apply.
class Foo {
constructor(bar){
this.bar = bar
}
set bar(newBar){
if (!newBar.match(/\w+/))
throw Error("Invalid bar value")
// I can use super despite not being a derived class.
return super.bar = newBar
}
}
const baz = new Foo("Baz")
baz.bar = "new value" // No recursion
This seems like a useful feature as the property doesn't have to be "hidden" by prefixing it with an underscore. Plus I don't have to mess with the property enumerability to avoid the "hidden" version from showing in a loop or serialization.
But the set syntax is a bit of a black-box and I can't tell what it's actually doing.
Am I breaking something here or is it okay to use?
Also what is super referencing here?
This seems like a useful feature as the property doesn't have to be "hidden" by prefixing it with an underscore or something. Plus I don't have to mess with the property enumerability to avoid the "hidden" version from showing in a loop or serialization.
No, it's not useful. It's a hack at best, and doesn't do what you expect.
There is nothing hidden here at all. You are creating a new property with the name bar on the instance itself, shadowing any getters/setters you had defined on the prototype. The second assignment does not get your setter caller. Also the instance property is a normal enumerable property, so it will show up in for in loops and serialisation.
Also what is "super" referencing here?
The super keyword refers to the prototype of the object that the method (or setter) is defined on, i.e. Object.getPrototypeOf(Foo.prototype). This is the Object.prototype in your case, since your class Foo doesn't extend anything.
The .foo access will be looked up on that prototype, and would normally find a method that you inherited from your parent class or something. When using that, the property reference super.foo will however make the receiver of the operation (i.e. what would the this keyword in a method invocation) be the current this instance, not the prototype.
In your case, it's not a method call but an assignment. This could run a setter inherited from the parent class, but in your case there is no Object.prototype.foo property so it will fall back to standard assignment on the target - and that target is the baz instance itself, where a new own property will be created.
So no, it is not okay to use.

Where is this parameter getting its value from?

I have this code:
https://jsfiddle.net/toddmotto/qaqeapn6/
I'm wondering, where is the 'target' parameter getting its value from?
function (target)
It is a decorated class
A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form #expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.
Please refer https://www.typescriptlang.org/docs/handbook/decorators.html
That is internal feature of Class Decorators.
ES2016 Decorators work on property descriptors and classes. They automatically get passed property names and the target object, as we’ll soon cover. Having access to the descriptor allows a decorator to do things like changing a property to use a getter, enabling behaviour that would otherwise be cumbersome such as automatically binding methods to the current instance on first access of a property.
You can read further information: https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.qj8979jae

What is difference in .value="somevalue" and set(value,"somevalue") in html?

I want to know the difference between in html and javascript.
dijit.byId("link_id").value = linkData.link_id;
dijit.byId("link_id").set("value",linkData.link_id);
because, when I used the first option, it did not set the value in the linkid textbox, but the second option did that. So just wanted to know what is happening in both situations.
The difference is that using the first method, you're directly setting a property of the widget object, for example, setting the value.
In the second example however, you're using the setter provided by dijit/_WidgetBase, which in turn calls a _set function, for the value property it would be _setValueAttr(). Widgets can extend these setters with custom functionality that will happen when using that setter. For example it can take the necessary steps to display the value as well.
If we, for example pick a dijit/form/Select widget and look at the code of _setValueAttr(), we notice that it calls some extra functions like:
domAttr.set(this.valueNode, "value", this.get("value"));
this._refreshState(); // to update this.state
This functionality is only called when using dijit.byId("link_id").set("value", "myValue");, and that's the reason why your value is only displayed in this case. When you're directly setting the property, you're "short-circuiting" this functionality.
TL;DR: Always use the setter function when using Dojo widgets.

Observe a variable value, when it chaged take some actions

I was trying to determine the best way to observe a variable's value and track its changes, for example 'language' or 'time-zone', then when it will be changed take some actions depending on the new value.
I thought of using setInterval, but I have many 'interval's in my website, so I don't want to overuse it, I'm worried that it may affect the user experience. Instead I found my self compelled to trigger the actions which I want to be done after the value changes in each method may change the variable's value, this is simple but makes my code a bit tightly coupled.
what do you suggest for that.
It seems like Object.observe would be pretty much exactly what you need; unfortunately it is currently proposed as a "post ECMAScript 6" spec, so it will be a while until it is widely available in browsers. There are shim implementations though (e.g. here or here), which could give you the same functionality in current browsers.
An alternative approach would be wrapping the object in question in a direct proxy, but those are part of ES6, and also not widely adopted by browsers yet.
In languages like C++, you'd do this with accessor methods.
For example, instead of accessing a property of a class with something like foo.bar, you'd say foo.getBar().
getBar() would look something like this:
this.getBar = function(){
console.log("bar was accessed");
return bar;
}
You should also have a method to set the value of bar, i.e.
this.setBar = function(newBar){
console.log("Setting the value of bar");
bar = newBar;
}
These methods will give you more control of your variables, too. For example, if someone tries to assign a string to something that should be an integer, you could throw an error. Or if you have a variable called "length", you could throw an error if someone tries to make it less than zero.
You should use Object.prototype.watch() to track variable's change
The watch() method watches for a property to be assigned a value and
runs a function when that occurs.
Watches for assignment to a property named prop in this object,
calling handler(prop, oldval, newval) whenever prop is set and storing
the return value in that property. A watchpoint can filter (or
nullify) the value assignment, by returning a modified newval (or by
returning oldval).
If you delete a property for which a watchpoint has been set, that
watchpoint does not disappear. If you later recreate the property, the
watchpoint is still in effect.
To remove a watchpoint, use the unwatch() method. By default, the
watch method is inherited by every object descended from Object.
there is not standard, but you can use the gist polifill created by eli-grey
Anyway this is a duplicate of Listening for variable changes in JavaScript or jQuery

Categories

Resources