D3.js Wiki v1.0
Made with by Crypteia Networks

What is this document and who is it intended for?

Since Mike Bostock, creator of the D3 library, shared the excellent D3.js library with all of us, we thought we should give something back in return to the community. Although this document has been initially created for internal use, we felt that sharing it might help some people struggling to get the grasp of the basics (and even some more advanced features) of the vast D3.js library. There might be more efficient ways to handle specific functions, but while we gain more experience, this document will keep evolving with us. Enjoy!

Changelog
Updated on March 16th, 2016

  • First version release

D3 Basic Usage

D3 uses operations on selections to create, remove elements depending on the provided dataset or apply attributes and properties to selected elements.

dataset = [4, 8, 7, 42, 3]                                                    # Define dataset - array length = 5

d3box = d3.select('body')                                                     # Select body
  .append('div')                                                              # Append parent element container
  .selectAll('div')                                                           # Select all children div elements (results = 0 since parent was just appended)
  .data(dataset)                                                              # Load dataset
  .enter()                                                                    # Creates placeholder DOM nodes equal to dataset length and binds dataset values
  .append('div')                                                              # Create enough elements so that their number is at least equal to dataset length
  .attr('class', 'd3box')                                                     # Add class for easier selection, styling
  .text((d) ->                                                                # Add text inside d3-box with dataset value
    d
  )

If a dataset is being updated, then the data operator compares the new dataset length to the original and updates node elements depending on the result of the data operation.

# Based on previous example, original dataset = [4, 8, 7, 42, 3]

# Example 1
dataset = [32, 12]                                                            # Updated dataset - array length = 2 

d3.selectAll('.d3box')                                                        # Selects and counts elements to apply the updated dataset, count = 5
  .data(dataset)                                                              # Data operator updates with new dataset and compares new dataset length to previous, result = -3 
  .text((d) ->                                                                # Updates text inside d3-boxes with new dataset values
    d
  )
  .exit()                                                                     # Removes 3 additional DOM node placeholders
  .remove()                                                                   # Removes 3 additional elements
  
# Example 2
dataset = [32, 12, 32, 45, 67, 31, 34, 2]                                     # Updated dataset - array length = 8 

d3.selectAll('.d3box')                                                        # Selects and counts elements to apply the updated dataset, count = 8
  .data(dataset)                                                              # Data operator updates with new dataset and compares new dataset length to previous, result = 4 
  .enter()                                                                    # Creates placeholder DOM nodes equal to dataset length and binds dataset values, creates 4 new nodes
  .append('div')                                                              # Create enough elements so that their number is at least equal to dataset length, adds 5 new elements
  .attr('class', 'd3box')                                                     # Add class for easier selection, styling
  .text((d) ->                                                                # Updates text inside existing d3-boxes and creates text for newly added d3-boxes
    d
  )

Both enter().append() and exit().remove() functions can be run under the same selection and d3 decides based on the updated dataset length if it has to add or remove nodes/elements.

# Based on previous example, original dataset = [4, 8, 7, 42, 3]
# Example 3
dataset = [32, 12, 32, 45, 67, 31, 34, 2]                                     # Updated dataset - array length = 8 

d3.selectAll('.d3box')                                                        # Selects and counts elements to apply the updated dataset, count = 8
  .data(dataset)                                                              # Data operator updates with new dataset and compares new dataset length to previous, result = 4 
  .enter()                                                                    # Creates placeholder DOM nodes equal to dataset length and binds dataset values, creates 4 new nodes
  .append('div')                                                              # Create enough elements so that their number is at least equal to dataset length, adds 5 new elements
  .attr('class', 'd3box')                                                     # Add class for easier selection, styling
  .text((d) ->                                                                # Updates text inside existing d3-boxes and creates text for newly added d3-boxes
    d
  )
  .exit()                                                                     # Removes nothing
  .remove()                                                                   # Removes nothing

Chart Dimensions

Whether the chart has fixed dimenions or not depends on the dimensions of the parent container. Fixed dimenions charts are forced when wrapping inside fixed-container-wrapper class.

parentWidth = parentContainer.parent().width()
parentHeight = parentContainer.parent().height()
containerHeight = parentContainer.height()
margin = 
  top: 20
  right: 20
  bottom: 50
  left: 50
  
width = parentContainer.width() - (margin.left) - (margin.right)              # Chart Width
height = parentHeight - (margin.top) - (margin.bottom)                        # Chart Height


$(window).on 'resize', ->
  width = parentContainer.parent().width() - (margin.left) - (margin.right)
  height = parentContainer.parent().height() - (margin.top) - (margin.bottom)

SVG Element

The SVG is the element container that holds the chart. Although SVG are more practical for chart/graphs creation, any kind of element can be used as a container. The main advantage by using SVG is the accelerated SVG transforms of its contained elements.

svg = d3.select(this)                                                         # this = ember defined element
  .append('svg')                                                              # Creates SVG
  .attr('width', width + margin.left + margin.right)                          # Creates SVG width dimension
  .attr('height', height + margin.top + margin.bottom)                        # Creates SVG height dimension
  .append('g')                                                                # Creates group element inside SVG (used for zoom transforms)
  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')     # Moves 0,0 to margin.left / margin.top 


$(window).on 'resize', ->
  d3.select(d3parentContainer + ' svg')                                       # Select SVG
    .attr('width', width + margin.left + margin.right)                        # Change SVG width
    .attr 'height', height + margin.top + margin.bottom                       # Change SVG height
  
  svg                                                                         # Select 'SVG g' - defined as svg, easier to use as parent
    .attr('width', width + margin.left + margin.right)                        # Change SVG g width
    .attr('height', height + margin.top + margin.bottom)                      # Change SVG g height

Labels

Labels are used to provide chart or axes descriptions and context.

xaxislabel = svg                                                              # Select chart
  .append('text')                                                             # Append text
  .attr('class', 'd3-chart-label label-x-axis')                               # Attach specific classes for styling
  .text('x Axis Label')                                                       # Label output description
  .attr('x', width)                                                           # Position to the far right of the chart
  .attr('y', height + (margin.bottom/2))                                      # Position at the bottom of the chart
  .attr('dy', '1em')                                                          # Fine tune vertical position
  .attr 'text-anchor', 'end'                                                  # Right Text alignment

yaxislabel = svg
  .append('text')
  .attr('class', 'd3-chart-label label-y-axis')
  .text('y Axis Label')
  .attr('x', -height / 2)
  .attr('transform', 'rotate(-90)')                                           # Rotate -90% chart reads from bottom->top
  .attr('y', -margin.left / 2)
  .attr('dy', '-0.5em')
  .attr 'text-anchor', 'middle'                                               # Middle text alignment
  
  
$(window).on 'resize', ->
  xaxislabel
    .attr('x', width)                                                         
    .attr('y', height + (margin.bottom/2))

  yaxislabel
    .attr('x', -height / 2)   

Scales

Scales are functions that map from an input domain to an output range.

# Required in all cases
parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;                    # Date values input from dataset in ISO 8601 form

dataset.forEach (d) ->
  d.date = parseDate(d.date)                                                  # Parse date values
  d.value = +d.value                                                          # Convert strings to numbers 
  return

# Example 1- Time Scale
x = d3.time.scale()
  .domain(d3.extent(dataset, (d) ->
    d.date
  )).range([
    0
    width
  ])
  
# Example 2 - Linear Scale
y = d3.scale.linear()
  .domain([
    0
    d3.max(dataset, (d) ->
      d.yvalue
    )
  ]).range([
    height
    0
  ])  

# Example 3 - Linear Scale
opacityScale = d3.scale.linear()
  .domain([0,100])
  .range([20,100])


# Example 4 - Ordinal Scale - used for discreet domain values
colorScale = d3.scale.ordinal()
  .domain(["online", "offline", "pending"])
  .range(["green", "red", "orange"])
      
# Example 5 - Ordinal Scale with rangeBands  
y = d3.scale.ordinal()
  .domain(d3.range(0, dataset.length))                                        # Get dataset length
  .rangeBands([0, width],0.2)                                                 # rangeBands divides width to bands equal to dataset length, while 0.2 is the padding between bands 
  
$(window).on 'resize', ->
  x.range([                                                                   # Select scale and update range to new chart width
    0
    width
  ])    
  
  y.range([                                                                   # Select scale and update range to new chart height
    height
    0
  ])

# Example 6 - Categorical Color Scale
c10 = d3.scale.category10()                                                   # Categorical Color Scale

svg.selectAll('circle')                                                       # Example usage
  .attr("fill", c10 );

Scales usage example

Axes

Chart Axes take include tick values from their attached scales.

# Define Axes
xAxis = d3.svg.axis()                                                         # Function that creates the axis
  .scale(x)                                                                   # Attached scale
  .orient('bottom')                                                           # Ticks orientation compared to axis
  .ticks(5)                                                                   # Number of ticks - approximation, d3 handles actual number of ticks
  .tickFormat(d3.time.format("%d/%m"))                                        # Tick formatting https://github.com/mbostock/d3/wiki/Time-Formatting

yAxis = d3.svg.axis()
  .scale(y)
  .orient('left')
  .ticks(4)
  .tickFormat(d3.format("0.3s"))                                              # Number formatting https://github.com/mbostock/d3/wiki/Formatting


# Create xAxes
d3xaxis = svg                                                                 # Define d3xaxis element
  .append('g')                                                                # Group for cleaner SVG structure
  .attr('class', 'x d3-axis')                                                 # Attach class for styling
  .attr('transform', 'translate(0,' + height + ')')                           # Position x axis at bottom
  .call xAxis                                                                 # Run function to create axis

d3yaxis = svg
  .append('g')
  .attr('class', 'y d3-axis')
  .call yAxis                                                                 # No need to define position if the yaxis is at the left side of chart, starting at 0,0
  

$(window).on 'resize', ->
  d3xaxis 
    .attr('transform', 'translate(0,' + height + ')')                         # Keep to appropriate position after container resize.
    .call(xAxis)                                                              # Call function to update dimensions according to range changes
  
  d3yaxis 
    .attr('transform', 'translate(0,0)')                                      # Keep yaxis at the chart 0,0 position
    .call(yAxis)

Additional Options

Dual Y Axis

Used in cases where two different scale domains are used in the same chart.

# Create second yAxis on the right of the chart
margin.right=50                                                               # Right margin of parent container must be changed to accomodate new y axis

y2 = d3.scale.linear()                                                        # Define new y2 scale
    .domain([XX,XX])                                                          # Define new domain
    .range([                                                                  
      height
      0
    ])

y2Axis = d3.svg.axis()                                                        # Define 2nd y Axis
  .scale(y2)                                                                  # Use new y2 scale
  .orient('right')                                                            # Ticks from right side
  .ticks(4)
  .tickFormat(d3.format("0.3s"))                                              # Number formatting https://github.com/mbostock/d3/wiki/Formatting

d3y2axis = svg
  .append('g')
  .attr('class', 'y2 d3-axis')
  .attr('transform', 'translate(' + width + ',0)')                            # Position y2 axis at the right side of the chart
  .call y2Axis


y2axislabel = svg
  .append('text')
  .attr('class', 'd3-chart-label label-y2-axis')
  .text('y2 Axis Label')
  .attr('x', height / 2)
  .attr('transform', 'translate(' + (width+margin.right/2) + ',0) rotate(90)')  # Rotate 90 and position y2 axis at the right side of chart
  .attr('dy', '-0.5em')
  .attr 'text-anchor', 'middle'                                               


### Additional options might be needed, line color to match y2axis color, etc ###  


$(window).on 'resize', ->
  y2.range([                                                                  # Don't forget to update scale range
    height
    0
  ])

  d3y2axis 
    .attr('transform', 'translate(' + (width+margin.right/2) + ',0) rotate(90)')  # Keep y2 axis at the right side of the chart
    .call(y2Axis)                                                                 # Adjust height according to y2 scale's new domain

Negative Y Axis

Useful in cases we want to see two different keys' values using a negative axis.

# Define dataset
dataset = [{                                                                  
  "date": "2015-11-05T14:06:14Z",
  "value_in": 4,
  "value_out": 148
}, {
  "date": "2015-06-30T07:25:03Z",
  "value_in": 479,
  "value_out": 761
}, {
  "date": "2015-12-05T03:33:09Z",
  "value_in": 420,
  "value_out": 335
}, {
  "date": "2015-08-27T08:23:36Z",
  "value_in": 391,
  "value_out": 716
}]

# Options to be changed for negative axis
dataset.forEach (d) ->
  d.date = parseDate(d.date)
  d.value_in = +d.value_in
  d.value_out = +(d.value_out*-1)                                             # Change value_out values to negative
  return

# Sample y scale for example 
#  y = d3.scale.linear()                                                      # y scale defined for usual one direction axis definition
#    .domain([
#      0
#      d3.max([
#        d3.max(dataset, (d) ->
#          d.value_in
#        )
#        d3.max(dataset, (d) ->
#          d.value_out
#        )
#      ])
#    ]).range([
#      height
#      0
#    ]) 

  
y.domain([                                                                    # The previously defined y scale's domain has to be changed to the minimum and maximum of both value_in and value_out domain 
  d3.min([
    d3.min(dataset, (d) ->
      d.value_in
    )
    d3.min(dataset, (d) ->
      d.value_out
    )    
  ])
  d3.max([
    d3.max(dataset, (d) ->
      d.value_in
    ),
    d3.max(dataset, (d) ->
      d.value_out
    )
  ])
])  

# Line at the 0 of the domain is created instead of x axis that is placed at the bottom to avoid overlapping of elements with the axis values
zeroline = svg
  .append('line')
  .attr('class', 'd3-line d3-zero-line')
  .attr('x1', 0)
  .attr('x2', width)
  .attr('y1', y(0))                                                           # Place at domain value=0
  .attr('y2', y(0))
  .attr('opacity', .2) 
  
$(window).on 'resize', ->

  # y scale range code here #
  
  zeroline
    .attr('x2', width)                                                        # Resize zero line
    .attr('y1', y(0))                                                         
    .attr('y2', y(0))    

Tooltips

Tooltips are used to promote useful information when interacting with various elements on a chart.

# Define Tooltip
tooltip = d3.select(d3parentContainer)                                        # Tooltips are defined seperately inside each chart for easier selection and positioning
  .append('div')                                                              
  .attr('class', 'd3tooltip noarr hidden')                                    # Tooltips are created hidden at first, shown only upon interaction. Noarr class specifies that no tooltip arrow will be shown
  
# Example 1
elem.on('mousemove', mousemoveFunc)                                           # A tooltip is usually bound to mouse actions
    .on('mouseout', mouseoutFunc)                                              

mousemoveFunc = (d, i) ->                                                     
  mouse = d3.mouse(svg.node()).map((d) ->                                     # Function used to map mouse positioning from SVG 0,0 for accurate absolute positioning
    parseInt d
  )
  left = Math.min(parentWidth, mouse[0] + margin.left+15)                     # Define mouse.left
  top = Math.min(parentHeight, mouse[1]+15)                                   # Define mouse.top
  tooltip.classed('hidden', false)                                            # Remove 'hidden' class to show tooltip
    .attr('style', 'left:' + left + 'px;top:' + top + 'px')                   # Position Tooltip
    .html("Total: " + d.total + '')                          # Bind message to tooltip 
  return

mouseoutFunc = (d, i) ->                                                      
  tooltip.classed 'hidden', true                                              # Add 'hidden' class to hide tooltip
  return
  
  
# Example 2 - Graph / Map       
tooltip = d3.select(d3parentContainer)                                        
  .append('div')
  .attr('class', 'd3tooltip relativepos hidden')                              # 'relativepos' class used to position tooltip with arrow
  
marker.on('mousemove', mousemoveFunc)                                         # Tooltips are triggered by hovering over nodes
      .on('mouseout', mouseoutFunc)  
  
mousemoveFunc = (d, i) ->                                                     # Since maps/graphs might be usable, a different approach is needed for positioning the tooltip
  tooltipleft = d3.mouse(d3.select(d3parentContainer).node())[0]              # Mouse x position is defined by targeted node x position
  tooltiptop = d3.mouse(d3.select(d3parentContainer).node())[1]               # Mouse y position is defined by targeted node y position
  tooltip.classed('hidden', false)
  .attr('style', 'left:' + tooltipleft + 'px;top:' + tooltiptop + 'px')
  .html d.devicename
  return

mouseoutFunc = (d, i) ->
  tooltip.classed 'hidden', true
  return

component.set('mousemoveFunc', mousemoveFunc)
component.set('mouseoutFunc', mouseoutFunc)
  

Thresholds

Thresholds are used to inform about incidents or unique events that need the user's attention. This can be achieved using visual elements (eg threshold lines), using data filtering or by using scales (eg color scale, opacity scale).

Thresholds can be defined as dynamic or fixed. If the domain the trehshold belongs to is dynamic, the visual representation in the chart might be variable depending on the use case.

  • Dynamic threshold: defined as a percentage of the domain.
  • Fixed threshold: defined as a fixed value. If a fixed treshold exists outside the domain, it also defines the maximum domain value.
# Define dataset
dataset = [                                                                     
  {
    'date': '2011-07-01T19:14:34.000Z'
    'bytes': 58.13
  }
  {
    'date': '2011-07-01T19:13:34.000Z'
    'bytes': 53.98
  }
  {
    'date': '2011-07-01T19:12:34.000Z'
    'bytes': 67.00
  }
  {
    'date': '2011-07-01T19:11:34.000Z'
    'bytes': 89.70
  }
  {
    'date': '2011-07-01T19:10:34.000Z'
    'bytes': 99.00
  }
]

# Define y scale
y = d3.scale.linear()                                                           
  .domain([
    0
    d3.max(dataset, (d) ->
      d.bytes
    )
  ]).range([
    height
    0
  ])


# Example 1 - Threshold line
threshold = y(d3.mean(dataset, (d) ->                                           # Get domain average. If the domain max value increases, the threshold line will change position
  +d.bytes
))

thresholdline = svg                                                             # Define threshold line
  .append('line')
  .attr('class', 'd3-thresholdline')                                            # Append class for easier selection and styling
  .attr('x1', 0)                                                                # Make line horizontal with width equal to chart width
  .attr('x2', width)                                                            
  .attr('y1', threshold)                                                        # Position line to domain mean value
  .attr('y2', threshold)
  
$(window).on 'resize', ->  
  
  thresholdline                                                                 # Adjust size according to chart resize
    .attr 'x2', width
    .attr('y1', threshold)
    .attr('y2', threshold) 

    
# Example 2 - Color scale with threshold
var colorScale = d3.scale.linear()                                              # Define Scale
    .domain([
      0                                                                         
      d3.max(dataset, function(d) { return d.bytes; })*0.8                      # Color Threshold set at 80%
      d3.max(dataset, function(d) { return d.bytes; })*0.81
      d3.max(dataset, function(d) { return d.bytes; })
    ])
    .range([
      "blue"
      "blue"
      "red"
      "red"
    ]);     
    
d3.selectAll('d3-box')                                                          
  .data(dataset)
  .attr('fill', ->
    colorScale(d.bytes)                                                         # ColorScale Usage
  )
  

# Example 3 - Filtering data above a threshold
svg.selectAll(".d3-dot")
  .data(dataset)
  .enter().append("circle")
  .attr("class", "d3-dot")
  .filter((d,i) ->                                                              # Filter data from dataset
    return d.value > 80                                                         
  )
  .style("fill", "red")                                                         # Apply attributes only to the filtered data
  .attr("r", 3.5)
  .attr("cx", (d) -> 
    return x(d.date)
  )
  .attr("cy", (d) ->
    return y(d.value)
)      

Line Chart

Line charts are useful for data that flow on a timeline.

# Line Chart Example

# Define dataset
dataset = [
  {
    'date': '2011-07-01T19:14:34.000Z'
    'value': 58.13
  }
  {
    'date': '2011-07-01T19:13:34.000Z'
    'value': 53.98
  }
  {
    'date': '2011-07-01T19:12:34.000Z'
    'value': 67.00
  }
  {
    'date': '2011-07-01T19:11:34.000Z'
    'value': 89.70
  }
  {
    'date': '2011-07-01T19:10:34.000Z'
    'value': 99.00
  }
]

parentWidth = parentContainer.parent().width()                                # Get parent container width using Jquery - parent defines if chart has fixed or fluid dimensions
parentHeight = parentContainer.parent().height()                              # Get parent container height using Jquery
margin =                                                                      # Set margin
  top: 20
  right: 20
  bottom: 50
  left: 50
width = parentContainer.width() - (margin.left) - (margin.right)              # Define width used for svg g element
height = parentHeight - (margin.top) - (margin.bottom)                        # Define height used for svg g element

# Parse date/time in a specfic
parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;

# Data convert to recognisable form
dataset.forEach (d) ->
  d.date = parseDate(d.date)
  d.value = +d.value
  return
  
# Define SVG element
svg = d3.select(d3parentContainer)
  .append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')  

# Create scales
x = d3.time.scale()
  .domain(d3.extent(dataset, (d) ->
    d.date
  )).range([
    0
    width
  ])

y = d3.scale.linear()
  .domain([
    0
    1.05 * d3.max(dataset, (d) ->
      Math.max d.value
    )
  ])
  .range([
    height
    0
  ]) 
  
# Define axes
xAxis = d3.svg.axis()
  .scale(x)
  .orient('bottom')
  .ticks(5)

yAxis = d3.svg.axis()
  .scale(y)
  .ticks(4)
  .orient('left')  

# Create axes
svg
  .append('g')
  .attr('class', 'x d3-axis')
  .attr('transform', 'translate(0,' + height + ')')
  .call xAxis

svg
  .append('g')
  .attr('class', 'y d3-axis')
  .attr('transform', 'translate(0,0)')
  .call yAxis

  
# Define line
line = d3.svg.line()                                                          # Function that constructs a line generator used to create an SVG line
  .x((d) ->                                                                   # Positions line points on the x axis using defined scale
    x d.date
  ).y((d) ->                                                                  # Positions line points on the y axis using defined scale
    y d.value
  ).interpolate('linear')                                                     # Line interpolation. Can be used to smoothen lines by creating splines between points. http://www.d3noob.org/2013/01/smoothing-out-lines-in-d3js.html

# Create line
svg
  .append('path')                                                             # Lines are created as paths
  .data(dataset)                                                              # Bind dataset to path
  .attr('class', 'd3-line line1')                                             # Add class for line styling and easier selection
  .attr 'd', line(dataset)                                                    # Create line by using the previously defined line generator and bound dataset
  
$(window).on 'resize', ->

  width = parentContainer.width() - (margin.left) - (margin.right)            # Update chart width
  height = parentHeight - (margin.top) - (margin.bottom)                      # Update chart height

  d3.select(d3parentContainer + ' svg')                                       # Update SVG element dimensions
    .attr('width', width + margin.left + margin.right)
    .attr 'height', height + margin.top + margin.bottom
  svg.attr('width', width + margin.left + margin.right)                       # Update svg g dimensions
    .attr 'height', height + margin.top + margin.bottom

  x.range [                                                                   # Select scale and update range to new chart width
    0
    width
  ]
  
  y.range [                                                                   # Select scale and update range to new chart height
    height
    0
  ]

  d3.select(d3parentContainer + ' .x.d3-axis')                                # Select and update x axis. d3 parentContainer is used to select x axis of current chart in case of multiple charts
    .attr('transform', 'translate(0,' + height + ')')
    .call xAxis
  
  d3.select(d3parentContainer + ' .y.d3-axis')                                # Select and update y axis
    .attr('transform', 'translate(0,0)')
    .call yAxis  

  d3.select(d3parentContainer + ' .d3-line.line1')                            # Select line
    .attr 'd', line(dataset)                                                  # Update line path. Path is adjusted by the line generator that is adjusted by the resize-updated scale ranges

Line Chart Example

* Example is adjusted to static d3parentcontainer element.

Additional Options

* For Dual Y Axis, Negative Y Axis, check the Axes section.

Line Chart with Vertical Line

# Based on previous Line Chart Example

# Required functions for extracting data from the chart
bisectDate = d3.bisector((d) ->                                               # Used to extract specific dataset position from within the chart
  d.date
).left

dataset.sort (a, b) ->                                                        # Dataset needs to be sorted for the function to work. Has to be placed AFTER dataset.forEach() parsing
  a.date - (b.date)

# Create vertical line
vertical = svg.append('line')                                                 # Create line
  .attr('class', 'd3-vertline')                                               # Add class for easier selection and styling
  .attr('y1', 0)                                                              # Make vertical line equal to chart height
  .attr('y2', height)
  .attr('x1', 0)                                                              # Make line vertical and position at beginning of chart
  .attr('x2', 0)                                                

# Create functions for data extraction and vertical line positioning
mousemoveFunc = ->                                                            # Mousemove function is used to extract d.value from mouse position
  x0 = x.invert(d3.mouse(this)[0])
  i = bisectDate(dataset, x0, 1)
  d0 = dataset[i - 1]
  d1 = dataset[i]
  d = if x0 - (d0.date) > d1.date - x0 then d1 else d0
  console.log d.value
  return

