Coffeescript helpers for d3.js

fp.js is a tiny library to help keep d3.js dom manipulation code clean.

Update (December 29, 2013) #

Upgraded to support the format at the bottom. Check out this rewrite of the d3.js Grouped Bar Chart Example using fp.js.

I write a lot of d3.js code and most dom manipulation code got complicated and hard to maintain. With fp.js attributes, styles and events can be set easily. But the most important fact is that FP.js lets you maintain indentation in coffescript code that matches the dom structure.

numbers = [5, 3, 23, 6, 7, 0, 1, 25, 11, 19, 20]
chartHeight = 300

chart = d3.select("body")
 .append("svg")
 .attr("width", "100%")
 .attr("height", "100%")

bars = chart.selectAll("g.bar").data(numbers).enter()
 .append("g")
 .attr("class", "bar")

bars.append("rect")
 .attr("y", (d) -> chartHeight - d * 10)
 .attr("x", (d, i) -> i * 20)
 .attr("width", 20)
 .attr("height", (d) -> d * 10)

bars.append("line")
 .attr("class", "budget")
 .attr("y1", (d) -> chartHeight - d * 9)
 .attr("y2", (d) -> chartHeight - d * 9)
 .attr("x1", (d, i) -> i * 20)
 .attr("x2", (d, i) -> i * 20 + 20)
 .attr("stroke", (d) -> if d < 20 then "white" else "#aaa")

Above is plain code to draw a simple bar chart with d3.js, and below is the code with FP.js.

numbers = [5, 3, 23, 6, 7, 0, 1, 25, 11, 19, 20]
chartHeight = 300

FP d3, 'select', 'body', ->
 FP @, 'svg', width: "100%", height: "100%", ->
   FP @, 'selectAll', 'g.bar', ->
    FP @, 'data', numbers, ->
     FP @, 'enter', ->
      FP @, 'g.bar', ->
       FP @, 'rect',
        y: ((d) -> chartHeight - d * 10)
        x: ((d, i) -> i * 20)
        width: 20, height: ((d) -> d * 10)
       FP @, 'line.budget',
        y1: ((d) -> chartHeight - d * 9)
        y2: ((d) -> chartHeight - d * 9)
        x1: ((d, i) -> i * 20)
        x2: ((d, i) -> i * 20 + 20)
        stroke: ((d) -> if d < 20 then "white" else "#aaa")

I am planning to remove the clutter of FP @, so that it’ll look like the following; but for that I will have to either use with keyword or create functions for select, rect, line, etc, in a local context similar to coffeekup.

FP d3, ->
 select 'body', ->
  svg width: "100%", height: "100%", ->
    selectAll 'g.bar', ->
     data numbers, ->
      enter ->
       g '.bar', ->
        rect
         y: ((d) -> chartHeight - d * 10)
         x: ((d, i) -> i * 20)
         width: 20, height: ((d) -> d * 10)
        line '.budget',
         y1: ((d) -> chartHeight - d * 9)
         y2: ((d) -> chartHeight - d * 9)
         x1: ((d, i) -> i * 20)
         x2: ((d, i) -> i * 20 + 20)
         stroke: ((d) -> if d < 20 then "white" else "#aaa")

Another option is to use @, which cleaner and not that messy, but code readers might get confused whether @ is referring to the object you are making the call from. Also, using a one letter name such as $ or _ for fp.js will give a similar code - $.rect, $.line, etc.

FP d3, ->
 @select 'body', ->
  @svg width: "100%", height: "100%", ->
    @selectAll 'g.bar', ->
     @data numbers, ->
      @enter ->
       @g '.bar', ->
        @rect
         y: ((d) -> chartHeight - d * 10)
         x: ((d, i) -> i * 20)
         width: 20, height: ((d) -> d * 10)
        @line '.budget',
         y1: ((d) -> chartHeight - d * 9)
         y2: ((d) -> chartHeight - d * 9)
         x1: ((d, i) -> i * 20)
         x2: ((d, i) -> i * 20 + 20)
         stroke: ((d) -> if d < 20 then "white" else "#aaa")
 
102
Kudos
 
102
Kudos

Now read this

Forestpin Enterprise Redesign

We released the new version of Forestpin Enterprise last week. The new version is a complete rewrite of both the backend and UI. The backend was rewritten to be faster and to introduce a bunch of new features and analytics. The user... Continue →