Public
Edited
Sep 27, 2023
Insert cell
Insert cell
{
const svg = d3
.create("svg")
.attr("width", w)
.attr("height", h);

//Create bars
svg
.selectAll("rect")
.data(dataset)
.join("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => h - yScale(d[1]))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d[1]))
.attr("fill", d => color(d[1]))
.on("click", () => sortBars(svg));

//Create labels
svg
.selectAll("text")
.data(dataset)
.join("text")
.text(d => d[0])
.attr("text-anchor", "end")
.attr('transform', (d, i) => {
return (
'translate( ' +
(xScale(i) + xScale.bandwidth() / 2) +
' , ' +
(h - yScale(d[1]) + 5) +
'),' +
'rotate(-90)'
);
})
.attr("x", "0") //(d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("dy", "0.25em")
.attr("y", "0") //d => h - yScale(d[1]) + 14)
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", d => (d[1] > 2 ? "white" : "black"));

//Define sort order flag
let sortOrder = false;

//Define sort function
const sortBars = svg => {
//Flip value of sortOrder
sortOrder = !sortOrder;

svg
.selectAll("rect")
.sort((a, b) =>
sortOrder ? d3.ascending(a[1], b[1]) : d3.descending(a[1], b[1])
)
.transition()
.delay((d, i) => i * 50)
.duration(1000)
.attr("x", (d, i) => xScale(i));

svg
.selectAll("text")
.sort((a, b) =>
sortOrder ? d3.ascending(a[1], b[1]) : d3.descending(a[1], b[1])
)
.transition()
.delay((d, i) => i * 50)
.duration(1000)
.text(d => d[0])
.attr("text-anchor", "end")
.attr('transform', (d, i) => {
return (
'translate( ' +
(xScale(i) + xScale.bandwidth() / 2) +
' , ' +
(h - yScale(d[1]) + 5) +
'),' +
'rotate(-90)'
);
})
.attr("x", "0") //(d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("dy", "0.25em")
.attr("y", "0");
};

return svg.node();
}
Insert cell
dataset = fakeMembersGrouped //fakeMembersGroupedAndSorted
Insert cell
fakeMembersGroupedAndSorted = new Map(
[...fakeMembersGrouped].sort(
(a, b) => (a[1] < b[1] && 1) || (a[1] === b[1] ? 0 : -1)
)
)
Insert cell
fakeMembersGrouped = d3.rollup(
fakeMembers,
v => v.length, // aggregate the data by counting the number of people/records
d => d.location.state // group by state
)
Insert cell
fakeMembers = getFakeMembers(100).then(
(members) => members,
(err) => new Error("cannot load members from randomuser.me")
)
Insert cell
getFakeMembers = count =>
new Promise((resolves, rejects) => {
const api = `https://api.randomuser.me/?nat=US&results=${count}`;
const request = new XMLHttpRequest();
request.open('GET', api);
request.onload = () =>
request.status === 200
? resolves(JSON.parse(request.response).results)
: rejects(Error(request.statusText));
request.onerror = err => rejects(err);
request.send();
})
Insert cell
xScale = d3
.scaleBand()
.domain(d3.range(Array.from(fakeMembersGrouped.keys()).length))
.rangeRound([0, w])
.paddingInner(0.05)
Insert cell
yScale = d3
.scaleLinear()
.domain([0, d3.max(fakeMembersGrouped.values())])
.range([0, h])
Insert cell
color = d3
.scaleSequential()
.domain([0, d3.max(fakeMembersGrouped, d => d[1])])
.interpolator(d3.interpolateBlues)
Insert cell
w = 700
Insert cell
h = 400
Insert cell
padding = 100
Insert cell
margin = ({ top: 30, right: 0, bottom: 30, left: 40 })
Insert cell
d3 = require("d3@6")
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