mousemovelineFunc = ->                                                        # Function used to move the vertical line at mouse cursor position
  mousex = d3.mouse(this)
  mousex = mousex[0] + 5
  vertical.attr('x1', mousex).attr('x2', mousex).style 'stroke-opacity', '1'
  return

mouseoutlineFunc = ->                                                        # Function used to hide the vertical line
  vertical.style 'stroke-opacity', '0'
  return

svg                                                                           # In order to extract d.value an overlay has to be created under the svg element...
  .append('rect')
  .attr('class', 'd3-overlay')
  .attr('width', width)
  .attr('height', height)
  .on('mousemove', mousemoveFunc)                                             # ...and execude mousemoveFunc(). We can then use the extracted d.value inside a tooltip or any other predefined element.

svg                                                                           # To move the vertical line we apply the function on the svg element itself
  .on('mousemove', mousemovelineFunc)                                         # Move vertical line to mouse position
  .on('mouseout', mouseoutlineFunc)                                           # Hide vertical line

$(window).on 'resize', ->

  d3.select('.line-chart .d3-overlay')                                        # Adjust overlay element dimensions
    .attr('width', width)
    .attr('height', height)  

Line Chart with Vertical Line Example

Line Chart with Area fill

# Based on previous Line Chart Example

# Define area
area = d3.svg.area()
  .x((d) ->                                                                   # Positions points on the x axis using defined scale to define area width
    x d.date
  )
  .y0(y(0))                                                                   # Base of the area element - defined on y(0) = relative zero of the scale
  .y1((d) ->                                                                  # Positions points on the y axis using defined scale to define area height
    y d.value                                                                 
  ).interpolate 'linear'                                                      # Line interpolation. Make sure it's the same interpolation used for the line

# Create area
svg
  .append('path')                                                             # Areas are created as paths
  .data(dataset)                                                              # Bind dataset to path
  .attr('class', 'd3-area area1')                                             # Add class for line styling and easier selection
  .attr 'd', area(dataset)                                                    # Create area by using the previously defined area generator and bound dataset
  
$(window).on 'resize', ->

  d3.select(d3parentContainer + ' .d3-area.area1')                            # Select area
    .attr 'd', area(dataset)                                                  # Update area path. Path is adjusted by the area generator that is adjusted by the resize-updated scale ranges

Line Chart with Area Fill Example

Line Chart with Brush Zoom

Brush zoom creates interactivity with a selection range.

# Based on previous Line Chart Example

x2 = d3.time.scale()                                                          # Additional x2 scale is defined used for the brush
  .domain(d3.extent(dataset, (d) ->
    d.date
  )).range([
    0
    width
  ])
  
brush = d3.svg.brush()                                                        # Define brush
  .x(x2)                                                                      # Use newly defined x2 scale
  .on('brush', ->                                                             # Define brush() function
    x.domain if brush.empty() then x2.domain() else brush.extent()            # If there is no brush selection, domain x is used, if a brush extent is defined, it is applied to x2 domain
    extent = brush.extent()                                                   # Define range extent for the brush selection
    console.log(extent)                                                       # Test extent
    return
).on('brushend', ->                                                           # When mouse click, touch is released, brushend() function executes
  updateChart()                                                               
  return
)

svg.append('g')                                                               # Create brush element
  .attr('class', 'd3-brush')                                                  
  .call(brush)
  .selectAll('rect')                                                          # Rect is the drawn representation of the brush element
  .attr 'height', height  


updateChart = ->                                                              # Function executed on brush()
  extent = brush.extent()                                                     # Bring selected brush extent to be used for new dataset selection
  console.log('Update Chart Extent' + extent)                                 # Test extent
  
  ### Update scale domains, update generated paths ###
  
  d3.selectAll('.d3-brush')                                                   # Select all brush elements
    .call brush.clear()                                                       # and clear them so that they can be re-defined with new dataset

Line Chart with Brush Selection Example

Column Chart

Column charts are useful for displaying discreet domain data in combination with their quantitative values.

# Column Chart Example

# Define dataset
dataset = [
  {
    'date': '2011-07-01T19:14:34.000Z'
    'value': 58.13
  }
  {
    'date': '2011-07-01T19:13:34.000Z'
    'value': 53.98
  }
  {
    'date': '2011-07-01T19:12:34.000Z'
    'value': 67.00
  }
  {
    'date': '2011-07-01T19:11:34.000Z'
    'value': 89.70
  }
  {
    'date': '2011-07-01T19:10:34.000Z'
    'value': 99.00
  }
]

parentWidth = parentContainer.parent().width()                                # Get parent container width using Jquery - parent defines if chart has fixed or fluid dimensions
parentHeight = parentContainer.parent().height()                              # Get parent container height using Jquery
margin =                                                                      # Set margin
  top: 20
  right: 20
  bottom: 50
  left: 50
width = parentContainer.width() - (margin.left) - (margin.right)              # Define width used for svg g element
height = parentHeight - (margin.top) - (margin.bottom)                        # Define height used for svg g element

# Parse date/time in a specfic
parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;


# Data convert to recognisable form
dataset.forEach (d) ->
  d.date = parseDate(d.date)
  d.value = +d.value
  return
  
# Define SVG element
svg = d3.select(d3parentContainer)
  .append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')  

# Create scales
x = d3.scale.ordinal()                                                        # Ordinal scale is used...
  .domain(d3.range(0, dataset.length))                                        # ...and depending on the dataset length...
  .rangeBands([                                                               # ...the rangeBands function auto-magically sets column widths and padding
    0
    width
  ], 0.2)

x2 = d3.scale.ordinal()                                                       # Second horizontal scale needed for x Axis ticks
  .rangeBands([
    0
    width
  ], 0.2)
  .domain dataset.map((d) ->                                                  # Domain created from mapping d.date values
    d.date
  )  

y = d3.scale.linear().domain([                                                
    0
    d3.max(dataset, (d) ->
      d.value
    ) * 1.1                                                                   # 1.1 defines the padding between d3.max dataset value and the chart height
  ]).range([
    height
    0
  ])
  
# Define axes
xAxis = 
  d3.svg.axis()
  .scale(x2)
  .orient('bottom')
  .tickFormat(d3.time.format("%d/%m"))                                        # Format date as 25/10 - https://github.com/mbostock/d3/wiki/Time-Formatting

yAxis = d3.svg.axis()
  .scale(y)
  .orient('left')
  .tickValues([
    d3.min(dataset, (d) ->
      d.value
    )
    d3.max(dataset, (d) ->
      d.value
    )
  ])
  .tickFormat(d3.format("0.3s"));                                             # Unit formatting - https://github.com/mbostock/d3/wiki/Formatting 

# Create axes
svg
  .append('g')
  .attr('class', 'x d3-axis')
  .attr('transform', 'translate(0,' + height + ')')
  .call xAxis
svg
  .append('g')
  .attr('class', 'y d3-axis')
  .call yAxis


# Create group elements
d3elem = svg.selectAll('g.d3elem')
  .data(dataset)
  .enter()
  .append('g')
  .attr('class', 'd3elem')
      
# Create rectangles in each group
d3elem.append('rect')                                                         # Append a rectangle (column) in each d3elem
  .attr("class", "d3-rect")                                                   # Attach class for easier selection and styling
  .attr('y', (d) ->                                                           
    height - (height - y(d.value)))                                           # Set column position. Isn't 0 because SVG absolute 0,0 is at the top left and increases downwards
  .attr('height', (d) ->
    height - y(d.value))                                                      # Set column height
  .attr('width', x.rangeBand())                                               # With is set by the rangeBand scale
  .attr('x', (d, i) ->                                                        # Position column
    x i
  )  
  
$(window).on 'resize', ->

  width = parentContainer.width() - (margin.left) - (margin.right)            # Update chart width
  height = parentHeight - (margin.top) - (margin.bottom)                      # Update chart height

  d3.select(d3parentContainer + ' svg')                                       # Update SVG element dimensions
    .attr('width', width + margin.left + margin.right)
    .attr 'height', height + margin.top + margin.bottom
  svg.attr('width', width + margin.left + margin.right)                       # Update svg g dimensions
    .attr 'height', height + margin.top + margin.bottom

  x.rangeBands [                                                              # Select x scale and update range to new chart width
    0
    width
  ], 0.2
  
  x2.rangeBands([                                                             # Select x2 scale and update range to new chart width
  	0
    width
  ], 0.2)  
  
  y.range [                                                                   # Select scale and update range to new chart height
    height
    0
  ]

  d3.select(d3parentContainer + ' .x.d3-axis')                                # Select and update x axis. d3 parentContainer is used to select x axis of current chart in case of multiple charts
    .attr('transform', 'translate(0,' + height + ')')
    .call xAxis
  
  d3.select(d3parentContainer + ' .y.d3-axis')                                # Select and update y axis
    .attr('transform', 'translate(0,0)')
    .call yAxis  

  d3elem.select('.d3-rect')                                                   # Resize and reposition columns
    .attr('y', (d) ->
      height - (height - y(d.value)))
    .attr('height', (d) ->
      height - y(d.value))
    .attr('width', x.rangeBand())
    .attr 'x', (d, i) ->
      x i                             

Column Chart Example

Additional options

Combo Column-Line Chart

# Based on previous example

dataset = [
  {
    'date': '2011-07-01T19:14:34.000Z'
    'value': 58.13
    'avg': 20.23
  }
  {
    'date': '2011-07-01T19:13:34.000Z'
    'value': 53.98
    'avg': 52.34
  }
  {
    'date': '2011-07-01T19:12:34.000Z'
    'value': 67.00
    'avg': 30.32
  }
  {
    'date': '2011-07-01T19:11:34.000Z'
    'value': 89.70
    'avg': 29.90
  }
  {
    'date': '2011-07-01T19:10:34.000Z'
    'value': 99.00
    'avg': 85.12
  }
]

# Define additional y2 scale for line. Additional x scale could also be defined but should be avoided.
y2 = d3.scale.linear()                                                        
  .domain([
    0
    d3.max(dataset, (d) ->
      d.avg                                                                   
    ) * 2.2                                                                   # *2.2 forces the line to be almost in the vertical center of the chart 
  ])
  .range([
    height
    0
  ])
  
# Define line
comboline = d3.svg.line()                                                     
  .x((d, i) ->
    x(i) + x.rangeBand() / 2
  ).y((d) ->
    y2 d.avg
  )

# Create line
trendline = svg.append('g')                                                   
  .append('path')
  .attr('class', 'd3-trendline')
  .data(dataset)
  .attr('d', comboline(dataset))                                              # Generate line using comboline function 
  .attr('fill', 'none')    


$(window).on 'resize', ->

  y2.range([                                                                  # Update range
    height
    0
  ])

  d3.select('.d3-trendline')                                                  # Update line
    .attr('d', comboline(dataset))  

Combo Column-Line Chart Example

Grouped column chart

# Grouped Column Chart Example - Based on previous Column Chart Example

# Define dataset
dataset= [
  {
  "groupname": "groupname1"
  "val1": 8
  "val2": 3
  },
  {
  "groupname": "groupname2"
  "val1": 18
  "val2": 9        },
  {
  "groupname": "groupname3"
  "val1": 12
  "val2": 8
  },
  {
  "groupname": "groupname4"
  "val1": 9
  "val2": 10
  },
  {
  "groupname": "groupname5"
  "val1": 45
  "val2": 32
  },
  {
  "groupname": "groupname6"
  "val1": 14
  "val2": 24
  },
  {
  "groupname": "groupname7"
  "val1": 58
  "val2": 33
  }
]

parentWidth = parentContainer.parent().width()                                # Get parent container width using Jquery - parent defines if chart has fixed or fluid dimensions
parentHeight = parentContainer.parent().height()                              # Get parent container height using Jquery
margin =                                                                      # Set margin
  top: 20
  right: 20
  bottom: 50
  left: 50
width = parentContainer.width() - (margin.left) - (margin.right)              # Define width used for svg g element
height = parentHeight - (margin.top) - (margin.bottom)                        # Define height used for svg g element

###
# Parse date/time in a specfic
parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
###

# Data convert to recognisable form
dataset.forEach (d) ->
	d.val1 = +d.val1
	d.val2 = +d.val2
  return
  
# Define SVG element
svg = d3.select(d3parentContainer)
  .append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')  

###
# Create scales
x = d3.scale.ordinal()                                                        # Ordinal scale is used...
  .domain(d3.range(0, dataset.length))                                        # ...and depending on the dataset length...
  .rangeBands([                                                               # ...the rangeBands function auto-magically sets columns' widths and padding
    0
    width
  ], 0.2)

x2 = d3.scale.ordinal()                                                       # Second horizontal scale needed for x Axis ticks
  .rangeBands([
    0
    width
  ], 0.2)
  .domain dataset.map((d) ->                                                  # Domain created from mapping d.date values
    d.date
  )  

y = d3.scale.linear().domain([                                                
    0
    d3.max(dataset, (d) ->
      d.value
    ) * 1.1                                                                   # 1.1 defines the padding between d3.max dataset value and the chart height
  ]).range([
    height
    0
  ])
###

# New scales defined
x0 = d3.scale.ordinal()                                                       # This scale will create the bands between differnt Group Names (defined later)
	.rangeRoundBands([0, width], .1)

x1 = d3.scale.ordinal()                                                       # This scale will create the bands between differnt grouped columns

y = d3.scale.linear()                                                         # Domain will be created after dataset is mapped with new groupName values
	.range([height, 0]);


groupNames = d3.keys(dataset[0])                                              # Returns array containing property names from dataset 
	.filter((key) ->                                                            # Excludes the groupname key
  	key != 'groupname'
  )

dataset.forEach (d) ->                                                        # For every record of the dataset...
  d.cols = groupNames.map((name) ->                                           # ...a new key is created...
    {                                                                         #...that includes an array of the values of the groupNames keys
      name: name
      value: +d[name]
    }
  )
  return

x0.domain dataset.map((d) ->                                                  # x0 domain is set by length of groupnames
  d.groupname
)

x1.domain(groupNames).rangeRoundBands([                                       # x1 domain is set by creating bands for columns inside the x0 bands
  0
  x0.rangeBand()
  ], .05)

y.domain [                                                                    # y domain is set by finding the max value inside d.cols (equals to d3 max value1, value2)
  0
  d3.max(dataset, (d) ->
    d3.max d.cols, (d) ->
      d.value
  )
]



###
# Define axes
xAxis = 
  d3.svg.axis()
  .scale(x2)
  .orient('bottom')
  .tickFormat(d3.time.format("%d/%m"))                                        # Format date as 25/10 - https://github.com/mbostock/d3/wiki/Time-Formatting

yAxis = d3.svg.axis()
  .scale(y)
    .orient('left')
    .tickValues([
      d3.min(dataset, (d) ->
        d.value
      )
      d3.max(dataset, (d) ->
        d.value
      )
     ])
    .tickFormat(d3.format("0.3s"));                                           # Unit formatting - https://github.com/mbostock/d3/wiki/Formatting 
###

# New axes defined
xAxis = d3.svg.axis()
  .scale(x0)
  .orient('bottom')

yAxis = d3.svg.axis()                                                         
  .scale(y)
  .orient('left')
  .tickValues([                                                               # Axis will show ticks only for lower and higher value
    d3.min(dataset, (d) ->
      d3.min d.cols, (d) ->
        d.value
    )  
    d3.max(dataset, (d) ->
      d3.max d.cols, (d) ->
       d.value
    )
  ])
  .tickFormat(d3.format("0.3s"))                                              # Unit formatting - https://github.com/mbostock/d3/wiki/Formatting


# Create axes
svg
  .append('g')
  .attr('class', 'x d3-axis')
  .attr('transform', 'translate(0,' + height + ')')
  .call xAxis
svg
  .append('g')
  .attr('class', 'y d3-axis')
  .call yAxis

###
# Create group elements
d3elem = svg.selectAll('g.d3elem')
  .data(dataset)
  .enter()
  .append('g')
  .attr('class', 'd3elem')
      
# Create rectangles in each group
d3elem.append('rect')                                                         # Append a rectangle (column) in each d3elem
  .attr("class", "d3-rect")                                                   # Attach class for easier selection and styling
  .attr('y', (d) ->                                                           
    height - (height - y(d.value)))                                           # Set column position. Isn't 0 because SVG absolute 0,0 is at the top left and increases downwards
  .attr('height', (d) ->
    height - y(d.value))                                                      # Set column height
  .attr('width', x.rangeBand())                                               # With is set by the rangeBand scale
  .attr('x', (d, i) ->                                                        # Position column
    x i
  )
###

# Create groupname g elements
d3bggroup = svg.selectAll('g.d3bggroup')                                      # The groupname band
  .data(dataset)
  .enter()
  .append('g')                                                                # Create d3bggrops equal to dataset length
  .attr('class', 'd3bggroup')
  .attr 'transform', (d) ->
    'translate(' + x0(d.groupname) + ',0)'                                    # Group is positioned using x0 scale
  

# Create g elements
d3elem = d3bggroup.selectAll('g.d3elem')                                      # d3elems are created inside the groupname bands. These group elements are useful for adding labels, background elems, etc
  .data(dataset)
  .enter()
  .append('g')
  .attr('class', 'd3elem')
      
# Create rect in each g
d3elem.data((d) ->                                                            
	d.cols
).append('rect')                                                              # Columns
.attr("class", "d3-rect")
.attr('y', (d) ->
	height - (height - y(d.value))
).attr('height', (d) ->
	height - y(d.value)
).attr('width', x1.rangeBand()).attr('x', (d) ->                              # x1 scale is used to create column bands
	x1(d.name)
)  
  
$(window).on 'resize', ->

  width = parentContainer.width() - (margin.left) - (margin.right)            # Update chart width
  height = parentHeight - (margin.top) - (margin.bottom)                      # Update chart height

  d3.select(d3parentContainer + ' svg')                                       # Update SVG element dimensions
    .attr('width', width + margin.left + margin.right)
    .attr 'height', height + margin.top + margin.bottom
  svg.attr('width', width + margin.left + margin.right)                       # Update svg g dimensions
    .attr 'height', height + margin.top + margin.bottom

  ###
  x.rangeBands [                                                              # Select x scale and update range to new chart width
    0
    width
  ], 0.2
  
  x2.rangeBands([                                                             # Select x2 scale and update range to new chart width
  	0
    width
  ], 0.2)  
  ###
  
  x0.rangeRoundBands([0, width], .1)                                          # Select scale and update range to new chart width
  
  x1.rangeRoundBands([                                                        # Select scale and update range to new chart width
    0
    x0.rangeBand()
    ], .05)  
  
  y.range [                                                                   # Select scale and update range to new chart height
    height
    0
  ]

  d3.select(d3parentContainer + ' .x.d3-axis')                                # Select and update x axis. d3 parentContainer is used to select x axis of current chart in case of multiple charts
    .attr('transform', 'translate(0,' + height + ')')
    .call xAxis
  
  d3.select(d3parentContainer + ' .y.d3-axis')                                # Select and update y axis
    .attr('transform', 'translate(0,0)')
    .call yAxis  

  svg.selectAll('g.d3bggroup').attr 'transform', (d) ->                       # Resize and reposition groupname bands
  	'translate(' + x0(d.groupname) + ',0)'  

  d3elem.select('.d3-rect').attr('y', (d) ->                                  # Resize and reposition columns
    height - (height - y(d.value))
  ).attr('height', (d) ->
  	height - y(d.value)
	).attr('width', x1.rangeBand()).attr('x', (d) ->
  	x1(d.name)
	)                        

Grouped Column Chart Example

Bar Chart

Bar charts are very similar to column charts, and are useful for displaying discreet domain data in combination with their quantitative values. They are preffered in cases where the discreet values contain large names/labels.

# Bar Chart Example

# Define dataset
dataset= [
  {
  "devicename": "Iphone"
  "value": 8
  },
  {
  "devicename": "Android Device 1"
  "value": 3
  },
  {
  "devicename": "Android Tablet"
  "value": 4
  },
  {
  "devicename": "ScLord's Desktop"
  "value": 55
  },
  {
  "devicename": "Ioanna's Phone"
  "value": 112
  },
  {
  "devicename": "My Blackberry"
  "value": 77
  },
  {
  "devicename": "My phone"
  "value": 20
  },
  {
  "devicename": "Guest 1"
  "value": 30
  },
  {
  "devicename": "Guest 2"
  "value": 58
  },
  {
  "devicename": "ScLord's Laptop"
  "value": 78
  }                
]

parentWidth = parentContainer.parent().width()                                # Get parent container width using Jquery - parent defines if chart has fixed or fluid dimensions
parentHeight = parentContainer.parent().height()                              # Get parent container height using Jquery
margin =                                                                      # Set margin
  top: 20
  right: 20
  bottom: 50
  left: 50
width = parentContainer.width() - (margin.left) - (margin.right)              # Define width used for svg g element
height = parentHeight - (margin.top) - (margin.bottom)                        # Define height used for svg g element


# Data convert to recognisable form
dataset.forEach (d) ->
  d.value = +d.value
  return
  
# Define SVG element
svg = d3.select(d3parentContainer)
  .append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')  

# Create scales
x = d3.scale.linear()
	.domain([
    0
    d3.max(dataset, (d) ->
    	d.value
    ) * 1.1
    ])
    .range([
      0
      width
    ])

y = d3.scale.ordinal()                                                        # Ordinal scale i used...
	.domain(d3.range(0, dataset.length))                                        #...and depending on the dataset length...
  .rangeBands([                                                               #... the rangeBands function auto-magically set bar widths and padding
    0
    height
  ], 0.2)
  
# Define axes
xAxis = d3.svg.axis()
	.scale(x)
  .orient('bottom')
  .tickValues([
  	d3.min(dataset, (d) ->
    	d.value
    )
  	d3.max(dataset, (d) ->
    	d.value
    )  
  ])
  .tickFormat(d3.format("0.3s"))                                              # Unit formatting - https://github.com/mbostock/d3/wiki/Formatting 
  
yAxis = d3.svg.axis()
	.scale(y)
  .orient('left')

# Create axes
svg
  .append('g')
  .attr('class', 'x d3-axis')
  .attr('transform', 'translate(0,' + height + ')')
  .call xAxis
svg
  .append('g')
  .attr('class', 'y d3-axis')
  .call yAxis


# Create group elements
d3elem = svg.selectAll('g.d3elem')                                            # Useful for adding and positioning labels
  .data(dataset)
  .enter()
  .append('g')
  .attr('class', 'd3elem')
      
# Create rectangles in each group
d3elem.append('rect')
	.attr("class", "d3-rect")
	.attr('width', (d) ->
	  x(d.value)
	)
  .attr('height', y.rangeBand())                                              # Bar height is set by rangeBands scale
  .attr('y', (d, i) ->                                                        # Position bar vertically
  	y i
	)
  
d3elem.append('text')
	.text((d) ->                                                                # Add labels to group elements
    return d.devicename
  )
  .attr('y', (d, i) ->                                                        # Position labels
  	y(i) + y.rangeBand() / 2 + 5
	)
  .attr('x', '10')
	.attr('class', 'd3-vlabel')                                                 # Add class for easier selection and styling

$(window).on 'resize', ->

  width = parentContainer.width() - (margin.left) - (margin.right)            # Update chart width
  height = parentHeight - (margin.top) - (margin.bottom)                      # Update chart height

  d3.select(d3parentContainer + ' svg')                                       # Update SVG element dimensions
    .attr('width', width + margin.left + margin.right)
    .attr 'height', height + margin.top + margin.bottom
  svg.attr('width', width + margin.left + margin.right)                       # Update svg g dimensions
    .attr 'height', height + margin.top + margin.bottom

  x.range [0,width]                                                           # Select x scale and update range to new chart width
  y.rangeBands [0,height], 0.2                                                # Select y scale and update range to new chart height
 
  d3.select(d3parentContainer + ' .x.d3-axis')                                # Select and update x axis. d3 parentContainer is used to select x axis of current chart in case of multiple charts
    .attr('transform', 'translate(0,' + height + ')')
  	.call xAxis
  d3.select(d3parentContainer + ' .y.d3-axis')                                # Select and update y axis
    .attr('transform', 'translate(0,0)')
    .call yAxis  
  
  d3elem.select('.d3-rect').attr('width', (d) ->                              # Resize and reposition columns
	  x(d.value)
  ).attr('height', y.rangeBand()).attr 'y', (d, i) ->
  	y i
    
  d3elem.select('.d3-vlabel').attr('y', (d, i) ->                             # Position labels
  	y(i) + y.rangeBand() / 2 + 5
	)  
                                                                  

Bar Chart Example

Small Multiples - Lines

Small multiples are series of similar charts and are useful in comparisons between datasets that share a common horizontal axis - usually a timeline, and different y axes, that can also be based on different domains.

# Small Mutiples - Lines Example

