Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created June 5, 2014 16:02
Show Gist options
  • Save bennadel/b8df0da573a18897346e to your computer and use it in GitHub Desktop.
Save bennadel/b8df0da573a18897346e to your computer and use it in GitHub Desktop.
Executing JavaScript In The LESS CSS Precompiler
@foo: `javascript` ;
@foo: ~`"surrounding quotes will be removed"`
h1 {
content: "This is yo' content!";
}
h2 {
content: "THIS IS YO' CONTENT!";
}
// Use the backtick character to run JavaScript directly in LESS CSS. We are using a
// Function here because LESS calls (my theory) .toString() on the function and stores
// the return value. It seems that only Functions returns their "source code" when
// .toString() is called, which allows us to reuse the JavaScript in other JavaScript
// code block instances.
@api: `function(){
// Set up the API that we want to expose in other JavaScript contexts.
var api = {
// This is just me testing some JavaScript stuff.
getContent: function() {
return( "This is yo' content!" );
},
// When executing a JavaScript expression, you can only execute one expression
// at a time (ie, no semi-colons). Unless, you are in a function. The point of
// the run() function is to allow the calling context a way to enter a function
// context and get access to the API at the same time. The API is injected as the
// only argument to the given callback.
// --
// NOTE: The callback MUST RETURN A VALUE so that LESS can get the value of it.
run: function( callback ) {
return( callback( api ) );
}
};
// Return the public API. Since this JavaScript expression is return the parent
// Function, it will have to invoked in a different JavaScript context to actually
// get access to the API.
return( api );
}` ;
// I assign the API the global namespace, "api". This could have been done in the
// previous JavaScript code block; but, I kind of liked the idea of breaking it out into
// its own rsponsability.
// --
// NOTE: I am using a self-invoking function here to help ensure that "this" points to
// the global context and not to the context of the evaluation (if its different).
@apiGlobalInjector: `(function() {
// Inject the API and store it in the global object.
this.api = (@{api})();
// The JavaScript HAS TO RETURN something so LESS CSS can assigne the variable value.
return( "Injected in global" );
})()` ;
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //
h1 {
content: `api.getContent()` ;
}
h2 {
content: `api.run(function( api ) {
return( api.getContent().toUpperCase() );
})` ;
}
@ccwq
Copy link

ccwq commented Oct 31, 2014

good feature

@zvuv
Copy link

zvuv commented Nov 3, 2016

I enjoyed this post and will experiment a bit myself. Thank you. Two thoughts:

  1. Probably not a good idea to depend on an undocumented feature in production code.

  2. This is a clear demonstration why you should not expose any part of your private API. Sooner or later some clever programmer is going to worm his way in through the smallest opening that you might leave and proceed to make your app do things you never imagined.

:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment