MarcZ
MarcZ

Reputation: 983

Accessing a CSS custom property (aka CSS variable) through JavaScript

How do you get and set CSS custom properties (those accessed with var(…) in the stylesheet) using JavaScript (plain or jQuery)?

Here is my unsuccessful try: clicking on the buttons changes the usual font-weight property, but not the custom --mycolor property:

<html>
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
  <style>
    body { 
      --mycolor: yellow;
      background-color: var(--mycolor);
    }
  </style>
</head>
<body>

  <p>Let's try to make this text bold and the background red.</p>
  <button onclick="plain_js()">Plain JS</button>
  <button onclick="jQuery_()">jQuery</button>

  <script>
  function plain_js() { 
    document.body.style['font-weight'] = 'bold';
    document.body.style['--mycolor'] = 'red';
  };
  function jQuery_() {
    $('body').css('font-weight', 'bold');
    $('body').css('--mycolor', 'red');
  }
  </script>
</body>
</html>

Upvotes: 97

Views: 50435

Answers (5)

Paul Cantrell
Paul Cantrell

Reputation: 9314

Answers involving getPropertyValue() are the correct ones if you want the raw string value of the variable. However, this raw string value:

  • could be expressed in any units, and
  • will not be evaluated if it is an expression, i.e. will return "calc(1px + 1px)" instead of "2px".

If you need calc evaluation and/or unit normalization, here is a hack that will always give you pixels:

function evaluateCssLength(lengthExpr, contextElem) {
    // getComputedStyle(…).getPropertyValue(…) returns the raw expression for a CSS calc value,
    // i.e. a string of the form "calc(…)"
    //
    // To evaluate the calc, we create a temporary element and give it a max-width, which is a CSS
    // property that (1) expects a length and (2) will always resolve to the same actual pixel value
    // regardless of its containing element and surrounding CSS. We read the max-width back, now
    // computed and expressed as pixels, then discard the element.

    const measureElem = document.createElement("div");
    contextElem.appendChild(measureElem);
    measureElem.style.maxWidth = lengthExpr;
    const length = parseFloat(getComputedStyle(measureElem).maxWidth);
    contextElem.removeChild(measureElem);
    return length;
}

Use it as follows:

const containerElem = document.querySelect(".whatever");

// All the following will return pixels expressed as a float:
evaluateCssLength("37em", containerElem)
evaluateCssLength("var(--some-property)", containerElem)
evaluateCssLength("calc(100vh - 17ex * var(-foo))", containerElem)

Upvotes: 0

Q10Viking
Q10Viking

Reputation: 1121

You can use getComputedStyle function to get css variables,Here is a example.

const colors = document.querySelectorAll(".color");
const result = document.getElementById("result");
colors.forEach((color) => color.addEventListener("click", changeColor));

function changeColor(event) {
  const target = event.target;
  // get color
  const color = getComputedStyle(target).getPropertyValue("--clr");
  document.body.style.backgroundColor = color;

  // active color
  colors.forEach((color) => color.classList.remove("active"));
  target.classList.add("active");
  
  result.textContent = getComputedStyle(target).getPropertyValue("--clr")
  
}

result.textContent = "#1dd1a1";
body{
  background-color: #1dd1a1;
}

.colors{
  position: absolute;
  padding: 2rem;
  display: flex;
  gap: 1rem;
}

.color{
  display: inline-block;
  width: 2rem;
  height: 2rem;
  background-color: var(--clr);
  border-radius: 50%;
  cursor: pointer;
  transition: $time-unit;

  }
  .color.active{
    border: .2rem solid #333;
    transform: scale(1.25);
  }
<h1>Click to change Background</h1>
<section class="colors">
  <span class="color active" style="--clr: #1dd1a1"></span>
  <span class="color" style="--clr: #ff6b6b"></span>
  <span class="color" style="--clr: #2e86de"></span>
  <span class="color" style="--clr: #f368e0"></span>
  <span class="color" style="--clr: #ff9f43"></span>
