Let's assume I have the following data:
Year Teams Matches Goals
2014 32 64 171
2010 32 64 125
2006 32 64 147
2002 32 64 161
1998 32 64 171
1994 24 52 141
I'm trying to create a line plot with data from either Teams
, Matches
, or Goals
(based on which of these the user selects from a drop-down menu).
Here is my code:
index.html
file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"
integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N"
crossorigin="anonymous">
<title>Line Chart</title>
</head>
<body>
<div class="row">
<div class="row">
<div id="line-plot-user-selection">
<div class="form-group">
<select class="form-control" id="metric-type" onchange="updateMetric()">
<option value="Teams">Number of Teams</option>
<option value="Matches">Number of Matches</option>
<option value="Goals" selected>Number of Goals</option>
</select>
</div>
</div>
</div>
<div id="line-plot-div">
</div>
</div>
</body>
</html>
In main.js
:
let promises = [
d3.csv('data/world-cup-data.csv', d3.autoType),
];
Promise.all(promises)
.then(function (data) {
createVis(data)
})
.catch(function (err) {
console.log(err)
});
function createVis(data) {
let lineChartData = data[0]
// Instantiate the visualizations
lineChart = new LineChart('line-plot-div', lineChartData, 'Test Title');
}
let metricType = document.getElementById('metric-type').value;
function updateMetric() {
metricType = document.getElementById('metric-type').value;
lineChart.wrangleData();
}
Then, in lineChart.js
:
class LineChart {
constructor(parentElement, data, chartTitle) {
this.parentElement = parentElement;
this.data = data;
this.chartTitle = chartTitle;
this.initVis()
}
initVis() {
let vis = this;
vis.metricType = 'Teams';
vis.margin = {top: 80, right: 90, bottom: 90, left: 80};
vis.width = 800 - vis.margin.left - vis.margin.right;
vis.height = 700 - vis.margin.top - vis.margin.bottom;
// initialize the drawing area
vis.svg = d3.select('#' + vis.parentElement).append('svg')
.attr('width', vis.width + vis.margin.left + vis.margin.right)
.attr('height', vis.height + vis.margin.top + vis.margin.bottom)
.append('g')
.attr('transform', `translate (${vis.margin.left}, ${vis.margin.top})`);
// scales and axes
vis.xScale = d3.scaleLinear()
.range( [ 0, vis.width ] );
vis.yScale = d3.scaleLinear()
.range( [ vis.height, 0 ] );
vis.xAxis = d3.axisBottom()
.scale(vis.xScale);
vis.yAxis = d3.axisLeft()
.scale(vis.yScale);
// add chart title
vis.svg.append('g')
.attr('class', 'title line-chart-title')
.append('text')
.text(vis.chartTitle)
.attr('transform', `translate(${vis.width / 2 + 45}, -30)`)
.attr('text-anchor', 'middle');
// create the axis groups
vis.xAxisGroup = vis.svg.append('g')
.attr('class', 'x-axis axis')
.attr('transform', 'translate(0, ' + vis.height + ')');
vis.yAxisGroup = vis.svg.append('g')
.attr('class', 'y-axis axis');
// create axis labels
// vertical axis
vis.svg.append('text')
.attr('x', -65)
.attr('y', -20)
.text('Count')
.style('font-size', 20)
.style('font-weight', 600)
.style('fill', 'blue');
// horizontal axis
vis.svg.append('text')
.attr('x', vis.width - 35)
.attr('y', vis.height + 50)
.text('Year')
.style('font-size', 20)
.style('font-weight', 600)
.style('fill', 'blue');
this.wrangleData()
}
wrangleData() {
let vis = this;
let filteredData = []
vis.metricType = metricType;
filteredData = vis.data
vis.updateVis()
}
updateVis() {
let vis = this;
// update the domains
vis.xScale.domain([ d3.min(vis.data, function(d) {return d.Year; }), d3.max(vis.data, function(d) {return d.Year }) ]);
vis.yScale.domain( [ 0, d3.max(vis.data, function(d) {return d[vis.metricType]; }) ] );
// draw the line
vis.line = vis.svg.selectAll('.line')
.data(vis.data)
vis.line.exit().remove();
// draw the line
vis.line
.enter()
.append('path')
.attr('class', 'line')
.merge(vis.line)
.datum(vis.data)
.transition()
.duration(500)
.attr('d', d3.line()
.x(function (d) { return vis.xScale(d.Year); })
.y(function (d) { return vis.yScale(d[vis.metricType]); })
.curve(d3.curveMonotoneX)
);
// add the axes
vis.xAxisGroup
.transition()
.duration(500)
.style('font-size', '15px')
.style('color', 'blue')
.call(d3.axisBottom((vis.xScale)).tickFormat(d3.format('')))
.selectAll('text')
.attr('y', 20)
.attr('x', 0)
.attr('dy', '.35em')
;
vis.yAxisGroup
.transition()
.duration(500)
.style('font-size', '15px')
.style('color', 'blue')
.call(d3.axisLeft(vis.yScale));
}
}
Currently, the line chart for Teams
is displaying properly. But, when I select Matches
or Goals
from the drop-down menu, the line does not update. It remains static and only displays the data for Teams
.
I believe the problem is in wrangleData()
. It's not grabbing the user's section and applying the filter.
Can someone help me understand how to fix my code so that the line draws when Matches
or Goals
is selected by the user?
Thanks in advance!
I have just seen your question
i'm a bit confusing why are you making so complicated by using class and constructor for updating graph.. ok anywhay
so i have just made a simple functional graph with dropdown you can see on this live code but i did not used your core graph code, i made the simple functional graph with all futures like tooltip
gridlines
and transition
etc If you dont mind you can chage the design according to your needs. that i'm pasting the codepan source code link too
here is the graph
const convertRemToPixels = (rem) => { return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); }; const dt = [ { year: "2014", value: 35 }, { year: "2010", value: 32 }, { year: "2006", value: 32 }, { year: "2002", value: 34 }, { year: "1998", value: 25 }, { year: "1994", value: 32 }, ]; const margin = { top: 10, right: 30, bottom: 30, left: 60 }, width = 600 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; // append the svg object to the body of the page const update = (data) => { const svg = d3.select("#mainChart").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).select("#mainGroup").attr("transform", `translate(${margin.left},${margin.top})`); let tooltip = d3.select("#tooltipContainer").style("position", "absolute").style("top", 0).style("left", 0).style("display", "none"); // grid function function make_y_gridlines() { return d3.axisLeft(y).ticks(6); } // Add X axis --> it is a date format const x = d3.scalePoint().range([0, width]); const y = d3.scaleLinear().domain([0, d3.max(data.map((e) => parseInt(e.value)))]).range([height, 0]); var xAxis = d3.axisBottom(x); var yAxis = d3.axisLeft(y).ticks(6); x.domain( data.map(function (d) { console.log(d); return d.year; }) ).padding([0.2]); svg.select("#XAxis").transition().attr("transform", "translate(0," + height + ")").call(xAxis).style("color", "#a4a4a4"); svg.select("#XAxis").call(xAxis).selectAll("text").style("text-anchor", "end").attr("dx", ".6em").attr("dy", "1em").style("font-size", ".8rem").attr("transform", "rotate(0)").style("font-family", '"Roboto", sans-serif'); // Add Y axis svg.select("#YAxis").transition().call(yAxis).style("color", "#a4a4a4").style("font-size", ".7rem"); svg.select("#linePath").datum(data).attr("fill", "none").attr("stroke", "#14c884").transition().attr( "d", d3.line().x(function (d) { console.log(d); return x(d.year); }).y(function (d) { return y(d.value); }).curve(d3.curveCatmullRom.alpha(1.1)) ); const circle = d3.select("#LineDots"); circle.selectAll(".point").data(data).join("circle").on("mouseover", (e, i) => { d3.select(e.target).transition().attr("r", 4); tooltip.transition().duration(0).style("display", "block"); tooltip.html(`<div>${i.year}: <span>${i.value}</span></div>`).style("left", e.pageX + convertRemToPixels(-1.6) + "px").style("top", e.pageY - convertRemToPixels(2) + "px"); }).on("mouseout", (e) => { d3.select(e.target).transition().attr("r", 2); tooltip.transition().duration(0); tooltip.style("left", "0px").style("top", "0px").style("display", "none"); }).transition().attr("class", "point").attr("stroke", "#14c884").attr("fill", function (d, i) { return "#14c884"; }).attr("cx", function (d, i) { return x(d.year); }).attr("cy", function (d, i) { return y(d.value); }).attr("r", function (d, i) { return 2; }).style("opacity", 1); d3.select("#Grid").transition().call(make_y_gridlines().tickSize(-width).tickFormat("")).attr("id", "gridSystem"); }; update(dt); const selectGroupFromDropDown = document.getElementById("metric-type"); selectGroupFromDropDown.addEventListener("change", (e) => { if(e.target.value === "Matches"){ data = [ { year: "2014", value: 64 }, { year: "2010", value: 64 }, { year: "2006", value: 64 }, { year: "2002", value: 64 }, { year: "1998", value: 64 }, { year: "1994", value: 52 }, ]; update(data); }else if(e.target.value === "Goals"){ data = [ { year: "2014", value: 171 }, { year: "2010", value: 125 }, { year: "2006", value: 149 }, { year: "2002", value: 161 }, { year: "1998", value: 171 }, { year: "1994", value: 141 }, ]; update(data); }else if(e.target.value === "Teams"){ const dt = [ { year: "2014", value: 35 }, { year: "2010", value: 32 }, { year: "2006", value: 32 }, { year: "2002", value: 34 }, { year: "1998", value: 25 }, { year: "1994", value: 32 }, ]; update(dt) } });
#gridSystem line{ stroke: lightgrey; stroke-opacity: 0.7; shape-rendering: crispEdges; stroke-dasharray: 2 2; stroke-width: .05rem; } #gridSystem path { stroke-width: 0; } /* tooltip */.barTitle{ text-align: center; font-weight: bolder; padding: .2em 0; font-size: .8rem; color: black; }.tooltipContainer div span{ color: #536876; font-weight: bold; }.bar{ border-top-left-radius: 1em;important: } #tooltipContainer { line-height. 1;1: font-weight; bold: padding. .6em 1em;6em 1em: background;white: color; #9cb3c3: border-radius. ;4em: font-weight; 600: box-shadow. 0em 0em,5em rgb(165, 163; 163): font-size. ;6rem: font-family, 'Roboto'; sans-serif; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js"></script> <select class="form-control" id="metric-type"> <option value="Teams">Number ofTeams</option> <option value="Matches">Number of Matches</option> <option value="Goals" selected>Number of Goals</option> </select> <div id="tooltipContainer"></div> <svg id="mainChart"> <g id="mainGroup"> <g id="XAxis"></g> <g id="YAxis"></g> <g id="Grid"></g> <path id="linePath"></path> <g id="LineDots"></g> </g> </svg>
and here is the same source code from codepan
https://codepen.io/codingdarci/pen/LYrByKG
i hope this would solve your issue
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.