why are initial CSS styles not visible on DOM element.style field? - javascript

OK I have full expectation of going down in flames for asking something stupid (or at least duplicate), but in the attached snippet, why do I have to use window.getComputedStyle to access styles applied by CSS? I was under the impression that the .style field would at least reflect those styles initially applied by CSS, and/or manually changed since then.
If not, what are the exact rules governing which properties are reflected (and when) in an element's .style field?
setTimeout(() => {
console.log("the bckg color:", reddish.style.backgroundColor);
console.log("the width:", reddish.style.width);
console.log("from a computed style:", window.getComputedStyle(reddish).backgroundColor);
console.log("from a computed style:", window.getComputedStyle(reddish).width);
}, 100);
#reddish {
background-color: #fa5;
width: 100px;
height: 100px;
}
<html>
<body>
<div id="reddish"></div>
</body>
</html>

The HTMLElement.style property is not useful for completely
learning about the styles applied on the element, since it represents
only the CSS declarations set in the element's inline style
attribute, not those that come from style rules elsewhere, such as
style rules in the section, or external style sheets. To get
the values of all CSS properties for an element you should use
Window.getComputedStyle() instead.
Via- MDN Web Docs | Getting Style
Information
HTMLElement.style:
The HTMLElement.style property is used to get as well as set the inline style of an element.
console.log(document.getElementById("para").style.fontSize); // will work since the "font-size" property is set inline
console.log(document.getElementById("para").style.color); // will not work since the "color" property is not set inline
#para {color: rgb(34, 34, 34);}
<p id="para" style="font-size: 20px;">Hello</p>
Window.getComputedStyle():
The getComputedStyle() method however, returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain thus returning the css properties from both inline style declarations as well as from external style-sheets.
console.log(window.getComputedStyle(document.getElementById("para")).fontSize); // will work
console.log(window.getComputedStyle(document.getElementById("para")).color); // will work
#para {
color: rgb(34, 34, 34);
}
<p id="para" style="font-size: 20px;">Hello</p>

HTMLElement.style is for the inline style of an element. It does not take into account CSS whatsoever. This is basically just directly setting or getting a property on the element object.
<div style="color: red;">Hello</div>
Window.getComputedStyle() takes into account inline styles and CSS, after resolving cascading, inheritance, etc. It's basically the "final" actual style value used to render the element on the page.
// CSS
#blue-text {
color: blue !important;
}
// HTML
<div style="color: red;" id="blue-text">Hello</div>
// JS
const myElement = document.querySelector("#blue-text");
myElement.style.color; // "red" because that's the inline style
window.getComputedStyle(myElement).color; // "rgb(0, 0, 255)" because CSS !important overrides inline style

Related

Consternation on testing non-inherited-yet-inherited CSS display property

