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.
Last active
April 12, 2016 10:03
-
-
Save lmatteis/73ce86ae3ce3332035c9c2829a94b7c1 to your computer and use it in GitHub Desktop.
Multi-chart interactions using d3.dispatch and crossfilter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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