VARUNA JAYASIRI

@vpj

Coffeescript helpers for d3.js

December 28, 2013

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

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")

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.

///Coffeescript helpers for d3.js <<https://github.com/vpj/FP(fp.js)>> is a tiny library to help keep <<http://d3js.org/(d3.js)>> dom manipulation code clean. >>> **Update (December 29, 2013) Upgraded to support the format at the bottom. Check out <<http://bl.ocks.org/vpj/8168794(this)>> rewrite of the d3.js <<http://bl.ocks.org/mbostock/3887051(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. ```coffeescript 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. ```coffeescript 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 <<https://github.com/mauricemach/coffeekup/blob/master/src/coffeekup.coffee(coffeekup)>>. ```coffeescript 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. ```coffeescript 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")