chart = {
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
svg.append('g')
.attr('transform', `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(x))
.append('text')
.attr('text-anchor', 'end')
.attr('fill', 'black')
.attr('font-size', '12px')
.attr('font-weight', 'bold')
.attr('x', width - margin.right)
.attr('y', -10)
.text('Gross Domestic Product (GDP) per capita');
svg.append('g')
.attr('transform', `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y))
.append('text')
.attr('transform', `translate(20, ${margin.top}) rotate(-90)`)
.attr('text-anchor', 'end')
.attr('fill', 'black')
.attr('font-size', '12px')
.attr('font-weight', 'bold')
.text('Life Expectancy');
const yearLabel = svg.append('text')
.attr('class', 'year')
.attr('x', 40)
.attr('y', height - margin.bottom - 20)
.attr('fill', '#ccc')
.attr('font-family', 'Helvetica Neue, Arial')
.attr('font-weight', 500)
.attr('font-size', 80)
.text(years[0]); //<-- set as initial year
//create bubbles
const countries = svg
.selectAll('circle')
.data(initData) // <-- keep as initData
.join('circle')
.sort((a, b) => b.pop - a.pop) // <-- sort so smaller circles are drawn last
.attr('class', 'country')
.attr('opacity', 0.75)
.attr('fill', d => color(d.continent)) //apply color scale
.attr('cx', d => x(d.gdpPercap))
.attr('cy', d => y(d.lifeExp))
.attr('r', d => size(d.pop)); //apply size scale
// add a tooltip with country name
const tooltip = d3.select("body").append('div')
.attr('class', 'tooltip')
.style("opacity", 0)
.style("position", "absolute")
.style("background-color", "black")
.style("border-radius", "5px")
.style("padding", "10px")
.style("color", "white")
//event to make tooltip follow mouse - comment out if using fixed tooltip
window.onmousemove = function (e) {
const x = (e.clientX + 10) + 'px', y = (e.clientY - 10) + 'px';
tooltip.style("left", x).style("top", y);
};
// Add mouse hover interactions, using D3 to update attributes directly.
// In a stand-alone context, we could also use stylesheets with 'circle:hover'.
countries
// The 'on()' method registers an event listener function
.on('mouseover', function(event,d){
d3.select(this).attr('stroke', '#333').attr('stroke-width', 2);
tooltip
.transition()
.duration(200)
.style("opacity", 1);
tooltip.html("Country: " + d.country)
})
.on('mouseout', function(){
// Setting the stroke color to null removes it entirely.
d3.select(this).attr('stroke', null);
tooltip
.transition()
.duration(200)
.style("opacity", 0)
});
// Update function: given a year value, update the chart.
function setYear(year) {
// Update the year label by simply setting it to the new value.
yearLabel.text(year);
// Update countries and animate the transition:
// 1. Change the data to filter to the given year, keyed by country
// 2. Re-sort elements to ensure smallest remain on top, as pop values may have changed
// 3. Update position and radius, interpolated across a 1 sec (1000ms) animation
countries
.data(gapminder.filter(d => d.year === year), d => d.country) // <-- filter data by year and update bubble for each country transition
.sort((a, b) => b.pop - a.pop)
.transition() // <-- akin to a D3 selection, but interpolates values
.duration(2000) // <-- 1000 ms === 1 sec
.ease(d3.easeExpOut) // <-- sets pacing; cubic is the default, try some others!
.attr('cx', d => x(d.gdpPercap))
.attr('cy', d => y(d.lifeExp))
.attr('r', d => size(d.pop));
}
//return object containing node and setYear function
return Object.assign( svg.node(), {setYear} );
}