Get element's resolved height CSS property value; recognize 100% - javascript

The values returned by getComputedStyle are resolved values. These are usually the same as CSS 2.1’s computed values, but for some older properties like width, height, or padding, they are instead the same as used values.
-- MDN: window.getComputedStyle() notes
So is it currently possible to get the resolved value of height as it was specified in stylesheet?
I.e. know that some element's computed ..px (= used) height was specified as height: 100%; in style sheet? (100% being the resolved value in question.)
Is there some new specification regarding this problem in consideration?
Edit 2020-08-17: see very similar question and excellent answer from 2012: Is there a cross-browser method of getting the used css values of all properties of all elements?
(Sadly, noting seem to ave changed since then.)

No, there is no specification or functionality that supports or enables this method. There are plenty of ways to go the other direction, including...
getBoundingClientRect()
offsetHeight
getComputedStyle
... but none that will retrieve the exact percentage specified in the CSS, unfortunately.
You can try, as I've done below.
RegEx
If your height is specified as an inline style, you could RegEx it out of the attribute like so1:
let elem = document.getElementsByTagName("div")[0];
let re = /(?<=\height:\s+?)[^\;]+/i;
console.log(re.exec(elem.getAttribute("style"))[0]);
<div style="color: black; height: 100%; background: white;"></div>
This is terrible practice, though, and could be janky if there are multiple width declarations (which should never happen, but we're already in "bad code land", so why not?). Of course, this ignores the fact that inline styles should generally be avoided in the first place, so this probably won't apply to you.
Bounding calculations
It's also possible to calculate the value yourself by comparing the height of the element with the height of its parent.
let elem = document.getElementById("inner");
let pare = elem.parentElement;
let hrat = `${100*(elem.offsetHeight / pare.offsetHeight)}%`;
console.log(hrat);
#container {
height: 300px;
width: 400px;
background: repeating-linear-gradient(45deg, rgba(255,0,0,0.35), rgba(255,0,0,0.35) 10px, rgba(255,0,0,0.1) 10px, rgba(255,0,0,0.1) 20px), linear-gradient(white, white);
}
#inner {
height: 200px;
width: 400px;
background: darkgreen;
mix-blend-mode: hue;
}
<div id="container">
<div id="inner"></div>
</div>
If you add borders, margin, or padding, or the element adapts to the size of its content, though, this calculation will be incorrect.
Conclusion
In conclusion, everything is jank.
In my opinion, you'd be better off not fighting with CSS and JavaScript to coerce the value from the available information, and working out a way to do without the value. I've tried to do this kind of thing many times, so be forewarned: this way lies madness.
1RegEx lookbehinds are not even remotely close to being fully supported, so shouldn't be used in production.

You could read the stylesheet rule itself. If you know the selector that sets an elements width/height you can do this:
.box {
width: 12vw;
background: red;
}
<div class="box">red</div>
var sheet = document.styleSheets[0];
var rules = sheet.rules;
for (var i = 0; i < rules.length; i++) {
if (rules[i].selectorText == '.box') {
console.log(rules[i].style.width);
}
}
This will give you the 12vw you're looking for.
Depending on how your elements are defined, you could in theory create a helper function that gets these values for you by looping through the elements classList.

Related

CSS styles are applied and displayed but JS doesn't recognize the CSS applied

Something weird is going on and I'm not sure what it is. I created a bunch of elements and I want to get the width of my progress bar so I can work with it in my JS code.
In my JS I do:
var bar = document.getElementById('progressBar');
console.log(bar.style.width); //empty string
however in my CSS I have
#progressBar{
width: 600px;
border: 2px solid black;
}
and I can clearly see the 600px container and the border around it in the browser, but for some reason, JS doens't know about these CSS settings.
Any ideas what the problem might be?
EDIT: This is different from the suggested duplicate - the problem is not that I don't know how to the get the value, the problem is that the style.value doesn't get me what I expect.
That is correct behaviour.
The style property of a DOMElement is responsible for inline styles, not the actual computed values. To get the width, use getClientRects or getBoundingClientRect.
e.g.
var bar = document.querySelector('.bar');
console.log(bar.style.width); // empty string
console.log(bar.getBoundingClientRect().width); // 100px
.bar {
width: 100px;
height: 20px;
background: blue;
}
<div class='bar'></div>
You may also be interested in:
How do I get a computed style?

