Published
Edited
Mar 15, 2021
12 forks
Importers
202 stars
Insert cell
Insert cell
Insert cell
d3 = require('d3@6')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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
{
const ol = d3.create('ol');
ol.selectAll('li') // select all list elements (orange circle below)
.data(listData) // bind all our data values (blue circle below)
return ol.node(); // return the DOM element node of the 'ol' element for Observable to render
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const ol = d3.create('ol');
ol.selectAll('li') // select all list elements (orange circle above)
.data(listData) // bind all our data values (blue circle above)
.join('li') // a selection that merges the "enter" and "update" states

return ol.node();
}
Insert cell
Insert cell
{
const ol = d3.create('ol');
ol.selectAll('li') // select all list elements (orange circle above)
.data(listData) // bind all our data values (blue circle above)
.join(
enter => enter.append('li'),
update => update,
exit => exit.remove()
)
return ol.node();
}
Insert cell
Insert cell
{
const ol = d3.create('ol');
ol.selectAll('li') // select all list elements (orange circle above)
.data(listData) // bind all our data values (blue circle above)
.join('li')
.attr('class', 'country') // set each list element's "class" attribute to "country"
.text(d => `${d.country}: ${fmt(d.pop)}`) // add text to each list element
return ol.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const ol = d3.select('ol#enter-update-exit');
// use a new dataset, which can be manipulated with the sliders 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) // bind our data values; what do you notice about which li's are red/green/blue?
.join(
enter => enter.append('li'),
update => update,
exit => exit.remove()
)
.text(d => `${d.country}: ${fmt(d.pop)}`)
return newData;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const container = d3.create('div');
const barChart = container.selectAll(/* TODO */)

/* TODO: Add code here to generate the bar chart within the container.
(Hint: The "barData" variable holds the data to visualize.) */
barChart
.style('background', 'steelblue') // Set the background color of the div to 'steelblue'
.style('border', '1px solid white') // Set the border of the divs to 'white'
.style('padding', '3px') // Give the bars some padding
.style('font-size', 'small') // Set the font size of text inside the div to 'small'
/* TODO: Add code here to finish styling the visual properties and content of <div> elements */
return container.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
xScale = d3.scaleLinear()
.domain([0, d3.max(barData, d => d.value)])
.range([0, width])
Insert cell
Insert cell
xScale(49)
Insert cell
Insert cell
{
const container = d3.create('div');
container.selectAll('div')
.data(barData)
.join('div')
.style('background', 'steelblue')
.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`) // Use "xScale" instead of a magic number here.
.text(d => d.value)
return container.node();
}
Insert cell
Insert cell
colorScale = 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 => colorScale(d.key))
.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
Insert cell
{
const barHeight = 25,
height = barData.length * barHeight;
// 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, i) => i * barHeight)
.attr('width', d => xScale(d.value))
.attr('height', barHeight)
.style('fill', d => colorScale(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, i) => i * barHeight)
.attr('dx', -20)
.attr('dy', '1.2em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value)
// return SVG DOM element
return container.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
yScale = d3.scaleBand()
.domain(barData.map(d => d.key))
.range([height, 0]) // Try flipping this to [0, height] and see the effect on the bars below.
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);
container.selectAll('rect')
.data(barData)
.join('rect')
.attr('x', 0)
.attr('y', d => yScale(d.key)) // Use the "yScale" here instead of manually positioning bars
.attr('width', d => xScale(d.value))
.attr('height', yScale.bandwidth()) // Band scales divide a pixel range into equally-sized bands
.style('fill', d => colorScale(d.key))
.style('stroke', 'white')
container.selectAll('text')
.data(barData)
.join('text')
.attr('x', d => xScale(d.value))
.attr('y', d => yScale(d.key)) // Use the "yScale" here instead of manually positioning labels
.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
{
const width = 400,
height = 200;
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);
// Redefine the x scale here.
const x = /* add code here */;
// Redefine the y scale here.
const y = /* add code here */;
container.selectAll('rect')
.data(barData)
.join('rect')
.attr('x', d => /* add code here */)
.attr('y', d => /* add code here */)
.attr('width', d => /* add code here */)
.attr('height', d => /* add code here */)
.style('fill', d => color(d.key))
.style('stroke', 'white')
container.selectAll('text')
.data(barData)
.join('text')
.attr('x', d => /* add code here */)
.attr('y', d => /* add code here */)
.attr('dx', /* add code here */)
.attr('dy', '1em')
.attr('fill', 'white')
.style('font-size', 'small')
.style('text-anchor', 'middle')
.text(d => d.value)
return container.node();
}
Insert cell
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')
// Notice, 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 => colorScale(d.key))
.style('stroke', 'white');
g.append('text')
// Notice, 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
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 to see what happens!
// Domain (data values): [0, 1, 2, 3, 4, 5]; Range (pixel values): [height - 20, 10]
yMargin = yScale.copy().range([height - margin.bottom, margin.top])
Insert cell
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 whitespace.
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)) // Notice, we must subtract our left offset.
.attr('height', yMargin.bandwidth())
.style('fill', d => colorScale(d.key))
.style('stroke', 'white');
g.append('text')
.attr('x', d => xMargin(d.value) - xMargin(0)) // Notice, 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 => colorScale(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
Insert cell
Insert cell
{
const height = 500;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
const xScale = d3.scaleLinear()
.domain([0, d3.max(gapminder, d => d.fertility)])
.range([margin.left, width - margin.right])
.nice();
const yScale = d3.scaleLinear()
.domain([0, d3.max(gapminder, d => d.life_expect)])
.range([height - margin.bottom, margin.top])
.nice();

const colorScale = d3.scaleOrdinal()
.domain(gapminder.map(d => d.cluster))
.range(d3.schemeTableau10); // try other schemes, too!
const year = 1955;
const data = gapminder.filter(d => d.year === year);
const symbol = d3.symbol();
svg.append('g')
.attr('transform', d => `translate(${margin.left}, ${margin.top})`)
.selectAll('path') // d3-shape functions (like d3.symbol) generate attributes for SVG <path> elements
.data(data)
.join('path')
.attr('transform', d => `translate(${xScale(d.fertility)}, ${yScale(d.life_expect)})`)
.attr('fill', d => colorScale(d.cluster))
.attr('d', d => symbol()) // Notice, the output of the d3.symbol is wired up to the "d" attribute.
gapminderScatterAxesLabel(svg, height, xScale, yScale, year);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const height = 500;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
const xScale = d3.scaleLinear()
.domain([0, d3.max(gapminder, d => d.fertility)])
.range([margin.left, width - margin.right])
.nice();
const yScale = d3.scaleLinear()
.domain([0, d3.max(gapminder, d => d.life_expect)])
.range([height - margin.bottom, margin.top])
.nice();

const colorScale = d3.scaleOrdinal()
.domain(gapminder.map(d => d.cluster))
.range(d3.schemeTableau10); // try other schemes, too!
/* TODO: Add code here for two new scales for shape and size.
Hint: d3.symbols is an array of all built-in symbol types.
*/
const year = 1955;
const data = gapminder.filter(d => d.year === year);
svg.append('g')
.attr('transform', d => `translate(${margin.left}, ${margin.top})`)
.selectAll('path')
.data(data)
.join('path')
.sort((a, b) => b.pop - a.pop)
.attr('opacity', 0.75)
.attr('transform', d => `translate(${xScale(d.fertility)}, ${yScale(d.life_expect)})`)
.attr('fill', d => colorScale(d.cluster))
.attr('d', d => {
const symbol = d3.symbol().size(/* TODO */).type(/* TODO */);
return symbol()
});
gapminderScatterAxesLabel(svg, height, xScale, yScale, year);

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