Published
Edited
Mar 12, 2020
1 fork
1 star
Insert cell
md`# D3.js merge() extended - Small Multiples`
Insert cell
md`This is a quick demo to accompany my [Codementor article](https://www.codementor.io/@milesbryony/d3-js-merge-extended-small-multiples-14efbozjk5) on using d3.js merge for small multiples.`
Insert cell
chart = {
const color_key = {"star":"gold","boo":"blue","toad":"red"};
//two panels per row
const panels_per_row = 3;
const panel_width = (width - (margin * 3))/panels_per_row;
const height = margin + ((panel_width + margin) * (parseInt(my_data.length/2)+1));
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
let years = d3.set(my_data[0].chart_data, d => d.year).values();
const y_max = d3.max(my_data, d => d3.max(d.chart_data, m => m.value));
const start_x = margin;
const start_y = 80 + margin;
const x_scale = d3.scaleBand().domain(years).range([0,panel_width]);
const y_scale = d3.scaleLinear().domain([0,y_max]).range([panel_width - start_y - margin,0]);
const line = d3.line()
.x(d => x_scale(d.year))
.y(d => y_scale(d.value));
const area = d3.area()
.x(d => x_scale(d.year))
.y0(d => y_scale(0))
.y1(d => y_scale(d.value));
//join
var my_group = svg.selectAll('.chart_group')
.data(my_data, d => d.id);
//exit and remove
my_group.exit().remove();
//enter new groups
var enter = my_group.enter()
.append("g")
.attr("class","chart_group");

//append elements to new group
enter.append("rect").attr("class","group_rect");
enter.append("text").attr("class","group_text");
enter.append("image").attr("class","group_image");
enter.append("g").attr("class","sub_chart_group");

//merge
my_group = my_group.merge(enter);
position_group_elements(my_group);
//join
var sub_group = my_group.select(".sub_chart_group").selectAll('.sub_chart_elements_group')
.data(d => [d.chart_data]); // data is wrapped in an array because this is a line/area chart
//exit and remove
sub_group.exit().remove();
//enter new groups
var sub_enter = sub_group.enter().append("g").attr("class","sub_chart_elements_group");

//append elements to new group
sub_enter.append("path").attr("class","chart_line");
sub_enter.append("path").attr("class","chart_area");
//merge
sub_group = sub_group.merge(sub_enter);
sub_group.select(".chart_line")
.attr("d", line)
.attr("fill","none")
.attr("stroke",function(d){
d.color = color_key[this.parentElement.parentElement.id];
return d.color;
})
.attr("stroke-width",1)
.attr("transform","translate(" + start_x + "," + start_y + ")");
sub_group.select(".chart_area")
.attr("d", area)
.attr("stroke","none")
.attr("fill",d => d.color)
.attr("fill-opacity",0.2)
.attr("transform","translate(" + start_x + "," + start_y + ")");

function position_group_elements(my_group){

//position rectangle
my_group.select(".group_rect")
.attr("x", function(d,i){
//two groups per row so
var position = i % panels_per_row;
d.x_pos = (position * (panel_width + margin)) + margin;
d.y_pos = (parseInt(i/panels_per_row) * (panel_width + margin)) + margin;
return d.x_pos;
})
.attr("y", d => d.y_pos)
.attr("fill","white")
.attr("stroke","#333333")
.attr("stroke-width",0.5)
.attr("rx",5)
.attr("ry",5)
.attr("width",panel_width)
.attr("height",panel_width);
//then position text
my_group.select(".group_text")
.attr("x", d => (panel_width/2) + d.x_pos)
.attr("y", d => 75 + d.y_pos)
.attr("text-anchor","middle")
.attr("font-weight","bold")
.attr("font-family","Arial")
.text(d => d.title.toUpperCase());
//then position images
my_group.select(".group_image")
.attr("xlink:href", d => "https://bl.ocks.org/BMPMS/raw/f1acde6c8d8d8339d6b638c9e47bc01a/" + d.image)
.attr("x", d => (panel_width/2) + d.x_pos - 20)
.attr("y", d => d.y_pos + 15)
.attr("width",40)
.attr("height",40);
//then position sub groups
my_group.select(".sub_chart_group")
.attr("id",d => d.id)
.attr("transform", d => "translate(" + d.x_pos + "," + d.y_pos + ")");
};

return svg.node();
}
Insert cell
width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
Insert cell
margin = 20;
Insert cell
my_data = [
{id:"star",title:"star chart",image:"star.png","chart_data":[
{"year":1999,"value":55},{"year":2000,"value":85},{"year":2001,"value":105},{"year":2002,"value":120},{"year":2003,"value":130},{"year":2004,"value":150},{"year":2005,"value":155},{"year":2006,"value":165},{"year":2007,"value":175},{"year":2008,"value":180}
]},
{id:"toad",title:"toad chart",image:"toad.png","chart_data":[
{"year":1999,"value":25},{"year":2000,"value":35},{"year":2001,"value":45},{"year":2002,"value":60},{"year":2003,"value":90},{"year":2004,"value":120},{"year":2005,"value":140},{"year":2006,"value":90},{"year":2007,"value":70},{"year":2008,"value":50}
]},
{id:"boo",title:"boo chart",image:"boo.png","chart_data":[
{"year":1999,"value":155},{"year":2000,"value":185},{"year":2001,"value":155},{"year":2002,"value":150},{"year":2003,"value":140},{"year":2004,"value":90},{"year":2005,"value":75},{"year":2006,"value":65},{"year":2007,"value":30},{"year":2008,"value":20}
]}
]
Insert cell
d3 = require("d3@5")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more