Public
Edited
May 3
Insert cell
Insert cell
Insert cell
data = FileAttachment("happiness_data@1.csv").csv()

Insert cell
data
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
viz = d3.select(viz_container).select("#happiness")
Insert cell
vizWidth = Number(viz.attr("width"))
Insert cell
vizHeight = Number(viz.attr("height"))
Insert cell
tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "1px")
.style("border-radius", "5px")
.style("padding", "10px")
.style("pointer-events", "none");
Insert cell
padding = 20
Insert cell
shuffledData = [...data].sort(() => Math.random() - 0.5);
Insert cell
circles = viz.selectAll("circle")
.data(shuffledData)
.join("circle")
.attr("cx", (d, i) => 40 + i * (30 * 2 + padding))
.attr("cy", 250)
.attr("r", 30)
.attr("class", d => d["Country name"])
.attr("stroke", "white")
.attr("stroke-width", 1.5)
.attr("fill", d => colorScale(+d.Perception))

.on("mouseover", function(event, d) {
d3.select(this).attr("stroke", "black").attr("stroke-width", 2.5);
tooltip.style("visibility", "visible")
.html(`Country: ${d["Country name"]}`);
})
.on("mousemove", function(event) {
tooltip.style("top", (event.pageY - 10) + "px").style("left", (event.pageX + 10) + "px");
})
.on("mouseout", function() {
d3.select(this).attr("stroke", "white").attr("stroke-width", 1.5);
tooltip.style("visibility", "hidden");
})
.call(drag); // Attach the drag behavior
Insert cell
drag = {

function dragstarted() {
d3.select(this).attr("stroke", "black");
}

function dragged(event, d) {
d3.select(this).raise().attr("cx", event.x).attr("cy", event.y);
}

function dragended() {
d3.select(this).attr("stroke", null);
}

return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
Insert cell
colorScale = d3.scaleSequential(d3.interpolateBlues)
.domain(d3.extent(data, d => +d.Perception));
Insert cell
revealBtn = d3.select("#revealBtn")
Insert cell
revealBtn.on("click", function() {
// Select all the circles
const circles = viz.selectAll("circle");

// Update the position of each circle based on the ranked data
circles.transition()
.duration(1000) // Set the duration of the transition in milliseconds
.attr("cx", (d) => {
// Find the index of the current data item in the ranked data array
const rankIndex = data.findIndex(item => item["Country name"] === d["Country name"]);
// Calculate the new cx position based on the index
return 40 + rankIndex * (30 * 2 + padding);
})
.attr("cy", 250)
});
Insert cell
resetBtn = d3.select("#resetBtn");
Insert cell
resetBtn.on("click", function() {
const shuffledData = [...data].sort(() => Math.random() - 0.5);

viz.selectAll("circle")
.data(shuffledData, d => d["Country name"]) // Use a key function for object constancy
.transition()
.duration(1000)
.attr("cx", (d, i) => 40 + i * (30 * 2 + padding))
.attr("cy", 250)

// Re-attach drag behavior if it was removed
viz.selectAll("circle").call(drag);
});
Insert cell
d3.select("#btn-ss").on("click", function() {
sortByFactor("Explained by: Social support");
});
Insert cell
d3.select("#btn-hle").on("click", function() {
sortByFactor("Explained by: Healthy life expectancy");
});
Insert cell
function sortByFactor(factor) {
const sortedData = [...data].sort((a, b) => +b[factor] - +a[factor]); // Sort descending by factor

viz.selectAll("circle")
.data(sortedData, d => d["Country name"]) // Use a key function
.transition()
.duration(1000)
.attr("cx", (d, i) => 40 + i * (30 * 2 + padding))
.attr("cy", 250)

// Re-attach drag behavior if needed
viz.selectAll("circle").call(drag);
}
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