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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more