I want to change the color of an input field border. But yet before changing, I need to save the initial CSS via getComputedStyle so that I can set all the initial CSS of the input field back.
The problem is that the object of getComputedStyle is not fixed and changes dynamically.
I tried to copy the object into a new one without reference, but then I cannot use the property getPropertyValue because it is a different type of object.
Is there any way how I could retrieve back the initial CSS of the input field?
My code is the following:
const input_element = document.getElementById('text-id');
let computedStyle_fixed;
function change_color() {
// get computed style
const computedStyle = window.getComputedStyle(input_element);
// copy computed style into new object without reference
computedStyle_fixed = JSON.parse(JSON.stringify(computedStyle));
// change the border color of the input field
input_element.style.borderColor = 'red';
}
function retrieve_color() {
Array.from(computedStyle_fixed).forEach(
key => element.style.setProperty(key, computedStyle_fixed.getPropertyValue(key), 'important')
)
}
<input type='text' id='text-id'>
<button onclick='change_color()'>Change color</button>
<button onclick='retrieve_color()'>Retrieve color</button>
from what I understood is that you need all the css properties of a element and store in an object.
you can use spread operator to do so.
const computedStyle = {...window.getComputedStyle(input_element)};
you can now use this like a normal object.
ex : console.log(computedStyle.backgroundColor);
There are a number of issues in your code. Firstly in this line:
const computedStyle = window.getComputedStyle(input_element);
window.getComputedStyle returns a CSSStyleDeclaration object. When copied using:
computedStyle_fixed = JSON.parse(JSON.stringify(computedStyle));
the object assigned to computedStyle_fixed is a plain object, not a CSSStyleDeclaration object. This has consequences in the retrieve_color function explained further down.
Then in:
Array.from(computedStyle_fixed)
Array.from expects the argument to be an array–like object with a length property or an iterable object. computedStyle_fixed is neither so the result is an empty array and the following forEach does nothing (because length is 0).
Within the forEach callback, there is:
computedStyle_fixed.getPropertyValue(key)
the computedStyle_fixed object doesn't have a getPropertyValue method (see above), so if that line is executed the result will be a type error.
To iterate the properties of the computedStyle_fixed object, use Object.keys and access properties using square bracket notation, not the (missing) getPropertyValue method, e.g.
Object.keys(computedStyle_fixed).forEach(key =>
element.style.setProperty(key, computedStyle_fixed[key], 'important')
);
Other notes
Creating implicit globals like computedStyle_fixed is not recommended, declare them or set them as object properties.
Using JSON.stringify to create a shallow copy of an object is also not a good idea, it's much better to use Object.assign:
let computedStyle_fixed = Object.assign({}, computedStyle);
This also creates a plain object, not a CSSStyleDeclaration object.
I have solved it, I have just change a little the retrieve_color() function.
But I thought that it would set the style of the input border exactly as it used to be initialy.
If you look at the code at jsfiddle and click on change_color and then click on retrieve_color, you will see that the input does not look as it was (the border are different).
Do you have any idea how I could get it back exactly as it was?
// the code
https://jsfiddle.net/gfxjn4se/
Related
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);
I have some global vars, which I know, bad code and all.
One of these vars: MyApp.player.pet, is an object of type pet, attached to the player object. It has some properties like "xp" which is just a number.
I am passing this object to a dynamic UI class that displays information about all kinds of objects, which is why I can't use the reference to the global var inside the object. Here is how I pass it:
var listOfProperties =
[MyApp.player.pet.name,
{"xp: ": MyApp.player.pet.xp},
{"xpToNextLevel: " : MyApp.player.pet.xpToNextLevel}];
then I transfer this list of properties to a new UI object as such:
new tooltipText(listOfProperties, ..., ...)
However, when the value gets updated inside MyApp.player.pet.xp the value inside the object tooltipText doesn't change. Somehow it's passing a copy, but I want to pass a reference if those are correct terms.
I've tried searching for this but all I can find are people asking how to make copies of objects, which seems to be what I've done unintentionally but not what I want.
How can I make sure that when MyApp.player.pet.xp changes, the value inside the tooltiptext also changes?
There is some misconception in your question:
You did not create copies of your original objects.
You merely copied some of their property values, by putting them into new objects (e.g. {"xp: ": MyApp.player.pet.xp}).
If you want to change the properties of your original objects, you need to change them there, not in your newly created small objects.
So if you want a generic list of properties, you need to keep a reference to your original object (MyApp.player.pet) as well. Example:
var listOfProperties = {
obj: MyApp.player.pet,
propertyNames: ['xp', 'xpToNextLevel']
}
You can then use the property names to generically set the properties of your original object:
var propertyName = listOfProperties.propertyNames[0];
listOfProperties.obj[propertyName] = newValue;
After answering this question about using the style.top property of a div in jQuery's $.animate(), I started to wonder if there was a way to do it more in line with what the OP had thought (s)he could do.
The question is: can we get a direct reference to an object's property when that property is a primitive type?
For example, if you had an object called myDiv, you could get a reference to its style property, because that's an object, but is there any way to get a reference to the top property of style so that when this changes, you don't have to retrieve it again and again? (Note: I'm not saying this retrieval is computationally significant. This is just a question about what's possible, not what's good practice.)
var myDiv = document.getElementById('myDiv');
myDiv.style.top = "100px";
var myDivStyle = myDiv.style;
var myDivStyleTop = myDiv.style.top;
myDiv.style.top = "200px";
console.log(myDivStyle.top); // will print "200px"
console.log(myDivStyleTop); // will obviously print "100px"
Is there a way to reference that top property directly without going through style? My instinct is no, but JS has surprised me before, so I just wanted to make sure.
No. A variable are just simple container that can hold a value. Assigning a new value to a variable can never change the value of another variable or property.
In this regard, it doesn't matter whether the variable holds a primitive values or an object, that's just how variables work.
No there isn't. Primitives are immutable and never stored by reference.
From MDN
object reference
A link to an object. Object references can be used as if they were the objects they link to. The concept of object references arises when assigning the same object to more than one property. Each assigned property does not hold a copy of the object. Instead, they hold object references that link to the same object. In practice, this means that if the object is modified, all properties referring to the object reflect the modification.
primitive, primitive value
A data that is not an object and does not have any methods. JavaScript has 5 primitive datatypes: string, number, boolean, null, undefined. With the exception of null and undefined, all primitives values have object equivalents which wrap around the primitive values, e.g. a String object wraps around a string primitive. All primitives are immutable.
This works:
var button = $A("#aba_but_del")[0];
button.innerHTML = Su.Ani.flipPane.p1;
But this does not work
var button_text = $A("#aba_but_del")[0].innerHTML;
button_text = Su.Ani.flipPane.p1;
Why?
According to https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Operator_Precedence, the . has highest precedence. Does this mean that .innerHTML is being applied to [0]?
All primitives in javascript are immutable. This means that the following code just changes a different instance of a string:
button_text = Su.Ani.flipPane.p1;
In the first case button is an object referencing a DOM element. So when changing the innerHTML property value you are changing the DOM element itself.
button_text = ... replaces the variable with the value.
button.innerHTML = ... replaces the property of the object, which in turn affects the displayed HTML because of how the setter function works for that property.
You can't have a reference to a property.
When you do like this:
var button = $A("#aba_but_del")[0];
you get a reference to the element, so later on you can use that reference to access members of the element.
When you do like this:
var button = $A("#aba_but_del")[0].innerHTML;
you get the value of the property, not a reference to the property. The value is a string taht you assign to the button variable, and assigning a different string to the variable doesn't change the first string, and not the object where that string was copied from.
"The . has highest precedence. Does this mean that .innerHTML is being
applied to [0]?"
No. The [] operator has the same precedence as the . operator, as they are actually the same operator, so they are evaluated from left to right. Using obj['name'] is the same as obj.name, but the . operator can't be used with a property name that doesn't follow the rules of an identifier, like a numeric index.
1)
button_text is a string, no longer bound to the button DOM object.
So changing it will not impact the button element.
2)
Does this mean that .innerHTML is being applied to [0]? Yes. "." and "[]" have the same priority going from left to right.
JavaScript has no pointers or such stuff. If you assign something to a variable, you overwrite the previous value. You want to assign to the innerHTML property of the DOM object, changing the page rendering.
What are expando objects in javascripts?
For what purpose we need this ? Any complete example will be appreciated
I found 1 article here Javascript: The red-headed stepchild of web development
Well, in javascript, any object is an expando object. What it means is, as the article covers, that whenever you try to access a property1 it will automatically be created.
var myObj = {}; // completely empty object
myObj.myProp = 'value';
The moment you assign myProp a value, the property myProp is dynamically created, eventhough it didn't exist before. In a lot of other languages, such as C#, this is not normally possible (actually C# has just enabled expando object support as well, but that's besides the point). To access a property in a normal class in C#, you need to specify in the class that it does indeed have this property.
1 Not quite correct. See npup's comment below for clarification.
Everything except primitive types(string, number,boolean) are objects and support Key:values structure. properties(keys) can be accessed and set using the dot notation as well as the square brackets.
var myObj = {};
myObj.myProp1 = 'value1'; //works, an expando property
myObj[myProp2] = 'value2'; // doesn't work, myProp2 is an undefined name.
myObj['myProp2'] = 'value2'; // works , an expando property
myObj[2010]= 'value'; //note the key is number, still works, an expando property??
myObj.2010 = 'value'; // FAILS. to use dot notation, key must be a string
An article written in 2007 that uses document.all (as the only way to access elements)? That's a big red flag.
It is just dressing up "You can add properties to an object" with some buzzwords.
We need to be able to do this because otherwise we wouldn't be able to store data, and that would make JavaScript a pretty useless language.
(Everything is an array? No it isn't. And it iterates over an object without a hasOwnProperty wrapper. That isn't safe. Just keep away from the article, it is worse than useless)
JavaScript turns elements with specific IDs of names into expandos of the returned DOM object. It is explained here.