# Define dataset
dataset = [
  {
  'date': '2015-11-11T19:15:00.000Z'
  'cpu': 13
  'ram': 128
  'sessions': 1367
  }
  {
  'date': '2015-11-11T19:16:00.000Z'
  'cpu': 70
  'ram': 128
  'sessions': 3567
  }
  {
  'date': '2015-11-11T19:17:00.000Z'
  'cpu': 25
  'ram': 250
  'sessions': 567
  }
  {
  'date': '2015-11-11T19:18:00.000Z'
  'cpu': 20
  'ram': 120
  'sessions': 4567
  }
]

parentWidth = parentContainer.parent().width()                                # Get parent container width using Jquery - parent defines if chart has fixed or fluid dimensions
parentHeight = parentContainer.parent().height()                              # Get parent container height using Jquery
margin =                                                                      # Set margin
  top: 20
  right: 20
  bottom: 50
  left: 50
width = parentContainer.width() - (margin.left) - (margin.right)              # Define width used for svg g element
height = parentHeight - (margin.top) - (margin.bottom)                        # Define height used for svg g element

spacer = 15                                                                   # Used to create spacing between small multiples

# Parse date/time in a specfic
parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;

# Required functions for extracting data from the chart - needed for vertical line
bisectDate = d3.bisector((d) ->                                               # Used to extract specific dataset position from within the chart
  d.date
).left

# Data convert to recognisable form
dataset.forEach (d) ->
  d.date = parseDate(d.date)
  d.cpu = +d.cpu
  d.ram = +d.ram
  d.sessions = +d.sessions
  return

dataset.sort (a, b) ->                                                        # Dataset needs to be sorted for the function to work. Has to be placed AFTER dataset.forEach() parsing
  a.date - (b.date)
  
# Define SVG element
svg = d3.select(d3parentContainer)
  .append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')    

# Create scales
x = d3.time.scale()                                                           # Common x axis timeline scale
	.domain([
    d3.min(dataset, (d) ->
    	d.date
    )
    d3.max(dataset, (d) ->
    	d.date
    )
  ]).range([
  	0
  	width
  ])

# CPU
y = d3.scale.linear()                                                         # y scale to be used for the top chart
	.domain([
    0
    100
  ]).range([
    height / 3 - spacer                                                       # Limits range to the chart's top part
    0
  ])

# Ram
totalram = 256                                                                # totalram variable is set to give percentage values
y2 = d3.scale.linear()                                                        # y scale to be used for the middle chart
	.domain([
    0
    100                                                                       # convert to percentage
  ]).range([
    height / 3 - spacer                                                       # Limits range to the chart's vertical middle
    0
  ])
# Sessions
y3 = d3.scale.linear()
	.domain([
    0
    d3.max(dataset, (d) ->
  	  d.sessions
    ) * 1.1
  ]).range([
    height / 3 - spacer                                                       # Limits range to che chart's bottom part
    0
  ])
  
# Define axes
xAxis = d3.svg.axis()
	.scale(x)
  .ticks(4)
  .orient('bottom')
  .tickFormat(d3.time.format("%H:%M"))                                        # Format time as 19:23 - https://github.com/mbostock/d3/wiki/Time-Formatting
  
yAxis = d3.svg.axis().scale(y).orient('left').tickValues([                    # Limit tick values to min and max
  d3.min(dataset, (d) ->
    d.cpu
  )
  d3.max(dataset, (d) ->
    d.cpu
  )
])
yAxis2 = d3.svg.axis().scale(y2).ticks(2).orient('left').tickValues([         # Limit tick values to min and max
  d3.min(dataset, (d) ->
    d.ram
  ) / totalram*100
  d3.max(dataset, (d) ->
    d.ram
  ) / totalram*100
])
yAxis3 = d3.svg.axis().scale(y3).ticks(2).tickValues([                        # Limit tick values to min and max
  d3.min(dataset, (d) ->
    d.sessions
  )
  d3.max(dataset, (d) ->
    d.sessions
  )
]).tickFormat(d3.format('0.2s')).orient('left')                               # Unit formatting - https://github.com/mbostock/d3/wiki/Formatting 


# Create axes
svg.append('g')
  .attr('class', 'x d3-axis')
  .attr('transform', 'translate(0,' + (height - spacer) + ')')
  .call xAxis

svg.append('g')
  .attr('class', 'y d3-axis').call yAxis

svg.append('g')
  .attr('class', 'y2 d3-axis')
  .attr('transform', 'translate(0,' + height / 3 + ')')
  .call yAxis2

svg.append('g')
  .attr('class', 'y3 d3-axis')
  .attr('transform', 'translate(0,' + 2 * height / 3 + ')')
  .call yAxis3

# Define, create and position y axes labels
axes = svg.append('g')                                                        
	.attr("class", "d3-axes")

axes  
  .append('text')
  .attr('class', 'd3-chart-label label-y-axis')
  .text('CPU')
  .attr('x', -(height/6 -spacer/2))
  .attr('transform', 'rotate(-90)')
  .attr('y', -margin.left / 2)
  .attr 'text-anchor', 'middle'

axes  
  .append('text')
  .attr('class', 'd3-chart-label label-y2-axis')
  .text('RAM')
  .attr('x', -((3*height/6)-spacer/2))
  .attr('transform', 'rotate(-90)')
  .attr('y', -margin.left / 2)
  .attr 'text-anchor', 'middle'      

axes  
  .append('text')
  .attr('class', 'd3-chart-label label-y3-axis')
  .text('Sessions')
  .attr('x', -((5*height/6) - spacer/2))
  .attr('transform', 'rotate(-90)')
  .attr('y', -margin.left / 2)
  .attr 'text-anchor', 'middle' 

# Create lines to define small multiple charts 0
axisline = svg.append('g')
  .append('line')
  .attr('x1', 0)
  .attr('x2', width)
  .attr('y1', height / 3 - spacer)
  .attr('y2', height / 3 - spacer)
  .attr('stroke', '#ccc')

axisline2 = svg.append('g')
  .append('line')
  .attr('x1', 0)
  .attr('x2', width)
  .attr('y1', 2 * height / 3 - spacer)
  .attr('y2', 2 * height / 3 - spacer)
  .attr('stroke', '#ccc')

# Define lines
myline = d3.svg.line()
	.x((d) ->
  	x d.date
  )
  .y((d) ->
  	y d.cpu
	)

myline2 = d3.svg.line()
	.x((d) ->
  	x d.date
	)
  .y((d) ->
  	y2 d.ram / totalram * 100
	)

myline3 = d3.svg.line()
	.x((d) ->
  	x d.date
	)
  .y((d) ->
  	y3 d.sessions
	)
  
# Create lines  
line1 = svg.append('g').append('path').attr('class', 'd3-line').data(dataset).attr('d', myline(dataset))
line2 = svg.append('g').append('path').attr('class', 'd3-line').data(dataset).attr('d', myline2(dataset)).attr('transform', 'translate(0,' + height / 3 + ')')
line3 = svg.append('g').append('path').attr('class', 'd3-line').data(dataset).attr('d', myline3(dataset)).attr('transform', 'translate(0,' + 2 * height / 3 + ')')
  
# Create overlay to detect mouse position  
overlay = svg.append('rect').attr('class', 'd3-overlay').attr('fill-opacity', 0).attr('width', width).attr('height', height)

overlay
  .on('mousemove', (d, i) ->                                                  # Extract data from datasets
    mouse = d3.mouse(svg.node()).map((d) ->                                   # Standard data extraction procedure - copy/paste
      parseInt d
    )
    x0 = x.invert(d3.mouse(this)[0])
    i = bisectDate(dataset, x0, 1)
    d0 = dataset[i - 1]
    d1 = dataset[i]
    d = if x0 - (d0.date) > d1.date - x0 then d1 else d0                      # Standard data extraction procedure - end copy/paste
    console.log('CPU ' + d.cpu,                                               # Console log extracted results that can be used inside tooltips or other elements
    ', RAM % ' + d.ram*100/totalram,
    ', Sessions ' + d.sessions)
    return
	) 

# Create Vertical Line - Overlay layer is needed for this to work!
vertical = svg.append('line')
  .attr('class', 'd3-vertline')                                               # Add class for easier selection and styling
  .attr('y1', 0)
  .attr('y2', height)
  .attr('x1', 0).attr('x2', 0)

svg.on('mousemove', ->                                                        # Move vertical line to mouse x position
  mousex = d3.mouse(this)
  mousex = mousex[0] + 5
  vertical.attr('x1', mousex).attr('x2', mousex).style 'stroke-opacity', '1'
  return
).on('mouseover', ->
  mousex = d3.mouse(this)
  mousex = mousex[0] + 5
  vertical.attr('x1', mousex).attr('x2', mousex).style 'stroke-opacity', '1'
  return
).on 'mouseout', ->                                                           # Hide vertical line when the mouse is outside the chart
  vertical.style 'stroke-opacity', '0'
  return
     

$(window).on 'resize', ->

  width = parentContainer.width() - (margin.left) - (margin.right)            # Update chart width
  height = parentHeight - (margin.top) - (margin.bottom)                      # Update chart height

  d3.select(d3parentContainer + ' svg')                                       # Update SVG element dimensions
    .attr('width', width + margin.left + margin.right)
    .attr 'height', height + margin.top + margin.bottom
  svg.attr('width', width + margin.left + margin.right)                       # Update svg g dimensions
    .attr 'height', height + margin.top + margin.bottom

	x.range [                                                                   # Select scale and update range
  	0
  	width
  ]
	y.range [                                                                   # Select scale and update range
  	height / 3 - spacer
  	0
	]
	y2.range [                                                                  # Select scale and update range
  	height / 3 - spacer
  	0
	]
	y3.range [                                                                  # Select scale and update range
  	height / 3 - spacer
  	0
	]
	
	vertical.attr('y2', height)                                                 # Update vertical line's height
	overlay.attr('width', width).attr 'height', height                          # Update overlay box dimensions
  
	# Update lines
  line1.attr 'd', myline(dataset)                                             
	line2.attr('d', myline2(dataset))                                           
    .attr 'transform', 'translate(0,' + height / 3 + ')'                      
	line3.attr('d', myline3(dataset))                                           
  .attr 'transform', 'translate(0,' + 2 * height / 3 + ')'
	
  # Update axis lines
  axisline                                                                    
    .attr('x2', width)
    .attr('y1', height / 3 - spacer)
    .attr 'y2', height / 3 - spacer
	axisline2                                                                   
    .attr('x2', width)
    .attr('y1', 2 * height / 3 - spacer)
    .attr 'y2', 2 * height / 3 - spacer
        
	# Update axes
  d3.select(d3parentContainer + ' .x.d3-axis')
    .attr('transform', 'translate(0,' + (height - spacer) + ')')
    .call xAxis
	d3.select(d3parentContainer + ' .y.d3-axis')
    .call yAxis
	d3.select(d3parentContainer + ' .y2.d3-axis')
    .attr('transform', 'translate(0,' + height / 3 + ')')
    .call yAxis2
	d3.select(d3parentContainer + ' .y3.d3-axis')
    .attr('transform', 'translate(0,' + 2 * height / 3 + ')')
    .call yAxis3
  
	# Update axes labels positions
  d3.select(d3parentContainer + ' .label-y-axis')
    .attr('x', -(height/6 -spacer/2))
	d3.select(d3parentContainer + ' .label-y2-axis')
    .attr('x', -((3*height/6)-spacer/2))
	d3.select(d3parentContainer + ' .label-y3-axis')
    .attr('x', -((5*height/6) - spacer/2))

Small Multiples - Lines Example

Small Multiples - Columns

# Small Mutiples - Columns Example, based on previous example, changed code commented out

# Define dataset
dataset = [
  {
  'date': '2015-11-11T19:15:00.000Z'
  'cpu': 13
  'ram': 128
  'sessions': 1367
  }
  {
  'date': '2015-11-11T19:16:00.000Z'
  'cpu': 70
  'ram': 128
  'sessions': 3567
  }
  {
  'date': '2015-11-11T19:17:00.000Z'
  'cpu': 25
  'ram': 250
  'sessions': 567
  }
  {
  'date': '2015-11-11T19:18:00.000Z'
  'cpu': 20
  'ram': 120
  'sessions': 4567
  }
]

parentWidth = parentContainer.parent().width()                                # Get parent container width using Jquery - parent defines if chart has fixed or fluid dimensions
parentHeight = parentContainer.parent().height()                              # Get parent container height using Jquery
margin =                                                                      # Set margin
  top: 20
  right: 20
  bottom: 50
  left: 50
