I am using CartoCSS to style features in a run-time CartoDB layer (as opposed to a pre-made visualization) but it gets messy doing this within JavaScript:
sublayers: [{
cartocss: '#initvisitedfws {bottom/marker-width: 24; bottom/marker-ignore-placement:false; bottom/marker-fill: gold; bottom/marker-allow-overlap:true; top/marker-width: 14; top/marker-fill: #111; top/marker-file: url(clock.svg); top/marker-allow-overlap:true;',
interactivity: 'cartodb_id, parent',
sql: "SELECT * FROM subprojects"
}]
I could keep it somewhat neat by using multi-line strings but that becomes unsightly and tedious in its own way, so I would prefer to edit the CartoCSS in its own native .mss file (especially in tandem with this Sublime Text 2 .mss syntax/autocomplete package).
The ghetto-ish way I am approaching this is with an XMLHttpRequest:
function getMss() {
var xmlhttp;
xmlhttp=new XMLHttpRequest();
xmlhttp.open('GET', "somepath/somefile.mss", false);
xmlhttp.send();
var mapStyle = xmlhttp.responseText;
return mapStyle;
}
...and then calling it from the CartoDB sublayer parameters:
var cartodbParams = {
user_name: 'user_name',
type: 'cartodb',
interaction: true,
sublayers: [{
cartocss: getMss(),
interactivity: 'cartodb_id, parent',
sql: "SELECT * FROM subprojects"
}]
}
This works fine but leads to another HTTP request, and I'm also not optimistic about its maintainability once I have dozens or hundreds of rules and multiple map layers.
So, what is a better solution for using CartoCSS within JavaScript?
2 Answers 2
We use a trick like:
<style type="cartocss/text" id="simple">
#earthquakes_cdbjs_lesson3{
marker-fill: #FF6600;
marker-allow-overlap: true;
...
}
</style>
and after that
cartocss: $("#simple").text();
-
Wow, I would not have thought of that. It's certainly cleaner than JS, so I will try it and mark as correct if it works, but I was hoping to keep the MSS file preserved. I'm sure I could automate something to inline it as you showed, but I'd prefer to point to it within the style tag, like
<style type="cartocss/text" id="simple" src="simple.mss">
. Is that possible?abettermap– abettermap2014年12月24日 02:27:27 +00:00Commented Dec 24, 2014 at 2:27 -
@abettermap no idea, better to take a look at, for instance, less.js which does that: github.com/less/less.js/blob/master/lib/less-browser/… looks like they load those files using XHRjavisantana– javisantana2014年12月25日 11:04:06 +00:00Commented Dec 25, 2014 at 11:04
I accepted @javisantana's answer but would like to provide some of my own findings and didn't want to muck up my original question, so here you go:
Elements
The accepted answer works great and is likely the most semantic approach, but I found myself using plain old <span>
elements (or <div>
or whatever) to house the mss since I was often in WordPress land, did not have access to the <head>
, and did not want to include a <style>
tag in the <body>
(even though it's probably cool, who knows).
To make sure it wasn't visible, I just added a CSS class: .never-show {display: none;}
...to the tag: <span class="never-show">...</span>
Method
I really, really like being able to edit .mss files in a text editor with CartoCSS syntax highlighting, so the Sublime Text package I mentioned before is not something I wanted to leave, nor did I want to use it and then paste the mss into the HTML.
SO, I went the PHP route instead:
<span id="mss_my-sublayer" class="never-show">
<?php
$contents = file_get_contents( 'path/to/my-sublayer.mss');
echo $contents;
?>
</span>
Accessing and Modifying
I had multiple sublayers in the map, so I wanted a reusable function to grab the mss for each, and to modify it when I had a "selected" feature:
function getMss(sublayerName, cartodb_id){
// Get contents of HTML element containing this sublayer's mss, as
// suggested by javisantana:
var mss = $('#mss_' + sublayerName).text();
// New MSS:
var newMss = '';
// If cartodb_id is passed in, append the "selected" style:
if (cartodb_id){
// Note that I needed the zoom stuff here in order to increase
// specificity. This had no impact on the style if I didn't!
newMss = '#' + sublayerName + '[cartodb_id=' + cartodb_id + ']' +
'[zoom>1][zoom<22]' +
'{marker-fill: red;}';
}
}
// newMss is empty if no cartodb_id, which effectively resets
// any sublayers to their default style by only using original mss.
// Otherwise, selected point feature is red:
return mss + newMss;
}
Usage
Not going to get into how to access sublayers, or the data of a clicked feature, so this assumes you're in the know. :) For a sublayer WITHOUT a selected feature, just don't pass in a cdbId to this function. To set the style for a sublayer whose feature was SELECTED:
// var cdbId = cartodb_id of the selected feature
// var mySublayer = the sublayer whose CSS is being reset:
mySublayer.setCartoCSS(getMss('my-sublayer', cdbId));
Here it is in action: http://friendsofsleepingbear.org/sbht-i-map
I'm sure there is much room to expand on this (API, anyone?), but I hope it helps. Thanks again to javisantana for getting this ball rolling, and it seems to be the preferred method of CartoDB as well.