Skip to content

Instantly share code, notes, and snippets.

@lmatteis
Last active April 12, 2016 10:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lmatteis/73ce86ae3ce3332035c9c2829a94b7c1 to your computer and use it in GitHub Desktop.
Save lmatteis/73ce86ae3ce3332035c9c2829a94b7c1 to your computer and use it in GitHub Desktop.
Multi-chart interactions using d3.dispatch and crossfilter

This shows how separate charts can be enclosed in d3.dispatch events, and based on generic update events, we can trigger each chart to update accordingly. Crossfilter .filter() takes care of updating the dimensions groups so we simply need to re-trigger part of the chart's code that updates the chart with new data.

<!DOCTYPE html>
<meta charset='utf-8'>
<style>
.bar--positive {
fill: #9BCCF5;
}
.bar--negative {
fill: pink;
}
text {
font: 10px sans-serif;
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.title {
font-size: 13px;
font-weight:bold;
}
.reset { cursor: pointer;}
</style>
<body>
<br />
<span id='bubble-chart'></span>
<span id='bar-chart'></span>
<script src='http://crossfilter.github.io/crossfilter/crossfilter.v1.min.js'></script>
<script src='https://d3js.org/d3.v3.min.js'></script>
<script>
var data = [{"topic":"BMW","age":"18-24","gender":"male","interactions":21600,"key":"BMW","positiveInteractions":21600},{"topic":"BMW","age":"18-24","gender":"female","interactions":-5500,"key":"BMW","positiveInteractions":5500},{"topic":"BMW","age":"25-34","gender":"male","interactions":19500,"key":"BMW","positiveInteractions":19500},{"topic":"BMW","age":"25-34","gender":"female","interactions":-5000,"key":"BMW","positiveInteractions":5000},{"topic":"BMW","age":"35-44","gender":"male","interactions":10700,"key":"BMW","positiveInteractions":10700},{"topic":"BMW","age":"35-44","gender":"female","interactions":-3500,"key":"BMW","positiveInteractions":3500},{"topic":"BMW","age":"45-54","gender":"male","interactions":5700,"key":"BMW","positiveInteractions":5700},{"topic":"BMW","age":"45-54","gender":"female","interactions":-2400,"key":"BMW","positiveInteractions":2400},{"topic":"BMW","age":"55-64","gender":"male","interactions":2500,"key":"BMW","positiveInteractions":2500},{"topic":"BMW","age":"55-64","gender":"female","interactions":-1100,"key":"BMW","positiveInteractions":1100},{"topic":"BMW","age":"65+","gender":"male","interactions":1600,"key":"BMW","positiveInteractions":1600},{"topic":"BMW","age":"65+","gender":"female","interactions":-600,"key":"BMW","positiveInteractions":600},{"topic":"Ford Mustang","age":"18-24","gender":"male","interactions":7600,"key":"Ford Mustang","positiveInteractions":7600},{"topic":"Ford Mustang","age":"18-24","gender":"female","interactions":-800,"key":"Ford Mustang","positiveInteractions":800},{"topic":"Ford Mustang","age":"25-34","gender":"male","interactions":7100,"key":"Ford Mustang","positiveInteractions":7100},{"topic":"Ford Mustang","age":"25-34","gender":"female","interactions":-900,"key":"Ford Mustang","positiveInteractions":900},{"topic":"Ford Mustang","age":"35-44","gender":"male","interactions":5100,"key":"Ford Mustang","positiveInteractions":5100},{"topic":"Ford Mustang","age":"35-44","gender":"female","interactions":-800,"key":"Ford Mustang","positiveInteractions":800},{"topic":"Ford Mustang","age":"45-54","gender":"male","interactions":3500,"key":"Ford Mustang","positiveInteractions":3500},{"topic":"Ford Mustang","age":"45-54","gender":"female","interactions":-600,"key":"Ford Mustang","positiveInteractions":600},{"topic":"Ford Mustang","age":"55-64","gender":"male","interactions":1400,"key":"Ford Mustang","positiveInteractions":1400},{"topic":"Ford Mustang","age":"55-64","gender":"female","interactions":-300,"key":"Ford Mustang","positiveInteractions":300},{"topic":"Ford Mustang","age":"65+","gender":"male","interactions":600,"key":"Ford Mustang","positiveInteractions":600},{"topic":"Ford Mustang","age":"65+","gender":"female","interactions":-200,"key":"Ford Mustang","positiveInteractions":200},{"topic":"Ford Motor Company","age":"25-34","gender":"male","interactions":4300,"key":"Ford Motor Company","positiveInteractions":4300},{"topic":"Ford Motor Company","age":"25-34","gender":"female","interactions":-800,"key":"Ford Motor Company","positiveInteractions":800},{"topic":"Ford Motor Company","age":"18-24","gender":"male","interactions":4200,"key":"Ford Motor Company","positiveInteractions":4200},{"topic":"Ford Motor Company","age":"18-24","gender":"female","interactions":-700,"key":"Ford Motor Company","positiveInteractions":700},{"topic":"Ford Motor Company","age":"35-44","gender":"male","interactions":3000,"key":"Ford Motor Company","positiveInteractions":3000},{"topic":"Ford Motor Company","age":"35-44","gender":"female","interactions":-600,"key":"Ford Motor Company","positiveInteractions":600},{"topic":"Ford Motor Company","age":"45-54","gender":"male","interactions":1800,"key":"Ford Motor Company","positiveInteractions":1800},{"topic":"Ford Motor Company","age":"45-54","gender":"female","interactions":-500,"key":"Ford Motor Company","positiveInteractions":500},{"topic":"Ford Motor Company","age":"55-64","gender":"male","interactions":800,"key":"Ford Motor Company","positiveInteractions":800},{"topic":"Ford Motor Company","age":"55-64","gender":"female","interactions":-300,"key":"Ford Motor Company","positiveInteractions":300},{"topic":"Ford Motor Company","age":"65+","gender":"male","interactions":500,"key":"Ford Motor Company","positiveInteractions":500},{"topic":"Ford Motor Company","age":"65+","gender":"female","interactions":-200,"key":"Ford Motor Company","positiveInteractions":200}];
var dispatch = d3.dispatch('loadBar', 'loadBubble', 'update');
dispatch.on('loadBar', function (dimension, group) {
var margin = {top: 0, right: 0, bottom: 40, left: 50},
width = 480 - margin.left - margin.right,
height = 480 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.ticks(10);
var t = d3.transition()
.duration(750);
var click;
var svg = d3.select('#bar-chart'),
g = svg.select('g');
if (g.empty()) {
g = svg.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 + ')');
g.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end')
.text('Interactions');
var reset = g.append('text')
.attr('class', 'reset')
.attr('y', 10)
.attr('x', 20)
.style('display', 'none')
.text('reset')
.on('click', function () { dimension.filter(null); dispatch.update(); reset.style('display', 'none') })
}
dispatch.on('update.bar', function () {
x.domain(group.all().map(function(d) { return d.key; }));
y.domain([0, d3.max(group.all(), function(d) { return d.value; })]);
g.select('.y.axis')
.transition(t)
.call(yAxis)
g.select('.x.axis')
.transition(t)
.call(xAxis)
var rects = g.selectAll('rect')
.data(group.all());
rects.enter().append('rect');
rects
.on('click', function (d) { dimension.filter(d.key); dispatch.update(); reset.style('display', 'block') })
.transition(t)
.attr('class', function(d) { return 'bar bar--' + (d.key == 'female' ? 'negative' : 'positive'); })
.attr('x', function(d) { return x(d.key); })
.attr('width', x.rangeBand())
.attr('y', function(d) { return y(d.value); })
.attr('height', function(d) { return height - y(d.value); })
var texts = g.selectAll('.label')
.data(group.all())
texts.enter().append('text').attr('class', 'label')
texts
.transition(t)
.attr('text-anchor', 'middle')
.attr('x', function(d,i) {
return x(d.key) + (x.rangeBand() / 2);
})
.attr('y', function(d,i) {
return y(d.value) + ((height - y(d.value)) / 2);
})
.attr('dy', '.35em')
.text(function (d) { return d.value })
})
})
dispatch.on('loadBubble', function (dimension, group) {
var margin = {top: 0, right: 0, bottom: 0, left: 0},
diameter = 460 - margin.left - margin.right;
var onClick;
var color = d3.scale.category10();
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.padding(1.5);
var t = d3.transition()
.duration(750);
var svg = d3.select('#bubble-chart'),
g = svg.select('g');
if (!svg.empty()) {
svg.select('svg').remove()
}
g = svg.append('svg')
.attr('width', diameter + margin.left + margin.right)
.attr('height', 480 + margin.top + margin.bottom)
.attr('class', 'bubble')
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
var reset = g.append('text')
.attr('class', 'reset')
.style('display', 'none')
.attr('y', 10)
.attr('x', 20)
.text('reset')
.on('click', function () { dimension.filter(null); dispatch.update(); reset.style('display', 'none') })
var node = g.selectAll('.node')
.data(bubble.nodes({ children: group.all() }).filter(function(d) { return !d.children; }))
node.enter().append('g')
.attr('class', 'node')
.attr('transform', function(d) {return 'translate(' + d.x + ',' + d.y + ')'; });
node.append('title')
.text(function(d) { return d.key; });
node.append('circle')
.attr('r', function(d) { return d.r; })
.style('fill', function(d) { return color(d.key); })
node.append('text')
.attr('dy', '.3em')
.style('text-anchor', 'middle')
.text(function(d) { return d.key + ' - ' + d.value; });
dispatch.on('update.bubble', function () {
var reset = g.selectAll('.reset')
node = g.selectAll('.node')
.data(bubble.nodes({ children: group.all() }).filter(function(d) { return !d.children; }))
node
.attr('class', 'node')
.transition(t)
.attr('transform', function(d) {return 'translate(' + d.x + ',' + d.y + ')'; });
node.select('circle')
.on('click', function (d) { dimension.filter(d.key); dispatch.update(); reset.style('display', 'block') })
.transition(t)
.attr('r', function(d) { return d.r; })
.style('fill', function(d) { return color(d.key); })
node.select('text')
.attr('dy', '.3em')
.style('text-anchor', 'middle')
.text(function(d) { if (d.value) return d.key + ' - ' + d.value; });
})
})
var xf = crossfilter(data)
var gender = xf.dimension(function (d) { return d.gender; }),
genders = gender.group().reduceSum(function (d) { return d.positiveInteractions; }),
topic = xf.dimension(function (d) { return d.topic }),
topics = topic.group().reduceSum(function (d) { return d.positiveInteractions; })
dispatch.loadBar(gender, genders);
dispatch.loadBubble(topic, topics);
dispatch.update();
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment