5

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?

6

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 Dec 24 '14 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 XHR – javisantana Dec 25 '14 at 11:04
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.