I have created a modal component in Angular. In a unit test, the modal is appearing in the DOM as shown:
However, I start out with a style on app-modal2 that includes display:none, so what actually renders is just the fixed text above the modal -- the content of the modal is correctly omitted:
When the user takes an action that adjusts the style to include display:block then the content of the modal correctly appears. Which is to say, the code is working exactly as I expect.
What I am confounded about is a unit test.
So: why my title ("Consternation on testing non-inherited-yet-inherited CSS display property") ?
Well, according to the docs, the display property is NOT inherited:
Using browser dev tools, I have confirmed that is true: descendant elements have values other than none for the display property. So even though descendant elements are affected by an ancestor having display: none it is because the subtree rooted at the ancestor is removed -- and this is not considered inheritance. Well, OK, potayto, potahto... Not technically inherited, but acts like it.
The visibility of my modal is controlled by the display property. It is set either to display: none or display:block depending on user actions. But that is strictly dealing with visibility, not existence. That is, #myContent is present with either display value. Since I therefore cannot test for existence of #myContent I must test strictly for visibility.
So how do I check an element for visibility controlled by some ancestor's display value, since display is not inherited? Is there a way to check for any ancestor having display:none? Or is there some other way to do this?
You can try using the jQuery parent() method, and put the style as the first argument.
I found out pretty disturbing your question. I think is one of the most hard questions to answer because goes right to the core of cascading and inheritance.
As far as I could find, display property is the only property that can't be specified (but computed) on how should be display by UA. HTML tags are pre-defined styles, those styles are display on UA without any CSS file, e.g. p elements are display as inline.
I tested it too with devtools; forgetting JS at all for very front-end purposes. (Maybe I'll check with with JS later as -second part-). This answer is intended for all audiences, newbies and experienced devs.
Before declare what is going to be styled, we may note that we have dependencies from the browser (User Agent) that parses the stylesheet.
We do not define all universe of properties to be styled, so when is not defined, a property needs to be set and the user agent roles to set a property (doesn't have to be its initial value), there's no official specification on how UA must render websites, it's expected them to be display as the stylesheet specifies, which often, does not act likely according browsing experience.
Cascading
One of the fundamental design principles of CSS is cascading.
What does an User Agent (UA) cascades? Elements? Properties? Objects?
Well, UA treat HTML tags as elements, and those elements are called as box tree, as the same, text included inside an element are called as text node.
Since CSS syntax and its parsing is a perfect cascade, that is the only word that remains if we need to figure out about how (UA) must display HTML documents. The UA also applies its own style sheet. This means that rendering also depends on the way (units) we use to specify values, if we specify a lot of different values e.g. pixels, cm, percentages, relative units (em, rem), etc, the more information UA needs to parse to be displayed, that's why front-end developers should be encouraged to perform clean css styles with homogeneous units to squeeze every milisecond out of browsing perfomance (such important in mobile experiences).
Inheritance
When no declarations try to set a the value for an element/property
combination. In this case, a value is be found by way of inheritance
or by looking at the property’s initial value.
What is called for inheritance, it's just the css properties that can be inherited (those are already established).
So, if a css property seems to be inherited, it's not really inheritance behavior, it's cascading behavior, and it's inheritance becomes by the nature of the syntax for the specified css property.
Answer
The display property is not inherited, but when none property is set, all the descendants elements will no generate any box-model subtree nor text node, (JS could be forcing the element to be display for testing purposes).
In the case of display:none; when the box tree and text node descendants are hidden by the parent element, the style applied of none is by cascading, not by inheritance.
In the example below, the span that is descendant of the fourth div element has set the background property as inherit, but the background can't be inherited, that's why the span element does not display any color background. Otherwise, the span that is descendant of the third div element inherits the color property. The fourth div element has display set: inline; once again, display can't be inherited, that's why the span element does not inherit that property and is displayed as block by the UA.
*{
border: 1px solid black;
}
.one {
display:block;
}
.two {
}
.three{
background:cornsilk;
}
.childthree{
color:red;
}
span{
background: inherit;
position: relative;
top:80px;
border: 5px solid black;
padding: 5px;
margin:5px;
}
.four{
display:inline;
}
canvas{
background:#99e6ff;
}
html {
padding:1em;
}
<div class="wrapper">
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three
<div class="childthree">I'm a subtree inside the third div<br><span>I'm span tag</span></div>
</div>
<div class="four">four<p>i'm a p tag with thext content<span>I'm a span element inside a p element</span></p</p>
<canvas></canvas>
</div>

How to get backgroundColor of element when it is set by the CSS property background?

One way to set the background-color property of an element is by using background: #someColorValue. However, doing this will not change the backgroundColor property in the style of that element. As a result, when I use element.style.backgroundColor, it returns an empty string.
This problem doesn't occur when setting color directly using the background-color property. Why does this happen??
When you access the style (ElementCSSInlineStyle.style) property of an element, you are accessing its inline style. This means that if you give an element a style via a class, you still cannot access it through style. As a result, it will return you an empty string ('').
Window.getComputedStyle, on the other hand, returns:
... the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain
This means that you can access the style that is given via a class, simply because it gives you all CSS properties after applying all active stylesheets (including those applied via a class).
In your particular case, you're trying to access background. background CSS property is actually a shorthand that also sets a lot of other background-related CSS properties. When you specify only the color using background, the other properties will automatically be inserted with their default values. You can access this background property through ElementCSSInlineStyle.style. However, when accessing background in the object Window.getComputedStyle returns, you will always get an empty string. This is because the object returned does not have the key background; it only has the keys for each background-related CSS properties (e.g. background-color, background-clip, etc.).
Here's a simple example demonstrating how you cannot access a non-inline style through style property of an element, and also how you cannot access the value of a property that is shorthand through the object Window.getComputedStyle
const boxOne = document.querySelector('#boxOne')
const boxTwo = document.querySelector('#boxTwo')
console.log(`Box One: background ${boxOne.style['background']}, background-color ${boxOne.style['background-color']}`)
console.log(`Box Two: background ${boxTwo.style['background']}, background-color ${boxTwo.style['background-color']}`)
const boxOneComputedStyles = getComputedStyle(boxOne)
const boxTwoComputedStyles = getComputedStyle(boxTwo)
// There's no 'background' key in getComputedStyle
console.log(`Box One (Computed Styles): background ${boxOneComputedStyles['background']}, background-color ${boxOneComputedStyles['background-color']}`)
console.log(`Box Two (Computed Styles): background ${boxTwoComputedStyles['background']}, background-color ${boxTwoComputedStyles['background-color']}`)
#boxOne,
#boxTwo {
background: #121212DD;
border-radius: 5px;
width: 50px;
height: 50px;
margin: 1em;
}
<div id="boxOne" style="background-color: #121212DD;"></div>
<div id="boxTwo" style="background: #121212DD;"></div>

Global Styles do not override shadow dom style

According to https://developers.google.com/web/fundamentals/web-components/shadowdom#stylefromoutside:
Outside styles always win over styles defined in shadow DOM. For
example, if the user writes the selector fancy-tabs { width: 500px; },
it will trump the component's rule: :host { width: 650px;}.
This doesn't seem to work in my example. I have setup an app-overlay component. Inside I have a div with a .child class. According to the above source, I'd expect the child to have the padding as set in the global scope:
app-overlay .child {
padding: 0 25%;
}
See full example here: http://plnkr.co/edit/YQOmtxSA9AThCcNmrEJc?p=preview
Note that the padding that is set as global, is not applied to the app-overlay child (even though, it's supposed to win over the component's style).
Is there any step I'm missing?
Ok, that's just plain confusing so I put it here in case someone crashes into it.
Overriding via global scope styling works only for inheritable CSS properties.
If you want to set some non-inheritable property (like padding - see full list here).
So I guess the only way to do this for non-inheritable properties is by either injecting CSS via the template (e.g. calling a file) or by css variables.

How to constrain the scope of inline CSS?

I'm rendering dynamic CSS for each item in a list. Each item will have potentially unique CSS rules for its elements, i.e.
<div id="thing1" class="vegas">
<style>
p {
font-size: 14pt; // this stuff is generated dynamically and i have no control over it
color: green;
}
</style>
<p>
I'm thing 1!
</p>
</div>
<div id="thing2" class="vegas" >
<style>
p {
font-size: 9pt; // so is this stuff
color: red;
}
</style>
<p>
I'm thing 2!
</p>
</div>
Is there a way to wrap each item in a general CSS rule that would limit the scope of each item's associated CSS to the item itself? Like
.vegas > * {
whatever-happens-in-here: stays in here;
}
Or any other way to handle scoping who-knows-what kinds of dynamically particular CSS?
The cascading style sheets are able to handle styling children of particular elements, so:
div#thing1 p {
rule: value; // Only applies to p in div with id="thing1"
}
div#thing2 p {
rule: value; // Only applies to p in div with id="thing2"
}
You need to know about the global styles that browsers have. For eg., find the below list:
font-family
line-height
text-align
The above have their default value as inherit, which means they get the property values from their parent, no matter what. So if you change the parent's property, your child also gets changed.
And you have other properties like:
margin
padding
border
width
height
These do not change, or inherit from the parent. So, if you wanna do something like what you wanted, you need to give your descendants, or immediate children, not to inherit or reset the styles for the children.
Why don't you use inline style attribute?
<p style="color:red;align:center"> Hello </p>
The above CSS style will only be applied to that particular paragraph tag.
You could use inline style statement for other tags and HTML elements too.
Or you could include an external common stylesheet and use the inline statements where you need a variation.CSS applies the latest style description it comes across.So the inline statements would over-ride the common css stylesheet effects.

Why does the javascript style property not work as expected?

<html>
<head>
<style type="text/css">
#wow{
border : 10px solid red;
width: 20px;
height: 20px;
}
</style>
</head>
<body>
<div id="wow"></div>
<script>
var val = document.getElementById("wow");
alert(val.style.length);
</script>
</body>
</html>
This is my code, why is val.style.length 0? Because I defined 3 properties, I expect it to be 3
An element's style property only reflects inline styles. It is essentially a way of getting (and indeed setting) inline styles.
From MDN:
[the style property] is not useful for learning about the element's style in general, since it represents only the CSS declarations set in the element's inline style attribute, not those that come from style rules elsewhere, such as style rules in the section, or external style sheets.
You can get all the styles applied to an element with window.getComputedStyle(element):
alert(window.getComputedStyle(val).length);
However, this probably won't do what you want, since it provides all the style properties on an element, even if they are still the default. In my browser (Chrome, FWIW), that means it always returns 285. This shouldn't be a surprise. The browser has a "built in" stylesheet that provides the defaults for all elements, after all.

Categories

Resources