Published
Edited
Feb 8, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require('d3@6')
Insert cell
Insert cell
// import all datasets from vega first
all_datasets = require('vega-datasets');
Insert cell
// the dataset we are using for this tutorial
gapminder = all_datasets['gapminder.json']();
Insert cell
Insert cell
Insert cell
Insert cell
html`
<ol>
<li><text>China: 1,303,182,268</text></li>
<li><text>India: 1,080,264,388</text></li>
<li><text>United States: 295,734,134</text></li>
<li><text>Indonesia: 218,465,000</text></li>
<li><text>Brazil: 186,112,794</text></li>
<li><text>Pakistan: 162,419,946</text></li>
<li><text>Bangladesh: 144,319,628</text></li>
<li><text>Nigeria: 128,765,768</text></li>
<li><text>Japan: 127,417,244</text></li>
<li><text>Mexico: 106,202,903</text></li>
</ol>
`
Insert cell
Insert cell
{
const ol = d3.create('ol'); // the ordered list element
ol.selectAll('li') // selects all 'li' elements (which may or may not exist!)
.data(listData) // provides the backing data
return ol.node(); // return the DOM element node of the 'ol' element
}
Insert cell
Insert cell
selection = {
const ol = d3.create('ol');
return ol.selectAll('li').data(listData);
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const ol = d3.create('ol'); // the ordered list element
ol.selectAll('li') // selects all 'li' elements (which may or may not exist!)
.data(listData) // provides the backing data
.join('li') // joins data with the selection, creating 'li' elements as needed
return ol.node(); // return the DOM element node of the 'ol' element
}
Insert cell
Insert cell
{
const ol = d3.create('ol'); // the ordered list element
ol.selectAll('li') // selects all 'li' elements (which may or may not exist!)
.data(listData) // provides the backing data
.join(
enter => enter.append('li'), // <-- append an li element for each entering item
update => update, // <-- do nothing with items that match an existing element
exit => exit.remove() // <-- remove li elements whose backing data is now gone
)
return ol.node(); // return the DOM element node of the 'ol' element
}
Insert cell
Insert cell
list = {
const fmt = d3.format(',');
const ol = d3.create('ol'); // the ordered list element
ol.selectAll('li') // selects all 'li' elements (which may or may not exist!)
.data(listData) // provides the backing data
.join('li') // joins data with the selection, creating 'li' elements as needed
.attr('class', 'country') // set CSS class to 'country'
.text( // add formatted text to 'li' elements
d => `${d.country}: ${fmt(d.pop)}` // a lambda function that specifies the text for each of the entry
)
return ol.node(); // return the DOM element node of the 'ol' element
}
Insert cell
Insert cell
Insert cell
// Run this cell to recolor just the first item in the list above
d3.select('li.country').style('color', randomColor()), null
Insert cell
// Run this cell to recolor ALL items in the list above
d3.selectAll('li.country').style('color', randomColor()), null
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const ol = d3.select('ol#enter-update-exit');