Javascript Assignment Operators with transform

if I have element,and I want to translate it for 50px more than the current value.Why I can't do something like this?
element.style.transform="translateX(+=50px)"
Is there a way to do something like that?
If your transform is set in a stylesheet, element.style.transform will return an empty string. You must use getComputedStyle.
To get the x translation as an integer use:
var transform = parseInt(window.getComputedStyle(element, null).transform.split(',')[4]);
Then use this to increment by 50 pixels:
element.style.transform = "translateX(" + (transform + 50) + "px)"
Here is a fiddle: https://jsfiddle.net/uhuh9r64/
JS does not understand CSS and vice versa, but CSS can solve this on its own.
Unknown to most people, the ECMAScript (proper name for JS) specification and the CSS and DOM standards are maintained by different groups and have little ability for interoperation. Most interfacing between them happens with JS invoking the APIs, but JS does not typically consider or care what API it invokes.
That is, JS has no explicit support for CSS and sees this as a string.
That doesn't matter when you can transform elements using pure CSS, without JS needing to be involved (although you can apply transforms from JS). A simple position transform, even in 2D, can easily move an element down by 50px.
The translateX() is a css function used to move the element horizontally on the plane. This transformation is define by a length that define how much it moves in horizontally. The sample code example is for you in the bellow.....
exm.html
<div>
<p>welcome</p>
<p class="transformed">bar</p>
<p>thanks</p>
</div>
exm.css
p {
width: 50px;
height: 50px;
background-color: teal;
}
.transformed {
transform: translateX(10px);
background-color: blue;
}

Referencing another element in CSS / doing math in CSS

I have two divs nested inside of a div.
<div id='outter' class='one'>
<div id='inner'></div>
<div id='button' class='bttn'>Click me!</div>
</div>
The outter div's height is a percentage of the page. I'd like one of the inside div's height to be a fixed difference away the outter div (i.e. $('#inner').height($('#outter').height() - 35)), because the second inner div is essentially a button with fixed height (35). I'd like this to happen even when I change the height (through CSS triggers (:hover/adding a class/etc. so I can use Transitions) or otherwise).
I googled around a bit and saw Less might be an answer, but from what I can tell it compiles in to static values, but I still need to use percentages, since I want this app to work/feel the same on any screen size.
I have examples of what I'm currently doing/how I'm thinking about it in some jsfiddles.
Current 'solution': http://jsfiddle.net/L9NVj/5/ (End heights are what I want them to be, but the transition looks terrible)
Idealistic 'solution': http://jsfiddle.net/L9NVj/6/ (End heights are wrong, but the inner div hugs appropriately)
Potential solution: http://jsfiddle.net/L9NVj/7/ (This hides the inner div on click and then shows it again when the appropriate size has been reached)
Any help/thoughts/insights would be greatly appreciated!
Consider absolute-positioning the inner elements, since the outer's size isn't controlled by their size/position.
#inner {
position: absolute;
top: 2px;
left: 2px;
right: 2px;
bottom: 35px;
/* ... */
}
.bttn {
position: absolute;
bottom: 2px;
left: 2px;
/* ... */
}
Example: http://jsfiddle.net/L9NVj/9/
How about conflicting absolute positioning. To do it, you'd just need to set the top, bottom, left and right of the #inner element and then transition those. That will maintain the distances around the edges of the element, and allow other positioning as well.
Note that while you don't need to actually calculate the value in this case, in the future, calc() can be used to calculate a dynamic value in CSS. In that case, you could do something like height: calc(100% - 37px); to get the same effect.
CSS3's calc() is the answer you're looking for, in combination with a JavaScript fallback for browsers that don't support calc(). In your 'Idealistic solution' fiddle, change your CSS height definition to the following:
height: -webkit-calc(100% - 35px);
height: calc(100% - 35px);
While normally you should include all prefixes (and you still may need to, depending upon your level of browser support), according to Can I Use, the only browsers that currently need prefixing are -webkit browsers.
What I would do with this knowledge is the following: grab a feature detection script, I really like Modernizr and detect to see if calc() is available in the browser. Modernizr has a non-core detect for calc() that you can use. Use that CSS in your CSS file as the default, then using a resource loader such as yepnope (comes with Modernizr), load in a JS solution if calc() isn't available.
Of your JavaScript solutions, I'd probably suggest your "Potential Solution" option, but instead of jQuery's hide() and show(), set opacity to 0 and 1 and use a CSS3 transition to transition between the two. I'd also not rely upon a timeout, but rather use the transitionend JavaScript event.
I edited your first jsfiddle little bit i think that's what you wanted. Just added line.
$(window).resize(function(){$('#inner').height($('#outter').height() - 35)});
jsfiddle:http://jsfiddle.net/Qqb3g/
You may have to some workaround to make transition smooth when button the button is clicked.
you need to calculate the inner div in %, so it can resize belong outer div, change your js code to this :
//calculating inner div'x height in % of outer
$('#inner').height((100 - (33/$('#outter').height() * 100)) + '%');
$('#button').click(function () {
$('#outter').toggleClass('two');
});
give a try to DEMO