width = parentContainer.width() - (margin.left) - (margin.right)              # Define width used for svg g element
height = parentHeight - (margin.top) - (margin.bottom)                        # Define height used for svg g element

spacer = 15                                                                   # Used to create spacing between small multiples

# Parse date/time in a specfic
parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;

# Required functions for extracting data from the chart - needed for vertical line
bisectDate = d3.bisector((d) ->                                               # Used to extract specific dataset position from within the chart
  d.date
).left

# Data convert to recognisable form
dataset.forEach (d) ->
  d.date = parseDate(d.date)
  d.cpu = +d.cpu
  d.ram = +d.ram
  d.sessions = +d.sessions
  return

dataset.sort (a, b) ->                                                        # Dataset needs to be sorted for the function to work. Has to be placed AFTER dataset.forEach() parsing
  a.date - (b.date)
  
# Define SVG element
svg = d3.select(d3parentContainer)
  .append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')    

###
# Create scales
x = d3.time.scale()                                                           # Common x axis timeline scale
	.domain([
    d3.min(dataset, (d) ->
    	d.date
    )
    d3.max(dataset, (d) ->
    	d.date
    )
  ]).range([
  	0
  	width
  ])
###

# Create scales
x = d3.scale.ordinal()                                                        # Ordinal scale defined for column bands definition
	.domain(d3.range(0, dataset.length))
	.rangeBands([0, width],0.2)

x2 = d3.scale.ordinal()                                                       
	.rangeBands([
    0
    width
  ], 0.2)
  .domain dataset.map((d) ->                                                  # x2 scale is defined for x axis ticks formatting
    d.date
  )

# CPU
y = d3.scale.linear()                                                         # y scale to be used for the top chart
	.domain([
    0
    100
  ]).range([
    height / 3 - spacer                                                       # Limits range to the chart's top part
    0
  ])

# Ram
totalram = 256                                                                # totalram variable is set to give percentage values
y2 = d3.scale.linear()                                                        # y scale to be used for the middle chart
	.domain([
    0
    100                                                                       # convert to percentage
  ]).range([
    height / 3 - spacer                                                       # Limits range to the chart's vertical middle
    0
  ])
# Sessions
y3 = d3.scale.linear()
	.domain([
    0
    d3.max(dataset, (d) ->
  	  d.sessions
    ) * 1.1
  ]).range([
    height / 3 - spacer                                                       # Limits range to che chart's bottom part
    0
  ])
  
# Define axes
xAxis = d3.svg.axis()
# .scale(x)                                                                   
  .scale(x2)                                                                  # Scale changed to x2
  .ticks(4)
  .orient('bottom')
  .tickFormat(d3.time.format("%H:%M"))                                        # Format time as 19:23 - https://github.com/mbostock/d3/wiki/Time-Formatting
  
yAxis = d3.svg.axis().scale(y).orient('left').tickValues([                    # Limit tick values to min and max
  d3.min(dataset, (d) ->
    d.cpu
  )
  d3.max(dataset, (d) ->
    d.cpu
  )
])
yAxis2 = d3.svg.axis().scale(y2).ticks(2).orient('left').tickValues([         # Limit tick values to min and max
  d3.min(dataset, (d) ->
    d.ram
  ) / totalram*100
  d3.max(dataset, (d) ->
    d.ram
  ) / totalram*100
])
yAxis3 = d3.svg.axis().scale(y3).ticks(2).tickValues([                        # Limit tick values to min and max
  d3.min(dataset, (d) ->
    d.sessions
  )
  d3.max(dataset, (d) ->
    d.sessions
  )
]).tickFormat(d3.format('0.2s')).orient('left')                               # Unit formatting - https://github.com/mbostock/d3/wiki/Formatting 


# Create axes
svg.append('g')
  .attr('class', 'x d3-axis')
  .attr('transform', 'translate(0,' + (height - spacer) + ')')
  .call xAxis

svg.append('g')
  .attr('class', 'y d3-axis').call yAxis

svg.append('g')
  .attr('class', 'y2 d3-axis')
  .attr('transform', 'translate(0,' + height / 3 + ')')
  .call yAxis2

svg.append('g')
  .attr('class', 'y3 d3-axis')
  .attr('transform', 'translate(0,' + 2 * height / 3 + ')')
  .call yAxis3

# Define, create and position y axes labels
axes = svg.append('g')                                                        
	.attr("class", "d3-axes")

axes  
  .append('text')
  .attr('class', 'd3-chart-label label-y-axis')
  .text('CPU')
  .attr('x', -(height/6 -spacer/2))
  .attr('transform', 'rotate(-90)')
  .attr('y', -margin.left / 2)
  .attr 'text-anchor', 'middle'

axes  
  .append('text')
  .attr('class', 'd3-chart-label label-y2-axis')
  .text('RAM')
  .attr('x', -((3*height/6)-spacer/2))
  .attr('transform', 'rotate(-90)')
  .attr('y', -margin.left / 2)
  .attr 'text-anchor', 'middle'      

axes  
  .append('text')
  .attr('class', 'd3-chart-label label-y3-axis')
  .text('Sessions')
  .attr('x', -((5*height/6) - spacer/2))
  .attr('transform', 'rotate(-90)')
  .attr('y', -margin.left / 2)
  .attr 'text-anchor', 'middle' 

# Create lines to define small multiple charts 0
axisline = svg.append('g')
  .append('line')
  .attr('x1', 0)
  .attr('x2', width)
  .attr('y1', height / 3 - spacer)
  .attr('y2', height / 3 - spacer)
  .attr('stroke', '#ccc')

axisline2 = svg.append('g')
  .append('line')
  .attr('x1', 0)
  .attr('x2', width)
  .attr('y1', 2 * height / 3 - spacer)
  .attr('y2', 2 * height / 3 - spacer)
  .attr('stroke', '#ccc')

###
# Define lines
myline = d3.svg.line()
	.x((d) ->
  	x d.date
  )
  .y((d) ->
  	y d.cpu
	)

myline2 = d3.svg.line()
	.x((d) ->
  	x d.date
	)
  .y((d) ->
  	y2 d.ram / totalram * 100
	)

myline3 = d3.svg.line()
	.x((d) ->
  	x d.date
	)
  .y((d) ->
  	y3 d.sessions
	)
  
# Create lines  
line1 = svg.append('g').append('path').attr('class', 'd3-line').data(dataset).attr('d', myline(dataset))
line2 = svg.append('g').append('path').attr('class', 'd3-line').data(dataset).attr('d', myline2(dataset)).attr('transform', 'translate(0,' + height / 3 + ')')
line3 = svg.append('g').append('path').attr('class', 'd3-line').data(dataset).attr('d', myline3(dataset)).attr('transform', 'translate(0,' + 2 * height / 3 + ')')
  
# Create overlay to detect mouse position  
overlay = svg.append('rect').attr('class', 'd3-overlay').attr('fill-opacity', 0).attr('width', width).attr('height', height)

overlay
  .on('mousemove', (d, i) ->                                                  # Extract data from datasets
    mouse = d3.mouse(svg.node()).map((d) ->                                   # Standard data extraction procedure - copy/paste
      parseInt d
    )
    x0 = x.invert(d3.mouse(this)[0])
    i = bisectDate(dataset, x0, 1)
    d0 = dataset[i - 1]
    d1 = dataset[i]
    d = if x0 - (d0.date) > d1.date - x0 then d1 else d0                      # Standard data extraction procedure - end copy/paste
    console.log('CPU ' + d.cpu,                                               # Console log extracted results that can be used inside tooltips or other elements
    ', RAM % ' + d.ram*100/totalram,
    ', Sessions ' + d.sessions)
    return
	) 

# Create Vertical Line - Overlay layer is needed for this to work!
vertical = svg.append('line')
  .attr('class', 'd3-vertline')                                               # Add class for easier selection and styling
  .attr('y1', 0)
  .attr('y2', height)
  .attr('x1', 0).attr('x2', 0)

svg.on('mousemove', ->                                                        # Move vertical line to mouse x position
  mousex = d3.mouse(this)
  mousex = mousex[0] + 5
  vertical.attr('x1', mousex).attr('x2', mousex).style 'stroke-opacity', '1'
  return
).on('mouseover', ->
  mousex = d3.mouse(this)
  mousex = mousex[0] + 5
  vertical.attr('x1', mousex).attr('x2', mousex).style 'stroke-opacity', '1'
  return
).on 'mouseout', ->                                                           # Hide vertical line when the mouse is outside the chart
  vertical.style 'stroke-opacity', '0'
  return
###

# Create Columns
d3bgelem = svg.selectAll('g.d3bgelem')
  .data(dataset)
  .enter()
  .append('g')
  .attr('class', 'd3bgelem')

d3bgelem.append('rect').attr("class", "d3-bg-rect d3-bg-rect-hovered")
	.attr('height', (height-spacer))
  .attr('width', x.rangeBand())
  .attr('x', (d, i) ->
    x i
	)

# CPU
d3elem = svg.selectAll('g.d3elem')                                            # Create group elements for easier addition of labels, etc
  .data(dataset)
  .enter()
  .append('g')
  .attr('class', 'd3elem')

d3elem.append('rect').attr("class", "d3-rect")                                # Add column
	.attr('y', (d) ->
  	(height/3-spacer) - ((height/3-spacer) - y(d.cpu))                        # Set column y position
	)
  .attr('height', (d) ->                                                      # Set column height
  	(height/3-spacer) - y(d.cpu)
	)
  .attr('width', x.rangeBand())                                               # Set column width
  .attr('x', (d, i) ->                                                        # Set column x position
  	x i
	)

# Ram
d3elem2 = svg.selectAll('g.d3elem2')
  .data(dataset).enter()
  .append('g')
  .attr('class', 'd3elem d3elem2')
	.attr('transform', 'translate(0,' + height / 3 + ')')

d3elem2.append('rect')
	.attr("class", "d3-rect")
  .attr('y', (d) ->
    (height/3-spacer) - ((height/3-spacer) - y2(d.ram*100/totalram))
  ).attr('height', (d) ->
    (height/3-spacer) - y2(d.ram*100/totalram)
  ).attr('width', x.rangeBand()).attr('x', (d, i) ->
    x i
  )
      
# Sessions
d3elem3 = svg.selectAll('g.d3elem3')
  .data(dataset)
  .enter()
  .append('g')
  .attr('class', 'd3elem d3elem3')
	.attr('transform', 'translate(0,' + 2*height / 3 + ')')
      
d3elem3.append('rect')
	.attr("class", "d3-rect")
	.attr('y', (d) ->
    (height/3-spacer) - ((height/3-spacer) - y3(d.sessions))
	).attr('height', (d) ->
    (height/3-spacer) - y3(d.sessions)
	).attr('width', x.rangeBand()).attr('x', (d, i) ->
    x i
	)

d3.select('.small-multiple-chart').selectAll('.d3bgelem')                     
	.on('mousemove', (d, i) ->
		mouse = d3.mouse(svg.node()).map((d) ->
  		parseInt d
		)
		console.log('CPU ' + d.cpu,                                               # When hovering over background elements d
      ', RAM % ' + d.ram*100/totalram,
      ', Sessions ' + d.sessions)
		) 
     