// use a new dataset, manipulable with the variables defined above
const newData = gapminder
.filter(d => d.year === year)
.sort((a, b) => b.pop - a.pop)
.slice(0, n);
ol.selectAll('li') // select all list elements
.data(newData, d => d.country) // bind our data values
.join(
/* TODO: edit these functions */
enter => enter.append('li'),
update => update,
exit => exit.remove()
)
.text(d => `${d.country}: ${d.pop}`);
return newData;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
md`Width is ${width} px and height is ${height} px.` // Observable's built-in width property!
Insert cell
Insert cell
Insert cell
{
const container = d3.create('div');
const data = barData;
const barChart = container.selectAll('div')
.data(data)
.join('div')
barChart
.style('background', 'steelblue') // Set the background color of the div to 'steelblue'
.style('border', '1px solid white') // Set the border of the div as 'white'
.style('font-size', 'small') // Set the text font size in the div to 'small'
.style('color', 'white') // Set the text font color to 'white'
.style('text-align', 'right') // Set the text alignment in the div to 'right'
.style('padding', '3px') // HINT: CSS styles require units, not just numbers
/* TODO: Add additional styles here to adjust the width and height of divs and append text labels */
return container.node();
}
Insert cell
Insert cell
Insert cell
xscale = d3.scaleLinear()
.domain([0, d3.max(barData, d => d.value)])
.range([0, width]) // Observable's built-in width property!
Insert cell
Insert cell
xscale(49)
Insert cell
Insert cell
{
const container = d3.create('div');
const data = barData;
const barChart = container.selectAll('div')
.data(data)
.join('div')
barChart
.style('background', 'steelblue') // Set the background color of the div to 'steelblue'
.style('border', '1px solid white') // Set the border of the div as 'white'
.style('font-size', 'small') // Set the text font size in the div to 'small'
.style('color', 'white') // Set the text font color to 'white'
.style('text-align', 'right') // Set the text alignment in the div to 'right'
.style('padding', '3px') // HINT: CSS styles require units, not just numbers
.style('width', d => xscale(d.value) + 'px')
.style('height', '15px')
.text(d => d.value)
return container.node();
}
Insert cell
Insert cell
color = d3.scaleOrdinal(d3.schemeTableau10).domain(barData.map(d => d.key));
// Default color schemes are here: https://github.com/d3/d3-scale-chromatic#categorical
// Try another one! For example: d3.schemePurples[6] or d3.schemePaired
Insert cell
{
const container = d3.create('div');

container.selectAll('div')
.data(barData)
.join('div')
.style('background', d => color(d.key)) // <-- Use color scale to specify the bar color
.style('border', '1px solid white')
.style('font-size', 'small')
.style('color', 'white')
.style('text-align', 'right')
.style('padding', '3px')
.style('width', d => `${xscale(d.value)}px`)
.text(d => d.value);

return container.node();
}
Insert cell
Insert cell
{
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
svg.selectAll('circle')
.data([{'x':0,'y':0,'color':'orange'},
{'x':width,'y':height,'color':'blue'}])
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 50)
.style('fill', d => d.color)
return svg.node();
}
Insert cell
Insert cell
height = 150
Insert cell
yscale = d3.scaleBand()
.domain(barData.map(d => d.key)) // 0th element at 0, last element + 1 at svgHeight
.range([0, height])
Insert cell
{
// create svg element, specify total width and height
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

// add SVG 'rect' elements for bars
container.selectAll('rect')
.data(barData)
.join('rect')
.attr('x', 0)
.attr('y', d => yscale(d.key))
.attr('width', d => xscale(d.value))
.attr('height', yscale.bandwidth()) // <-- Band scales split a pixel range into equal-sized bands
.style('fill', d => color(d.key))
.style('stroke', 'white');

// add SVG 'text' elements for labels
container.selectAll('text')
.data(barData)
.join('text')
.attr('x', d => xscale(d.value))
.attr('y', d => yscale(d.key))
.attr('dx', -20)
.attr('dy', '1.25em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);

// return SVG DOM element
return container.node();
}
Insert cell
Insert cell
Insert cell
{
// you may want to use a new scale, which has been defined for you
const new_xscale = d3.scaleBand()
.domain(barData.map(d => d.key))
.range([0, width / 2])
const new_yscale = d3.scaleLinear()
.domain([0, d3.max(barData, d => d.value)])
.range([0, height])
// create svg element, specify total width and height
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

// add SVG 'rect' elements for bars
container.selectAll('rect')
.data(barData)
.join('rect')
.attr('x', 0) // change this
.attr('y', d => yscale(d.key)) // change this
.attr('width', d => xscale(d.value)) // change this
.attr('height', yscale.bandwidth()) // change this
.style('fill', d => color(d.key))
.style('stroke', 'white');

// add SVG 'text' elements for labels
container.selectAll('text')
.data(barData)
.join('text')
.attr('x', d => xscale(d.value)) // change this
.attr('y', d => yscale(d.key)) // change this
.attr('dx', -20) // change this
.attr('dy', '1.25em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);

// return SVG DOM element
return container.node();
}
Insert cell
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

const g = container.selectAll('g')
.data(barData)
.enter() // <-- We use "enter" (not "join") so we can later append <rect> and <text> only for new data.
.append('g')
.attr('transform', d => `translate(0, ${yscale(d.key)})`);

g.append('rect')
// We no longer need to bind data to <rect>, or specify y positions.
.attr('width', d => xscale(d.value))
.attr('height', yscale.bandwidth())
.style('fill', d => color(d.key))
.style('stroke', 'white');

g.append('text')
// We no longer need to bind data to <text>, or specify y positions.
.attr('x', d => xscale(d.value))
.attr('dx', -20)
.attr('dy', '1.2em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);
return container.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
margin = ({top: 10, right: 10, bottom: 20, left: 20})
Insert cell
Insert cell
// Domain (data values): [0, 78], Range (pixel values): [20, width - 10]
xMargin = xscale.copy().range([margin.left, width - margin.right])
Insert cell
// Remember (0, 0) is the top-left hand corner; try flipping the elements and see what happens!
// Domain (data values): [0, 1, 2, 3, 4, 5], Range (pixel values): [height - 20, 10]
yMargin = yscale.copy().range([margin.top, height - margin.bottom])
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height)
.style('border', '1px dotted #999'); // <-- A dotted border helps us see the added margin

const g = container.selectAll('g')
.data(barData)
.enter()
.append('g')
.attr('transform', d => `translate(${margin.left}, ${yMargin(d.key)})`);

g.append('rect')
.attr('width', d => xMargin(d.value) - xMargin(0)) // <-- We must subtract our left offset.
.attr('height', yMargin.bandwidth())
.style('fill', d => color(d.key))
.style('stroke', 'white');

g.append('text')
.attr('x', d => xMargin(d.value) - xMargin(0)) // <-- We must subtract our left offset.
.attr('dx', -20)
.attr('dy', '1em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);

return container.node();
}
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height)
.style('border', '1px dotted #999');

container.append('g')
.attr('transform', `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(xMargin));

container.append('g')
.attr('transform', `translate(${margin.left}, 0)`)
.call(d3.axisLeft(yMargin));

return container.node();
}
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

const g = container.selectAll('g')
.data(barData)
.enter()
.append('g')
.attr('transform', d => `translate(${margin.left}, ${yMargin(d.key)})`);

g.append('rect')
.attr('width', d => xMargin(d.value) - xMargin(0))
.attr('height', yMargin.bandwidth())
.style('fill', d => color(d.key))
.style('stroke', 'white');

g.append('text')
.attr('x', d => xMargin(d.value) - xMargin(0))
.attr('dx', -20)
.attr('dy', '1em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);

container.append('g')
.attr('transform', `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(xMargin));

container.append('g')
.attr('transform', `translate(${margin.left}, 0)`)
.call(d3.axisLeft(yMargin));

return container.node();
}
Insert cell
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