</section>

Current Color: <span id="result"></span>

Upvotes: 2

CMedina
CMedina

Reputation: 4222

You can use document.body.style.setProperty('--name', value);:

var bodyStyles = window.getComputedStyle(document.body);
var fooBar = bodyStyles.getPropertyValue('--foo-bar'); //get

document.body.style.setProperty('--foo-bar', newValue);//set

Upvotes: 108

colxi
colxi

Reputation: 8670

The native solution

The standard methods to get/set CSS3 variables are .setProperty() and .getPropertyValue().

If your Variables are Globals (declared in :root), you can use the following, for getting and setting their values.

// setter
document.documentElement.style.setProperty('--myVariable', 'blue');
// getter
document.documentElement.style.getPropertyValue('--myVariable');

However the getter will only return the value of a var, if has been set, using .setProperty(). If has been set through CSS declaration, will return undefined. Check it in this example:

let c = document.documentElement.style.getPropertyValue('--myVariable');
alert('The value of --myVariable is : ' + (c?c:'undefined'));
:root{ --myVariable : red; }
div{ background-color: var(--myVariable); }
  <div>Red background set by --myVariable</div>

To avoid that unexpected behavior you have to make use of the getComputedStyle()method , before calling .getPropertyValue(). The getter will then, look like this:

getComputedStyle(document.documentElement,null).getPropertyValue('--myVariable');

In my opinion, accessing CSS variables should be more simple, fast, intuitive and natural...


My personal approach

I've implemented CSSGlobalVariablesa tiny (<3kb) javascript helper which automatically detects and packs into an Object all the active CSS global variables in a document, for easier access & manipulation.

// get the document CSS global vars
let cssVar = new CSSGlobalVariables();
// set a new value to --myVariable
cssVar.myVariable = 'red';
// get the value of --myVariable
console.log( cssVar.myVariable );

Any change applied to the Object properties, is translated automatically to the CSS variables.

Available in : https://github.com/colxi/css-global-variables

Upvotes: 55

slevy1
slevy1

Reputation: 3832

The following example illustrates how one may change the background using either JavaScript or jQuery, taking advantage of custom CSS properties known also as CSS variables (read more here). Bonus: the code also indicates how one may use a CSS variable to change the font color.

function plain_js() { 
    // need DOM to set --mycolor to a different color 
    d.body.style.setProperty('--mycolor', 'red');
     
    // get the CSS variable ...
    bodyStyles = window.getComputedStyle(document.body);
    fontcolor = bodyStyles.getPropertyValue('--font-color'); //get
 
    // ... reset body element to custom property's new value
    d.body.style.color = fontcolor;
    d.g("para").style["font-weight"] = "bold";
    this.style.display="none";
  };

  function jQuery_() {
    $("body").get(0).style.setProperty('--mycolor','#f3f');
    $("body").css("color",fontcolor);
    $("#para").css("fontWeight","bold");
    $(this).css("display","none");
  }
  
var bodyStyles = null;
var fontcolor = "";
var d = document;

d.g = d.getElementById;
d.g("red").addEventListener("click",plain_js);
d.g("pink").addEventListener("click",jQuery_);
:root {
     --font-color:white;
     --mycolor:yellow;
    }
    body { 
      background-color: var(--mycolor);
      color:#090;
    }
    
    #para {
     font: 90% Arial,Helvetica;
     font-weight:normal;
    }
    
    #red {
      background:red;
    }
    
    #pink {
      background:#f3f;
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<p id="para">Let's try to make the background red or pink and change the text to white and bold.</p>
  <button id="red">Red</button>
  <button id="pink">Pink</button>

Note that with jQuery, in order to set the custom property to a differnt value, this response actually holds the answer. It uses the body element's get() method which allows access to the underlying DOM structure and returns the body element, thereby facilitating the code setting the custom property --mycolor to a new value.

Upvotes: 6

Related Questions