How can I tell if a CSS property was manually set by page author?

I'd like to be able to tell if specific CSS properties (width, height, margin, padding, font-size, …) were set by the page author for a DOM element. My goal is to not change elements that have had their dimensions explicitly set, but to change those that have not.
function isPropertySet(elem, "width") should return true if the page author set the width CSS property either inline (style="width: 100px;"), or via a stylesheet.
This is not straightforward because the layout engine infers these values, and it seems that however I try to access them the browser has supplied values.
For instance, I've tried getComputedStyle(elem).getPropertyValue("width"), but this returns the computed width (not surprising given the name).
The style property, e.g. elem.style.width, isn't sufficient because it doesn't include properties set in stylesheets.
Before I go to the immense pain of searching through the stylesheets, does anyone have a better way?
Thanks!
If you want to supply default style set for the elements which were not customized, than the easiest way would be to create your own stylesheet and put it at the top, before any other CSS files. This way, every element customized elsewhere will overwrite your default styles. Be careful with the cascading order: not only your styles should precede every other, but the selectors should also be general enough.
If, on the other hand, for some reason you want to know through JavaScript whether the element was customized, then it's not possible, unless you want to compare the particular style with the default one, given that default styles may vary from browser to browser. For example, in Firefox, the default style for <h1/> is:
h1 {
display: block;
font-size: 2em;
font-weight: bold;
margin: .67em 0;
}
while Chrome has a slightly different style:
h1 {
display: block;
font-size: 2em;
-webkit-margin-before: 0.67em;
-webkit-margin-after: 0.67em;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
font-weight: bold;
}
This creates a problematic edge case. Imagine I want all <h1/> be font-weight:normal; font-size: 200%;, but there is one specific title on one specific page which I want to be exactly 2em and be displayed in bold. You'll think that the title is not customized, and override its style, while in fact, it was customized, the size and weight being set on purpose.
If you aren't worried about inherited styles and only the specific DOM element then maybe you can dynamically create the specific element&classname (so it only has CSS styles) and then examine it's width using the above methods and compare it?
The best way I have found to answer this question from JavaScript is to create a temporary element, that is not attached to the dom, and read my default values from that. I then test the default values against the values read from the element I'm testing (using jQuery or getComputedStyle) - if they compare then it's a best guess they haven't been set. Obviously this has a downside in the fact that if the element has had it's property set to the exact same value as the default you can't tell the difference.
Something like this
function hasDefaultStyle(elm, prop) {
var def = $('<'+$(elm).attr('tagName')+' />').css(prop);
return $(elm).css('prop') == def;
}
When dealing with different dimension metrics i.e. percent, cm, em and so on - these have to be dealt with in a different way—on browsers other than FireFox at least—due to the computed problem you mention.
FireFox does the right thing in my opinion and that when you request styles from an element that hasn't been placed in the dom it returns the original values i.e. like 50%.
For more information on how to solve at least the percent problem you can see my answer here:
Determine whether element has fixed or percentage width using JavaScript
It is rather ridiculous that such methods are necessary however :/

