scatterPlot = {
const margin = { top: 60, right: 30, bottom: 60, left: 70 };
const width = 800 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;
const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
const g = svg
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
svg
.append("text")
.attr("x", (width + margin.left + margin.right) / 2)
.attr("y", margin.top / 2)
.attr("text-anchor", "middle")
.attr("font-size", "20px")
.attr("font-weight", "bold")
.text("Players by Value (EUR) vs The Age of Players");
const x = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.value_eur) * 1.1])
.range([0, width]);
const y = d3.scaleLinear().domain([15, 45]).range([height, 0]);
// set of colors
const color = d3.scaleOrdinal(d3.schemeCategory10);
// Add X axis with grid lines
g.append("g")
.attr("transform", `translate(0, ${height})`)
.call(d3.axisBottom(x).ticks(10).tickSizeOuter(0))
.selectAll("line")
.attr("stroke", "#ccc");
// Add X axis label (centered)
g.append("text")
.attr("text-anchor", "middle")
.attr("x", width / 2)
.attr("y", height + margin.bottom - 15)
.attr("class", "axis-label")
.text("Value (EUR)");
// Add Y axis with grid lines
g.append("g")
.call(d3.axisLeft(y).ticks(10).tickSizeOuter(0))
.selectAll("line")
.attr("stroke", "#ccc");
// Add Y axis label (centered)
g.append("text")
.attr("text-anchor", "middle")
.attr("x", -height / 2)
.attr("y", -margin.left + 20)
.attr("transform", "rotate(-90)")
.attr("class", "axis-label")
.text("Age");
// Add tooltip
const tooltip = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("background", "lightsteelblue")
.style("padding", "5px")
.style("border-radius", "5px")
.style("pointer-events", "none");
// dots to scatter plot
g.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", (d) => x(d.value_eur))
.attr("cy", (d) => y(d.age))
.attr("r", 4) // radius for visibility
.style("fill", (d) => color(d.age))
.style("opacity", 0.7)
.attr("stroke", "black")
.on("mouseover", (event, d) => {
tooltip.transition().duration(200).style("opacity", 0.9);
tooltip
.html(
`Name: ${d.short_name}<br>Age: ${d.age}<br>Value: €${d.value_eur}`
)
.style("left", event.pageX + 5 + "px")
.style("top", event.pageY - 28 + "px");
})
.on("mouseout", () => {
tooltip.transition().duration(500).style("opacity", 0);
});
// legend for age colors
const legend = svg
.append("g")
.attr("transform", `translate(${width - 100}, ${margin.top})`);
const ageCategories = [15, 20, 25, 30, 35, 40, 45];
ageCategories.forEach((age, i) => {
legend
.append("rect")
.attr("x", 0)
.attr("y", i * 20)
.attr("width", 10)
.attr("height", 10)
.style("fill", color(age));
legend
.append("text")
.attr("x", 20)
.attr("y", i * 20 + 9)
.text(`Age: ${age}`)
.attr("alignment-baseline", "middle")
.style("font-size", "12px");
});
return svg.node();
}