$(window).on 'resize', ->

  width = parentContainer.width() - (margin.left) - (margin.right)            # Update chart width
  height = parentHeight - (margin.top) - (margin.bottom)                      # Update chart height

  d3.select(d3parentContainer + ' svg')                                       # Update SVG element dimensions
    .attr('width', width + margin.left + margin.right)
    .attr 'height', height + margin.top + margin.bottom
  svg.attr('width', width + margin.left + margin.right)                       # Update svg g dimensions
    .attr 'height', height + margin.top + margin.bottom

	###
  x.range [                                                                   # Select scale and update range
  	0
  	width
  ]
  ###
  
  x.rangeBands([0, width],0.2)                                                # Select scale and update rangeBands
	x2.rangeBands([0, width],0.2)                                               # Select scale and update rangeBands
  
	y.range [                                                                   # Select scale and update range
  	height / 3 - spacer
  	0
	]
	y2.range [                                                                  # Select scale and update range
  	height / 3 - spacer
  	0
	]
	y3.range [                                                                  # Select scale and update range
  	height / 3 - spacer
  	0
	]
	
	###
  vertical.attr('y2', height)                                                 # Update vertical line's height
	overlay.attr('width', width).attr 'height', height                          # Update overlay box dimensions
  
	# Update lines
  line1.attr 'd', myline(dataset)                                             
	line2.attr('d', myline2(dataset))                                           
    .attr 'transform', 'translate(0,' + height / 3 + ')'                      
	line3.attr('d', myline3(dataset))                                           
  .attr 'transform', 'translate(0,' + 2 * height / 3 + ')'
  ###
  
	# Update columns
	d3bgelem.select('.d3-bg-rect')
    .attr('width', x.rangeBand())
    .attr('height', (height-spacer))
    .attr('x', (d, i) ->
		  x i
	 )
	d3elem.select('.d3-rect').attr('y', (d) ->
		(height/3-spacer) - ((height/3-spacer) - y(d.cpu))
	).attr('height', (d) ->
		(height/3-spacer) - y(d.cpu)
	).attr('width', x.rangeBand()).attr('x', (d, i) ->
		x i
	)

	d3elem2.attr('transform', 'translate(0,' + height / 3 + ')')

	d3elem2.select('.d3-rect').attr('y', (d) ->
		(height/3-spacer) - ((height/3-spacer) - y2(d.ram*100/totalram))
	).attr('height', (d) ->
		(height/3-spacer) - y2(d.ram*100/totalram)
	).attr('width', x.rangeBand()).attr('x', (d, i) ->
		x i
	)

	d3elem3.attr('transform', 'translate(0,' + 2*height / 3 + ')')

	d3elem3.select('.d3-rect').attr('y', (d) ->
		(height/3-spacer) - ((height/3-spacer) - y3(d.sessions))
	).attr('height', (d) ->
		(height/3-spacer) - y3(d.sessions)
	).attr('width', x.rangeBand()).attr('x', (d, i) ->
		x i
	)    
	
  # Update axis lines
  axisline                                                                    
    .attr('x2', width)
    .attr('y1', height / 3 - spacer)
    .attr 'y2', height / 3 - spacer
	axisline2                                                                   
    .attr('x2', width)
    .attr('y1', 2 * height / 3 - spacer)
    .attr 'y2', 2 * height / 3 - spacer
        
	# Update axes
  d3.select(d3parentContainer + ' .x.d3-axis')
    .attr('transform', 'translate(0,' + (height - spacer) + ')')
    .call xAxis
	d3.select(d3parentContainer + ' .y.d3-axis')
    .call yAxis
	d3.select(d3parentContainer + ' .y2.d3-axis')
    .attr('transform', 'translate(0,' + height / 3 + ')')
    .call yAxis2
	d3.select(d3parentContainer + ' .y3.d3-axis')
    .attr('transform', 'translate(0,' + 2 * height / 3 + ')')
    .call yAxis3
  
	# Update axes labels positions
  d3.select(d3parentContainer + ' .label-y-axis')
    .attr('x', -(height/6 -spacer/2))
	d3.select(d3parentContainer + ' .label-y2-axis')
    .attr('x', -((3*height/6)-spacer/2))
	d3.select(d3parentContainer + ' .label-y3-axis')
    .attr('x', -((5*height/6) - spacer/2))

Small Multiples - Columns Example

Map Chart

Useful for items that carry geolocation attributes, eg, threats, networks, etc

# Map Chart Example

# Everything happens inside the d3.json function that ensures that the map json data is loaded before executing any of the actions
d3.json 'https://gist.githubusercontent.com/scooterlord/de90b9dd46658dd4983d/raw/92f1e0f843fe1c76dcf515599739d80c51925c9b/world-topo-bordered.json', (json) ->
  
  s = 1                                                                       # Set initial map scale
  map = d3.select('.map-chart')                                               # Select map parent container
  tooltip = map.append('div').attr('class', 'd3tooltip hidden')               # Create tooltip element
  
  width = $('.map-chart').width()                                             # Define map container dimensions
  height = $('.map-chart').height()                                           
  
  # map Ratio / container Ratio                                               # Create map & container ratio
  mR = 2                                                                      # The world map has a ratio of 2:1...
  cR = width/height                                                           # ...while the container ratio is set dynamically by the parent container. This will be used later to always keep the map boundaries inside the parent container. 
  
  
  # if map width > responsive window ratio                                    # Depending on the above ratios a different mapScale is set
  if mR > cR
  	mapScale = width / mR / Math.PI                                           
  else
  	mapScale = width / cR / Math.PI
  
  projection = d3.geo.equirectangular()                                       # The projection is set to equirectangular
    .center([                                                                 # Center is offset 15px to be equally centered vertically 
    	0
    	15
    ])
    .scale(mapScale*0.9)                                                      # The mapScale is multiplied by 0.9 so that is is initially created with padding
    .translate([                                                              # Map is centered in the parent container
    	width / 2
    	height / 2
    ])

  path = d3.geo.path().projection(projection)                                 # Path generator is defined
  bounds = path.bounds(topojson.feature(json, json.objects['countries']))     # Computes the projected bounding box for the countries = outputs array bounds[[left,top],[right,bottom]]. Actually creates a box from the boundaries of all the countries and outputs its corners positions.
  
  # bounds[[left,top],[right,bottom]]
  projw = bounds[1][0] - bounds[0][0]                                         # Bounding box width 
  projh = bounds[1][1] - bounds[0][1]                                         # Bounding box height
  
  
  # Zoom function
  zoomFunction = ->                                                           
    t = d3.event.translate                                                    # Defines zoom translation vector
    s = d3.event.scale                                                        # Defines zoom scale vector
    nprojw = (projw)*s+20                                                     # Fine Tune bounding box dimensions so that when panning a 20px padding is created
    nprojh = (projh)*s+20                                                     
    
    # Code used to keep the countries bounding box inside the parent container boundaries. * Defined from the center of the bounding box
    w_min = if width > nprojw then -(s-1)*width/2 else width*(1-s/2) - nprojw/2         
    w_max = if width > nprojw then  width*(1-s)/2 else -width*s/2 + nprojw/2            
    h_min = if height > nprojh then -(s-1)*height/2 else height*(1-s/2) - nprojh/2     
    h_max = if height > nprojh then  height*(1-s)/2 else -height*s/2 + nprojh/2        
    t[0] = Math.min(w_max, Math.max(w_min, t[0]))
    t[1] = Math.min(h_max, Math.max(h_min, t[1]))
    
    zoom.translate t                                                          # Execute translation for bounding box
    g.attr 'transform', 'translate(' + t + ')scale(' + s + ')'                # Translate/scale countries layer accordingly
    
    tooltip.classed 'hidden', true                                            # Hide tooltips on zoom
  
  zoom = d3.behavior.zoom().translate([                                       # Define zoom function
  	0                                                                         # Initially reset translation 
  	0
  ]).scale(1)                                                                 # Initially reset scale
  .scaleExtent([                                                              # Define zoom levels
  	1
  	32.89964245299408                                                         
  ]).on('zoom', zoomFunction)
  
  svg = map.append('svg')                                                     # Define svg element
    .attr('width', '100%')
    .attr('height', '100%')
    .call(zoom)
    .on("dblclick.zoom", null);                                               # Disable double click for zooming
  
  g = svg.append('g')                                                         # Define layer that holds countries paths
    .attr('class', 'countries')
        
  # Create countries
  g.selectAll('path')
    .data(topojson.feature(json, json.objects.countries).features)            # features = countries
    .enter()
    .append('path')
    .attr('class', 'land').attr('d', path)                                    # Apply class to all countries for easier selection/styling
  
  .on('mousemove', (d, i) ->                                                  # On mousemouse show country name inside tooltip
    tooltiptop = d3.mouse(d3.select('.map-chart').node())[1]
    tooltipleft = d3.mouse(d3.select('.map-chart').node())[0]
    tooltip.classed('hidden', false)
    .attr('style', 'left:' + tooltipleft + 'px;top:' + tooltiptop + 'px').html d.properties.name
    return
  ).on 'mouseout', (d, i) ->                                                  # On mouseout hide tooltip
  	tooltip.classed 'hidden', true
  	return
      
  # Window on resize function. * Careful! Inside d3.json function!!
  $(window).on 'resize', ->
  	width = $('.map-chart').width()                                           # Update map container dimenions
  	height = $('.map-chart').height()	
  	bounds = path.bounds(topojson.feature(json, json.objects['countries']))   # Update all variables required to keep bounding box inside map container after resizing window
  	projw = bounds[1][0] - bounds[0][0]
  	projh = bounds[1][1] - bounds[0][1]
  	cR = width/height
  	if mR > cR
  		mapScale = width / mR / Math.PI
  	else
  		mapScale = width / cR / Math.PI
  	projection.scale(mapScale*0.9).translate [
  		width / 2
  		height / 2
  	]
  	zoom.on('zoom', zoomFunction)                                             # Updates function with new translation/scale boundaries

	   g.selectAll('path').attr 'd', path                                       # Update countries translation

Map Chart Example

Additional Options

Map Chart with Markers and Marker Tooltips

# Based on previous Map Chart Example

marks = [
  {
    long: -75
    lat: 43
    message: 'Marker Message 1'
  }
  {
    long: -85
    lat: 41
    message: 'Marker Message 2'
  }
  {
    long: 23
    lat: 37
    message: 'Marker Message 3'
  }
]

# Everything happens inside the d3.json function that ensures that the map json data is loaded before executing any of the actions
d3.json 'https://gist.githubusercontent.com/scooterlord/de90b9dd46658dd4983d/raw/92f1e0f843fe1c76dcf515599739d80c51925c9b/world-topo-bordered.json', (json) ->
  
  # Zoom function
  zoomFunction = ->                                                           
    g0.attr 'transform', 'translate(' + t + ')scale(' + s + ')'               # Translate/scale markers layer accordingly
    markers.selectAll('.d3-map-marker')                                       # Select markers and update their position on zoom/pan
      .data(marks)
      .attr('transform', (d) ->
        'translate(' + projection([
          d.long
          d.lat
        ]) + ') scale(' + 1/s + ')')                                          # 1/s ensures that the marker zoom is semantic - marker size stays the same for all zoom levels 

  
  mousemoveFunc = (d, i) ->                                                   # mousemoveFunc defined to show message when hovering on a marker
    tooltiptop = d3.mouse(d3.select('.map-chart').node())[1]
    tooltipleft = d3.mouse(d3.select('.map-chart').node())[0]
    tooltip.classed('hidden', false)
      .attr('style', 'left:' + tooltipleft + 'px;top:' + tooltiptop + 'px')
      .html d.message
    return

  mouseoutFunc = (d, i) ->                                                    # mouseoutFunc defined to hide tooltip when hovering out of a marker
    tooltip.classed 'hidden', true
    return

  g0 = svg.append('g')                                                        # Create group layer to hold various map elements
    .attr('class', 'map-elems')
  
  markers = g0.append('g')                                                    # Create another group layer inside map elements specifically for markers
    .attr('class', 'markers-wrapper')

  markers.selectAll('.d3-map-marker')                                         # Select All markers
    .data(marks)                                                              # Load dataset
    .enter()                                                                  # Create placeholder marker DOM nodes
    .append('circle')                                                         # Create circle for each marker node
    .attr('class', 'd3-map-marker')                                           # Add class for easier selection/styling
    .attr('r', 7)                                                             # Set circle radius
    .attr('transform', (d) ->                                                 # Transform circles into position by converting lat, long to x,y coordinates using the projection
      'translate(' + projection([
        d.long
        d.lat
      ]) + ') scale(' + 1/s + ')')                                            # Ensure marker size stays the same for all zoom levels
    .on('mousemove', mousemoveFunc)                                           # Bind mousemoveFunc on mousemove to show message inside tooltip
    .on('mouseout', mouseoutFunc)                                             # Bind mouseoutFunc on mouseout

  # Window on resize function. * Careful! Inside d3.json function!!
  $(window).on 'resize', ->
    
    markers.selectAll('.d3-map-marker')                                       # Select all markers and update position on resize
      .attr 'transform', (d) ->
        'translate(' + projection([
          d.long
          d.lat
      ]) + ') scale(' + 1/s + ')'

Map Chart with Markers and Marker Tooltips Example

Map Chart with Zoom Buttons

# Based on previous Map Chart Example

