Retrieve or Set Less Variable from JavaScript

Retrieve or set LESS variable from JavaScript?

I think you're doing it in a bit too complicated way ;)

Here's what I propose:

  1. Set a class on your body tag:

    <body class='theme_default'></body>
  2. In your .less file define your themes:

    body.theme_default { base_color: #fff; }
    body.theme_gray { base_color: #ccc; }
    etc..
  3. Using jQuery (or just plain JS) change the class of the body tag upon dropdown state change.

That's how I'd do it (and replace the dropdown with some nice widget ;)
Cheers

Make less variables accessible from javascript?

You could put some special definitions in your CSS to pass through the definitions. These would only be used to pass the variables and nothing else. You'd need to come up with some convention, here I've used div.less-rule-{rule-name}

For example.

div.less-rule-black {
background-color: @black;
}
div.less-rule-grey-darker {
background-color: @greyDarker;
}

You could then pick these up using the JavaScript API for accessing stylesheets. You could put this in a utility class somewhere. This only needs to be done once when all the stylesheets are loaded.

var rules, rule, i, n, j, m, key;
var lessRules = [];
for (i = 0, n = document.styleSheets.length; i < n; i++) {
rules = document.styleSheets[i].cssRules;
for (j = 0, m = rules.length; j < m; j++) {
rule = rules[j];
if (rules[j].selectorText.indexOf('less-rule') !== -1) {
key = /div.less-rule-(.*)/.exec(rules[j].selectorText)[1];
lessRules[key] = rule.style['background-color'];
}
}
}

You can then access the variables by using the key in the hash.

console.log(lessRules['black']);
console.log(lessRules['grey-darker']);

Get less variables list using less.js

This is going to be an unconventional answer as it seems that this data isn't publicly exposed as part of the browser facing API.


tl;dr

  • Save a local copy of the less.js file.
  • Add this function definition somewhere

    function exposeVars(root) {
    var r=root._variables,n=Object.keys(r),m={}
    for(var k of n){m[k]=r[k].value}
    less.variables = m;
    }
  • Add the following call exposeVars(evaldRoot) just before return result on line ~2556.

  • Now less.variables contains all the variables from your file.

Disclaimer: Doing this is not a good idea! It's fine if you're just playing around, debugging or testing something, but don't depend on this hack for anything serious!


The basic aim here was to find the point in the source where the .less files are turned into abstract syntax trees (or some other formal structure).

Jumping straight into the source, I found a ParseTree class. It's a reasonable assumption to guess that it will contain the result of parsing the less file.

I wrote a quick test file and added it to the browser with the appropriate tag. It looks like this:

@myvar: red;
@othervar: 12px;

body {
padding: @othervar;
background: @myvar;
}

I've downloaded a local copy of less.js and added a breakpoint added to line 2556.

I had a poke around in the local scope to see what was available and found the variables in an object called evaldRoot.

evaldRoot = {
_variables: {
@myvar: {
name: '@myvar',
value: Color
},
@othervar: {
name: '@othervar',
value: Dimension
}
}
}

Next job was to work out where this data goes. It seems like the evaldRoot variable is used to generate the resulting CSS (which would make sense, as it contains information such as variables).

if (options.sourceMap) {
sourceMapBuilder = new SourceMapBuilder(options.sourceMap);
result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports);
} else {
result.css = evaldRoot.toCSS(toCSSOptions);
}

Whatever happens, the variables goes out of scope after it is used to generate a string of CSS as result.css.

To expose of these variables, the script needs a small modification. You'll have to expose the variables publicly somehow. Here's an example of doing that.

function exposeVars(root) {
var varNames = Object.keys(root._variables);

var variables = varNames.reduce(function(varMap, varName) {
varMap[varName] = root._variables[varName].value;
}, {});

less.variables = variables;
}

This can be added just before the return statement with the breakpoint.

exposeVars(evaldRoot);
return result;

Now the variables will be available in a name: value object on the global less object.

Sample Image

You could even modify the expose function to return the variables from a call to less.getVars(). Just like your initial suggestion.

function exposeVars(root) {
// ...
less.getVars = function() {
return variables;
};
}

Passing LESS variable to JavaScript

I just wrote a small piece of Javascript that reads this directly from document.styleSheets so it doesn't require any extra HTML. I've only tested it in Chrome.

In my less I have:

#less {
.thumbsWidth { width: @thumbsWidth; }
.thumbsTop { width: @thumbsTop; }
}

My Javascript reads:

var oLess = {};
$.each(document.styleSheets,function(i,sheet){
$.each(sheet.cssRules,function(i,rule){
var sRule = rule.cssText;
if (sRule.substr(0,5)=="#less") {
var aKey = sRule.match(/\.(\w+)/);
var aVal = sRule.match(/(\d+)/);
if (aKey&&aVal) oLess[aKey[1]] = aVal[0]<<0;
}
});
});
console.log(oLess);

Which makes oLess:

{
thumbsWidth: 123,
thumbsTop: 456
}

Currently this only reads the pixel values but you can easily modify it to read out anything.

-update-

Here's a fiddle: http://jsfiddle.net/Sjeiti/VHQ8x/ (with a gist in the resources https://gist.github.com/2948738)

-a very late update-

The above was nice at the time. Now you are better off using CSS variables and element.style.getPropertyValue("--my-var");. See here: https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties#Values_in_JavaScript

Manipulating LESS variables dynamically

What you are looking for is a dynamic way to modify LESS variables. You can do it using less.modifyvars().

Call it when the user clicks on the particular color and set the color to the variables. LESS changes the value of the variable and automatically renders the CSS again with the changed values.

Dynamically changing less variables

The creator of less.js added some features that should allow you to do something like this. Read the comments and the answers here: Load less.js rules dynamically

dynamically change LESS variables in page with JavaScript

Instead of doing the variable replacing client-side, since I was doing it in a Rails app anyway, I moved it server-side. I'm using the Ruby bindings for LESS to parse some LESS that includes my custom variables, rendering the parsed result with content_type: 'text/css' and using an AJAX request to add a stylesheet link to the rendered CSS.



Related Topics



Leave a reply



Submit