Appended control's CSS

I've developed a JavaScript Bookmarklet that have appended div to the current page.
But problem is that, when div and its content loaded because of pages' original CSS codes (Bookmarklet has its own CSS as well), my div's appearance corrupts.
I mean, on every page, some of elements looks different (sometimes labels' heights, sometimes textarea's backgroundcolor, etc.)
Is there a way to correct this fault that you know? It can be a CSS or JavaScript solution.
Is there any way to correct this fault that you know?
Yes, define every relevant property inside the DIV and !important:
<div style="width: 300px !important; line-height: 1em !important....">
there is no other perfectly fail-safe way. All external widgets I've seen do it this way.
It sounds like what you're saying is the page's CSS overrides your default styling of the content you inject. This is probably due to one of two things: not specifying every style attribute (and using relative values) for your content or your specificity isn't high enough.
Specify every style attribute
Let's say your content looks like this:
<div id="#cool-bookmarklet">Here is some content</div>
And your CSS looks like this:
#cool-bookmarklet {
color: #000000;
font-size: 80%;
}
Two problems with the above. Only two style attributes are declared, therefore every other attribute will be inherited from other styles. What if the page had CSS like this?
div {
width: 70%;
background-color: #000000;
}
You'll have problems because that CSS applies to your content (the div). Your div 'cool-bookmarklet' will be 70% the width of its parent and have a black background color. Not good.
Also, the font-size is a relative value, meaning it will be 80% of whatever the inherited value is. So if the font-size specified by the page is 10px, your font will be 8px. Here it's probably best to use explicit sizing to avoid any issues of inherited styles.
This is how your CSS should look to avoid inherited styles:
#cool-bookmarklet {
color: #000000;
font-size: 12px;
width: 400px;
background-color: #ffffff;
margin: 0;
padding: 0;
font-weight: normal;
/* etc, etc */
}
Specificity
There's a part of CSS that many people don't learn (and took me a while to understand) and it's called specificity. Specificity is used by browsers to determine what CSS styles to apply to elements when two selectors conflict.
From the CSS spec:
A selector's specificity is calculated as follows (from the spec):
Count 1 if the declaration is from is a 'style' attribute rather than a rule with a selector, 0 otherwise (= a) (In HTML, values of an element's "style" attribute are style sheet rules. These rules have no selectors, so a=1, b=0, c=0, and d=0.)
Count the number of ID attributes in the selector (= b)
Count the number of other attributes and pseudo-classes in the selector (= c)
Count the number of element names and pseudo-elements in the selector (= d)
Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity.
So a = styles in a style attribute of a html element. b = id selectors, c = class names and attributes, d = tag names. The selector with the highest specificity 'wins' if two selectors target the same element.
It's a little confusing, but you get the hang of it after a few tries.
Let's say you have these two rules in your CSS:
#cool-bookmarklet { color: red; }
div { color: blue; }
And the content:
<div id="cool-bookmarklet">Here is some content</div>
The selector '#cool-bookmarklet' would have a specificity of 100 (a=0, b=1, c=0, d=0). The selector 'div' has a specificity of 1 (a=0, b=0, c=0, d=1). '#cool-bookmarklet' would win and the div will have red text.
This is relevant because if your bookmarklet injects a stylesheet to style your content, other CSS on the page could override it if the specificity is higher. It's often easiest to give your content an ID (which has a high specificity 'b'). This allows you to target your content and not worry about other styles overriding.
Hope that helps!
I don't fully understand the question. Perhaps a little snippet would help?
If you are worried that existing styles might override the styles on the elements you are dynamically adding, you can add the !important tag. But if the styles are inline (which is invariably what happens with bookmarklets) there should be no need for that.

Categories