# Everything happens inside the d3.json function that ensures that the map json data is loaded before executing any of the actions
d3.json 'https://gist.githubusercontent.com/scooterlord/de90b9dd46658dd4983d/raw/92f1e0f843fe1c76dcf515599739d80c51925c9b/world-topo-bordered.json', (json) ->

  # Add zoom buttons. * Require additional CSS
  d3zoombuttons = d3.select('.map-chart')                                     # Create div element that will hold buttons
    .append('div')
    .attr('class', 'd3-zoom-buttons')
  
  d3zoomin = d3zoombuttons                                                    # Create zoom-in button
    .append('a')
    .attr('class', 'd3-zoom-button d3-zoomin btn-main')
    .on 'click', ->
      zoomByFactor(1.647182034535146)
  
  d3zoomout = d3zoombuttons                                                   # Create zoom-out button
    .append('a')
    .attr('class', 'd3-zoom-button d3-zoomout btn-main')
    .on 'click', ->
      zoomByFactor(1/1.647182034535146)
  
  zoomByFactor = (factor) ->                                                  # Standard zoom function copy/paste
    scale = zoom.scale()
    extent = zoom.scaleExtent()
    newScale = scale * factor
    if newScale < 1 then newScale=1                                           
    if extent[0] <= newScale and newScale <= extent[1]
      t = zoom.translate()
      c = [
        width / 2
        height / 2
      ]
      zoom.scale(newScale).translate([
        c[0] + (t[0] - (c[0])) / scale * newScale
        c[1] + (t[1] - (c[1])) / scale * newScale
      ]).event svg.transition().duration(0)
    return

Map Chart with Zoom Buttons Example

Force Directed Graph

Force Graphs are used for visual representations of networks or associations between elements. A physics simulated layout force is created with settings that drive the interaction among the nodes that are pushed in this force. Depending on the complexity of the layout and on the various applied parameters, a physics cooling factor named alpha slows down the physics simulation in simulation steps named ticks, until the layout settles on a stable solution and stops. Interacting with nodes resets the alpha factor and the simulation resumes until it cools down to a stop again.

For perfomance reasons, we follow a static force layout apporach for mobile devices - assumed o be all touch devices. In this case, we specify a specific number of ticks, approximated by the complexity of the graph to be represented and use CSS3 accelerated translations for zooming and panning.

# Force Directed Graph Example

# Detect a touch device - usually slower performance devices for animated physics simulation
isTouch = ->
  ! !('ontouchstart' of window) or ! !navigator.userAgent.match(/IEMobile/i)

# Define dataset
graph = 
  'nodes': [
    {
      'name': '192.168.192.1'
      'group': 2
      'totalconnections': 2
      'totalbandwidth': 3457346
    }
    {
      'name': '192.168.192.155'
      'group': 1
      'totalconnections': 1
      'totalbandwidth': 7547457
    }
    {
      'name': '192.168.192.203'
      'group': 1
      'totalconnections': 2
      'totalbandwidth': 875633
    }
    {
      'name': '192.147.192.207'
      'group': 2
      'totalconnections': 3
      'totalbandwidth': 346736
    }
    {
      'name': '192.147.192.103'
      'group': 2
      'totalconnections': 2
      'totalbandwidth': 123324
    }
    {
      'name': '192.147.192.115'
      'group': 2
      'totalconnections': 2
      'totalbandwidth': 265786
    }
    {
      'name': '192.147.192.105'
      'group': 1
      'totalconnections': 4
      'totalbandwidth': 984786
    }    
  ]
  'links': [
    {
      'source': 1
      'target': 0
      'value': 1
    }
    {
      'source': 2
      'target': 1
      'value': 20
    }
    {
      'source': 3
      'target':0
      'value': 8
    }
    {
      'source': 4
      'target': 0
      'value': 125
    }
    {
      'source': 5
      'target': 0
      'value': 79
    }
    {
      'source': 6
      'target': 1
      'value': 25
    }    
  ]

tooltip = d3.select('.graph-chart')                                           # Create tooltip
	.append('div')
  .attr('class', 'd3tooltip relativepos hidden')

width = $('.graph-chart').parent().width()                                    # Define graph dimensions
height = $('.graph-chart').parent().height()

nodes = []                                                                    # Create nodes array
links = []																																		# Create links array

# Define scales
color = d3.scale.category20c()																								# Categorical color scale for groups

rScale = d3.scale.linear()																										# Scale for node radius
	.domain([
  	0
    d3.max(graph.nodes, (d) ->																								# Radius size range defined by totalconnections dataset key
      d.totalconnections
    )
  ]).range([
    5
    15
  ])
  
linkScale = d3.scale.linear()																									# Scale for link stroke-width
  .domain([
    0
    d3.max(graph.links, (d) ->
      d.value
    )
  ]).range([
    1
    5
  ])  

# Define zoom behavior
scale=1																																				# Initial zoom scale

zoomWidth = (width - (scale * width)) / 2																			# Define initial position
zoomHeight = (height - (scale * height)) / 2

# Define function for panning/scaling depending on device type - for touch devices the whole parent container is zoomed-panned using CSS3 accelerated translations inside a hidden overflown parent graph-chart-container
# ...as opposed to the svg translations that take over on non-touch devices - considered devices with desktop cpus
if isTouch()
  zoomed = ->
    tooltip.classed 'hidden', true
    svg.attr 'style', 'transform:translate3d(' + d3.round(d3.event.translate[0], 1) + 'px,' + d3.round(d3.event.translate[1], 1) + 'px,0) scale3d(' + d3.round(d3.event.scale,2) + ',' + d3.round(d3.event.scale,2) + ',' + d3.round(d3.event.scale,2) + ');transform-origin:top left;'
    return
else
  zoomed = ->
    tooltip.classed 'hidden', true
    graphcontainer.attr 'transform', 'translate(' + d3.round(d3.event.translate[0], 1) + ',' + d3.round(d3.event.translate[1], 1) + ')scale(' + d3.round(d3.event.scale,2) + ')'
    return 


zoom = d3.behavior.zoom()																											# Constructs zoom behavior
  .translate([zoomWidth,zoomHeight])
  .scale(scale)																																# Use initial zoom scale
  .scaleExtent([																															# Limit zoom extent
    0.25
    2
  ]).on('zoom', zoomed)   

svg = d3.select('.graph-chart')																								# Define svg element
	.append('svg')
  .attr('width', width*scale)
  .attr('height', height*scale)

d3.select('.graph-chart')
	.call(zoom)																																	# Call zoom behavior

rect = svg.append("rect")																											# A rectangle with dimenions equal to the svg is created as a base layer to receive touches for drag behavior
	.attr("width", width)
	.attr("height", height)
	.style("fill", "none")
	.style("pointer-events", "all");

graphcontainer = svg.append('g')																							# Create group element to hold nodes/links, used for zoom behavior
	.attr 'transform', 'translate(' + zoomWidth + ',' + zoomHeight + ') scale(' + scale + ')'

# Create force layout
force = d3.layout.force()
	.gravity(0.3)
	.chargeDistance(800)
	.charge(-5000)
	.linkDistance(80)
	.friction(0.6)
	.size([width, height])																											# The center of the force gravity is positioned at size([x/2,y/2]); in this case using width/height gravity is positioned at horizontal and vertical center of the parent container
  
# Define drag behavior seperately so that it can be applied on nodes manually when needed - useful for mobile static layouts. * Must be defined AFTER force layout
dragstarted = (d) ->
	d3.event.sourceEvent.stopPropagation()
	d3.select(this).classed 'dragging', true
	return

dragged = (d) ->
	d3.select(this).attr('cx', d.x = d3.event.x).attr 'cy', d.y = d3.event.y
	return

dragended = (d) ->
	d3.select(this).classed 'dragging', false
	return

drag = force.drag().origin((d) ->
	d
)

if !isTouch()																																	# Manually applied the physics drag behaviour for desktop devices only. On touch devices dragging a node results in panning than physics dragging
  drag																																				
    .on('dragstart', dragstarted)
    .on('drag', dragged)
    .on('dragend', dragended)  

force.nodes(graph.nodes).links(graph.links).start()														# Start layout force

graphcontainer																																# Create arrow markers to be added on links pointing direction. * Causing issues with IE
	.append('svg:defs')
  .selectAll('marker')
  .data([ 'end' ])
  .enter()
  .append('svg:marker')
  .attr('class', 'd3-marker-arrow')
  .attr('id', String)
  .attr('viewBox', '0 -10 20 20')
  .attr('refX', 0.1)
  .attr('refY', 0)
  .attr('markerUnits', 'userSpaceOnUse')
  .attr('markerWidth', 20)
  .attr('markerHeight', 20)
  .attr('orient', 'auto')
  .append('svg:path')
  .attr 'd', 'M0,-5L10,0L0,5'
  
mousemoveFunc = (d, i) ->                                                     # Define function to show tooltip on hover
	tooltiptop = d3.mouse(d3.select('.graph-chart').node())[1]
	tooltipleft = d3.mouse(d3.select('.graph-chart').node())[0]
	tooltip
    .classed('hidden', false)
    .style('top', tooltiptop + 'px')                                          # Tooltip is position absolutely inside the element
    .style('left', tooltipleft + 'px')
    .text ->
			d.name  

link = graphcontainer.selectAll('.d3-link')																		# Create links
	.data(graph.links)
	.enter()
	.append('path')																															# Path needed for curved arcs. 'line' should be used for straight lines
	.attr('class', 'd3-link')																										# Apply class for easier selection and styling
	.style('stroke-width', (d) ->
		linkScale d.value
	).attr("marker-end", "url(#end)")																						# Append marker arrows on link ends. * The url has to be defined manually for web apps
  
node = graphcontainer.selectAll('.node')																			# Create nodes
	.data(graph.nodes)
  .enter()
  .append('circle')
  .attr('class', 'node')
	.attr('r', (d) ->
  	d.radius = rScale(d.totalconnections)																			# Set node radius size and also define d.radius used later for creating an offset from the center of each node equal to its radius for corrent marker arrow positioning
  ).style('fill', (d) ->
		color d.group)
	.on('mouseover', mousemoveFunc)																							# Show tooltip. Same function is applied for both mouseover and mousemove for optimal results on mobile devices
	.on('mousemove', mousemoveFunc)
	.on('mouseout', ->
		tooltip.classed 'hidden', true
	).call(force.drag)																													# Call drag behavior

tickFunction = ->																															# Define function to run on every force tick until the layout settles down. Used to create and position link arcs.
	link.attr('x1', (d) ->
		d3.round(d.source.x ,1)
	).attr('y1', (d) ->
		d3.round(d.source.y ,1)
	).attr('x2', (d) ->
		d3.round(d.target.x ,1)
	).attr 'y2', (d) ->
		d3.round(d.target.y ,1)
	
	node.attr('cx', (d) ->
		d3.round(d.x , 1)
	).attr 'cy', (d) ->
		d3.round(d.y , 1)

	link.attr 'd', (d) ->
		tightness = 6.0																														# Tightness defines how many points will define the arc. Higher tightness -> straighter links
		dx = d.target.x - (d.source.x)
		dy = d.target.y - (d.source.y)
		dr = Math.sqrt(dx * dx + dy * dy)
		qx = d.source.x + dx / 2.0 - (dy / tightness)
		qy = d.source.y + dy / 2.0 + (dx / tightness)
		dqx = d.target.x - qx
		dqy = d.target.y - qy
		qr = Math.sqrt(dqx * dqx + dqy * dqy)
		qr = 1 if qr == 0
		offset = d.target.radius + 10																							# d.target.radius defined inside the node. See above. 
		theta = Math.atan2(dy, dx) + Math.PI / 7.85
		d90 = Math.PI / 2
		tx = d.target.x - (dqx / qr*offset)																				# End of the link is positioned at th edge of node radius
		ty = d.target.y - (dqy / qr*offset)
		'M' + d3.round(d.source.x,1) + ',' + d3.round(d.source.y,1) + 'Q' + d3.round(qx,1) + ',' + d3.round(qy,1) + ' ' + d3.round(tx,1) + ',' + d3.round(ty,1) 
	return


# On touch devices, for performance reasons, a static force layout is formed by running the tick a specific number of times and showing the graph once (ticks=100 in this case). Since we are using a static layout for touch devices, the calculation of nodes positions, links positions and links arcs are only calculated on force end - after the graph appears, while on desktop devices we calculate them on every tick
if isTouch()																																	
  n = 100
  j = n * n
  while j > 0
    force.tick()
    --j 
  force.stop()
  force.on 'end', ->
    tickFunction()  
else
  force.on 'tick', ->
    tickFunction()

$(window).on 'resize', ->
	tooltip.classed 'hidden', true
	
	width = $('.graph-chart').parent().width()																	# Update map container dimensions
	height = $('.graph-chart').parent().height()
	
	d3.select('.graph-chart')																										# Change parent container dimensions
		.select('svg')
		.attr('width', width)
		.attr('height', height)
    
	rect																																				# Change base layer dimensions
		.attr('width', width)
		.attr('height', height)
    
	force.size([																																# Reposition force at the center of the parent container
		width
		height
	]).resume()
	return

Force Directed Graph Example

© 2016 Crypteia Networks