29

In JavaScript, you can get the value of a CSS variable using getPropertyValue(property). This function is useful for retrieving variables declared in the :root block.

:root {
    --example-var: 50px;
}

However, if this variable expression contains a function like calc, the getPropertyValue call returns the expression as text rather than computing it, even when using getComputedStyle.

:root {
    --example-var: calc(100px - 5px);
}

How can I get the computed value of a CSS variable that uses a CSS function like calc?

See the example below:

let div = document.getElementById('example');
console.log(window.getComputedStyle(div).getPropertyValue('--example-var'))
:root {
  --example-var: calc(100px - 5px);
}
<div id='example'></div>
FThompson
  • 28,352
  • 13
  • 60
  • 93
  • @TemaniAfif Great point, I hadn't considered how the value can depend on whatever element it is applied to. Your comment answers my question, feel free to post it as such. – FThompson May 21 '19 at 01:03
  • Tested @TemaniAfif comment, it will start working when you use it with `var(...)`. – yqlim May 21 '19 at 01:03
  • what you are searching for is the "used value" or the "calculated value" - the "computed value" is - short words - the "text" that remains for an element/selector after doing all the CSS computing like DOM inheritance / classes and so on. I know - often mixed up :) (see MDN the initial, computed, resolved, specified, used, and actual values) – halfbit Aug 26 '19 at 18:12

3 Answers3

12

Technically you cannot because the computed value is not static and will depend on other properties. In this case it's trivial since we are dealing with pixel value but imagine the case where you will have percentage value. Percentage is relative to other properties so we cannot compute it until it's used with var(). Same logic if we use unit like em, ch, etc

Here is a simple example to illustrate:

let div = document.getElementById('example');
console.log(window.getComputedStyle(div).getPropertyValue('--example-var'))
console.log(window.getComputedStyle(div).getPropertyValue('font-size'))
console.log(window.getComputedStyle(div).getPropertyValue('width'))
console.log(window.getComputedStyle(div).getPropertyValue('background-size'));
:root {
  --example-var: calc(100% + 5px - 10px);
}
#example {
  font-size:var(--example-var);
  width:var(--example-var);
  background-size:var(--example-var);
}
<div id='example'>some text</div>

It's important to note the last case, where background-size has a special behavior when combining percentage and pixel value. You can also clearly see in the first case that the browser will not even compute the 5px - 10px and will do it only when using var().


In case you know that the calc() will only be used with pixel values, you can simply apply it to any property and read it. It will work since the computed value will always be evaluted the same whataver the property is:

let div = document.getElementById('example');
console.log(window.getComputedStyle(div).getPropertyValue('--example-var'))
console.log(window.getComputedStyle(div).getPropertyValue('font-size'))
console.log(window.getComputedStyle(div).getPropertyValue('width'))
console.log(window.getComputedStyle(div).getPropertyValue('background-size'));
console.log(window.getComputedStyle(div).getPropertyValue('background-color'));
:root {
  --example-var: calc(100px - 10px);
  --test:var(--example-var)
}
#example {
  font-size:var(--example-var);
  width:var(--example-var);
  background-size:var(--example-var);
  background-color:var(--example-var);
}
<div id='example'></div>

Of course you need to make sure to consider a property that expect a pixel value or you will have an invalid value (like with background in the previous example).

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • @Kaiido no it's no invalid value. background-size has a special behavior and will consider percentage then pixel, it will not convert the whole result to pixel. Check this : https://stackoverflow.com/a/51734530/8620333 (in the section "Combining pixel and percentage values" I am explaining this) – Temani Afif May 21 '19 at 01:23
  • @Kaiido I was trying to explain that even with trivial case like `10px - 5px` the browser will not evalute this until we use this with var() and the background-size is a good example because we can see the result of only `10px - 5px` unlike the others where we will get the final result. Probably not very clear but I meant a visual result to see the pixel evaluation alone – Temani Afif May 21 '19 at 01:25
  • Ah my bad, I thought that was invalid. But still, I think it's actually the computed of the `var()` even with `width` you can find this `calc(100% + -5px)` by calling `div.computedStyleMap().get("width")`. However, on `font-size` it will return "11px"... – Kaiido May 21 '19 at 01:35
  • @Kaiido I don't know that function but probably there is intermediate states which is somehow logical because the brower will first calculate the `5px - 10px` then it will evalute the percentage value then will do the final sum So maybe the browser is storing them differently but it's sure that this won't happen before using var(). – Temani Afif May 21 '19 at 01:41
8

well a wacky way of doing this is adding


:root {
  --example-var: calc(100px - 5px);
}

#var-calculator {
    // can be fetched through .getBoundingClientRect().width
    width: var(--example-var); 

    // rest of these just to make sure this element
    // does not interfere with your page design.
    opacity: 0;
    position: fixed;
    z-index: -1000;
}


 <custom-element>
  <div id="var-calculator"></div>
</custom-element>


const width = document.getElementById('var-calculator').getBoundingClientRect().width

I don't know if this is applicable for your use case, but I just tested it and it works.

SaMiCoOo
  • 327
  • 2
  • 10
  • **(1)** The element must be in the DOM & occupy space, in ordered to be measured. **(2)** This workaround only works for things which can be measured indirectly by other DOM API methods, so there are much more cases where it's not applicable. – vsync Jan 03 '21 at 19:23
  • And (3) this doesn't work for values without unit (e.g. intermediate vars) because those will be treated as invalid CSS. – Mike 'Pomax' Kamermans May 14 '22 at 17:56
  • There are some CSS units that are calculated based off parents, so you would need this calculator to be in the same DOM position to always ensure the correct values. – William Park Aug 03 '23 at 15:14
2

Value will get computed when assigned (including programmatically) to some real property:

const div = document.getElementById('example');
div.style.width = 'var(--example-var)';
console.log(window.getComputedStyle(div).getPropertyValue('width'));
:root {
  --example-var: calc(100px - 5px);
}
<div id='example'></div>
Andrew Svietlichnyy
  • 743
  • 1
  • 6
  • 13
  • Will not work with most other properties. Try `box-shadow` for example. (`style.boxShadow`) – vsync Jan 03 '21 at 19:27