I am new to D3.js and have created a graph that shows the sales history of some sellers.
In the table below shows the total sales result in the year, in the graph shows the monthly evolution of sales, when I hover under each circle I want to show the amount that sold in the respective month.
But it is always showing the same values (the values are being shown at the top of the chart).
//#region Criando Tabela de Vendedores
var newMapVendedores = [
{
"vendnm": "CHARNECA",
"Vendas_Ano": 236009.2299999998,
"Vendas_Ant": 282753.77999999997
},
{
"vendnm": "JOÃO LUIS",
"Vendas_Ano": 257733.04999999996,
"Vendas_Ant": 332119.31
},
]
$(document).ready(function () {
$("#tableVendedores").append('<tfoot><th></th><th></th><th></th></tfoot>');
var table = $('#tableVendedores').DataTable({
"data": newMapVendedores,
"columns": [
{ "data": "vendnm", title: 'Vendedor' },
{ "data": "Vendas_Ano", title: 'Vendas Ano' },
{ "data": "Vendas_Ant", title: 'Vendas Ant' },
],
"bLengthChange": false,
"bPaginate": false,
"bFilter": false,
"info": false,
});
$('#tableVendedores').on('click', 'tr', function () {
var data = table.row(this).data();
});
});
//#endregion
//#region GRÁFICO
var codes = ["VENDAS_ANO", "VENDAS_ANT"];
$('span.values').html(codes.join(', '));
modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;
var data = null;
var filtered_data = null;
var margin = { top: 30, right: 20, bottom: 50, left: 50 };
var width = 600 - margin.left - margin.right;
var height = 350 - margin.top - margin.bottom;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";
var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25";
var circleRadius = 3;
var circleRadiusHover = 6;
var month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var x = d3.scaleBand().range([0, width]).domain(month).padding(1);
var y = d3.scaleLinear().range([height, 0]).domain([0, 65000]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var xAxis = d3.axisBottom(x).ticks(12);
var yAxis = d3.axisLeft(y);
var svg = d3.select("#line-chart-container")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var g = null;
var line = d3.line()
.x(function (d) {
return x(d.month);
})
.y(function (d) {
return y(d.value);
});
// .curve(d3.curveBasis);
let lines = svg.append('g')
.attr('class', 'lines');
var mouseG = svg.append("g") // this the black vertical line to folow mouse
.attr("class", "mouse-over-effects");
mouseG.append("path")
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "0");
// get data
d3.queue()
.defer(d3.json, 'http://www.json-generator.com/api/json/get/cjsmAgXIVu?indent=2')
.await(makeLineChart);
function makeLineChart(error, data) {
if (error) {
console.log(error);
}
color.domain(d3.keys(data[0])
.filter(function (key) {
return key == "CODE";
})
);
createAxis();
updateChart(data);
// radio button change
d3.selectAll("input[name='modality-selector']")
.on("change", function () {
modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;
clearChart();
createAxis();
updateChart(data);
});
} // end makeLineChart function
/**
* Create (if is the firts time) or updates the line chart,
* based on radio button selection.
*/
function updateChart(data) {
// filter data
filtered_data = data.filter(function (d) {
return d.MODALITY == modalitySelected && codes.includes(d.CODE);
});
// first we need to corerce the data into the right formats
filtered_data = filtered_data.map(function (d) {
return {
code: d.CODE,
month: d.MONTH,
modality: d.MODALITY,
value: +d.VALUE
};
});
filtered_data = d3.nest()
.key(function (d) {
return d.code;
})
.entries(filtered_data);
var codesArray = svg.selectAll(".code")
.data(filtered_data, function (d) {
return d.key;
})
.enter()
.append("g")
.attr("class", "code")
.on("mouseover", function (d, i) {
codesArray.append("text")
.attr("class", "title-text")
.style("fill", color(d.key))
.text('')
.attr("text-anchor", "middle")
.attr("x", 200)
.attr("y", 20);
})
.on("mouseout", function (d) {
codesArray.select(".title-text").remove();
})
codesArray.append("path")
.attr("class", "line")
.attr("d", function (d) {
// console.log(d)
return line(d.values);
})
.style("stroke", function (d) {
return color(d.key);
})
.style('opacity', lineOpacity)
.on("mouseover", function (d) {
d3.selectAll('.line')
.style('opacity', otherLinesOpacityHover);
d3.selectAll('.circle')
.style('opacity', circleOpacityOnLineHover);
d3.select(this)
.style('opacity', lineOpacityHover)
.style("stroke-width", lineStrokeHover)
.style("cursor", "pointer");
})
.on("mouseout", function (d) {
d3.selectAll(".line")
.style('opacity', lineOpacity);
d3.selectAll('.circle')
.style('opacity', circleOpacity);
d3.select(this)
.style("stroke-width", lineStroke)
.style("cursor", "none");
});
/* Add circles in the line */
codesArray.selectAll("circle-group")
.data(filtered_data, function (d) {
return d.key;
})
.enter()
.append("g")
.style("fill", function (d, i) {
return color(d.key);
})
.on("mouseover", function (d, i) {
d3.select(this)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.text(d.values[i].value)
.attr("x", d => x(d.key) + 5)
.attr("y", d => y(d.value) + 10);
})
.on("mouseout", function (d) {
codesArray.select(".text").remove();
})
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.append("circle")
.attr("cx", function (d) {
return x(d.month)
})
.attr("cy", d => y(d.value))
.attr("r", circleRadius)
.style('opacity', circleOpacity)
.on("mouseover", function (d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadiusHover);
})
.on("mouseout", function (d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadius);
});
}
function createAxis() {
g = svg.append("g")
.attr("class", "chartGroup")
g.append("g")
.attr("class", "axis x")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis);
g.append("g")
.attr("class", "axis y")
.call(yAxis);
}
/**
* Remove old chart (axis and lines).
*/
function clearChart() {
d3.select(".chartGroup").remove();
d3.selectAll(".code").remove();
}
//#endregion
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script type="text/javascript" charset="utf8"
src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
<title>Index 9</title>
</head>
<style>
svg {
font-family: Sans-Serif, Arial;
}
.line {
stroke-width: 1.5;
fill: none;
}
.axis path {
stroke: black;
}
.text {
font-size: 12px;
}
.title-text {
font-size: 12px;
}
table {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
font-size: 12px !important;
margin-left: 10px;
margin-top: 10px;
/* width: 300px !important; */
}
.table td {
padding: .0rem;
}
tr {
line-height: 25px;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
th,
td {
border: 1px solid #cccccc;
padding: 0px;
text-align: center;
}
input {
visibility: hidden;
}
label {
cursor: pointer;
margin-bottom: .0rem;
}
hr {
margin-top: -5px;
border-width: 1px;
border-color: black;
}
</style>
<body>
<div style="width: 600px; margin-left: 10px;">
<div id="line-chart-container"></div>
<hr>
<div style="width: 600px;" id="modality-selector-container">
<form id="modality-selector">
<div class="row table">
<div class="col-6">
<h7 style="margin-left: 10px;">Sales</h7>
<table id="tableSales" class="table table-hover">
<thead>
<tr>
<th>Sale</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="radio" name="modality-selector" id="rb-charneca" value="charneca"
checked />
<label for="rb-charneca">CHARNECA</label>
</td>
<td>236.009,23</td>
<td>282.753,78</td>
</tr>
<tr>
<td>
<input type="radio" name="modality-selector" id="rb-joaoluis"
value="joaoluis" />
<label for="rb-joaoluis">JOÃO LUIS</label>
</td>
<td>257.733,05</td>
<td>332.119,31</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</div>
</div>
</div>
</body>
As you can see, hovering over circles always shows the same values. Can you help me fix this?
Thank you very much in advance.
You are attaching the mouseover
listener to the groups, hoping that the index i
could give you the circle:
.on("mouseover", function (d, i) {
d3.select(this)
.append("text")
.text(d.values[i].value)
//etc...
However, that i
is the index of the groups, not of the circles, and you have just two groups, with indices 0 and 1.
The simplest solution is attaching the listener to the circles themselves. Here is your code with that change:
//#region Criando Tabela de Vendedores
var newMapVendedores = [{
"vendnm": "CHARNECA",
"Vendas_Ano": 236009.2299999998,
"Vendas_Ant": 282753.77999999997
},
{
"vendnm": "JOÃO LUIS",
"Vendas_Ano": 257733.04999999996,
"Vendas_Ant": 332119.31
},
]
$(document).ready(function() {
$("#tableVendedores").append('<tfoot><th></th><th></th><th></th></tfoot>');
var table = $('#tableVendedores').DataTable({
"data": newMapVendedores,
"columns": [{
"data": "vendnm",
title: 'Vendedor'
},
{
"data": "Vendas_Ano",
title: 'Vendas Ano'
},
{
"data": "Vendas_Ant",
title: 'Vendas Ant'
},
],
"bLengthChange": false,
"bPaginate": false,
"bFilter": false,
"info": false,
});
$('#tableVendedores').on('click', 'tr', function() {
var data = table.row(this).data();
});
});
//#endregion
//#region GRÁFICO
var codes = ["VENDAS_ANO", "VENDAS_ANT"];
$('span.values').html(codes.join(', '));
modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;
var data = null;
var filtered_data = null;
var margin = {
top: 30,
right: 20,
bottom: 50,
left: 50
};
var width = 600 - margin.left - margin.right;
var height = 350 - margin.top - margin.bottom;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";
var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25";
var circleRadius = 3;
var circleRadiusHover = 6;
var month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var x = d3.scaleBand().range([0, width]).domain(month).padding(1);
var y = d3.scaleLinear().range([height, 0]).domain([0, 65000]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var xAxis = d3.axisBottom(x).ticks(12);
var yAxis = d3.axisLeft(y);
var svg = d3.select("#line-chart-container")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var g = null;
var line = d3.line()
.x(function(d) {
return x(d.month);
})
.y(function(d) {
return y(d.value);
});
// .curve(d3.curveBasis);
let lines = svg.append('g')
.attr('class', 'lines');
var mouseG = svg.append("g") // this the black vertical line to folow mouse
.attr("class", "mouse-over-effects");
mouseG.append("path")
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "0");
// get data
d3.queue()
.defer(d3.json, 'http://www.json-generator.com/api/json/get/cjsmAgXIVu?indent=2')
.await(makeLineChart);
function makeLineChart(error, data) {
if (error) {
console.log(error);
}
color.domain(d3.keys(data[0])
.filter(function(key) {
return key == "CODE";
})
);
createAxis();
updateChart(data);
// radio button change
d3.selectAll("input[name='modality-selector']")
.on("change", function() {
modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;
clearChart();
createAxis();
updateChart(data);
});
} // end makeLineChart function
/**
* Create (if is the firts time) or updates the line chart,
* based on radio button selection.
*/
function updateChart(data) {
// filter data
filtered_data = data.filter(function(d) {
return d.MODALITY == modalitySelected && codes.includes(d.CODE);
});
// first we need to corerce the data into the right formats
filtered_data = filtered_data.map(function(d) {
return {
code: d.CODE,
month: d.MONTH,
modality: d.MODALITY,
value: +d.VALUE
};
});
filtered_data = d3.nest()
.key(function(d) {
return d.code;
})
.entries(filtered_data);
var codesArray = svg.selectAll(".code")
.data(filtered_data, function(d) {
return d.key;
})
.enter()
.append("g")
.attr("class", "code")
.on("mouseover", function(d, i) {
codesArray.append("text")
.attr("class", "title-text")
.style("fill", color(d.key))
.text('')
.attr("text-anchor", "middle")
.attr("x", 200)
.attr("y", 20);
})
.on("mouseout", function(d) {
codesArray.select(".title-text").remove();
})
codesArray.append("path")
.attr("class", "line")
.attr("d", function(d) {
// console.log(d)
return line(d.values);
})
.style("stroke", function(d) {
return color(d.key);
})
.style('opacity', lineOpacity)
.on("mouseover", function(d) {
d3.selectAll('.line')
.style('opacity', otherLinesOpacityHover);
d3.selectAll('.circle')
.style('opacity', circleOpacityOnLineHover);
d3.select(this)
.style('opacity', lineOpacityHover)
.style("stroke-width", lineStrokeHover)
.style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.selectAll(".line")
.style('opacity', lineOpacity);
d3.selectAll('.circle')
.style('opacity', circleOpacity);
d3.select(this)
.style("stroke-width", lineStroke)
.style("cursor", "none");
});
/* Add circles in the line */
codesArray.selectAll("circle-group")
.data(filtered_data, function(d) {
return d.key;
})
.enter()
.append("g")
.style("fill", function(d, i) {
return color(d.key);
})
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.append("circle")
.attr("cx", function(d) {
return x(d.month)
})
.attr("cy", d => y(d.value))
.attr("r", circleRadius)
.style('opacity', circleOpacity)
.on("mouseover", function(d) {
d3.select(this.parentNode)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.text(d.value)
.attr("x", d => x(d.month) + 10)
.attr("y", d => y(d.value) - 10);
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadiusHover);
})
.on("mouseout", function(d) {
codesArray.select(".text").remove();
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadius);
});
}
function createAxis() {
g = svg.append("g")
.attr("class", "chartGroup")
g.append("g")
.attr("class", "axis x")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis);
g.append("g")
.attr("class", "axis y")
.call(yAxis);
}
/**
* Remove old chart (axis and lines).
*/
function clearChart() {
d3.select(".chartGroup").remove();
d3.selectAll(".code").remove();
}
//#endregion
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
<title>Index 9</title>
</head>
<style>
svg {
font-family: Sans-Serif, Arial;
}
.line {
stroke-width: 1.5;
fill: none;
}
.axis path {
stroke: black;
}
.text {
font-size: 12px;
}
.title-text {
font-size: 12px;
}
table {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
font-size: 12px !important;
margin-left: 10px;
margin-top: 10px;
/* width: 300px !important; */
}
.table td {
padding: .0rem;
}
tr {
line-height: 25px;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
th,
td {
border: 1px solid #cccccc;
padding: 0px;
text-align: center;
}
input {
visibility: hidden;
}
label {
cursor: pointer;
margin-bottom: .0rem;
}
hr {
margin-top: -5px;
border-width: 1px;
border-color: black;
}
</style>
<body>
<div style="width: 600px; margin-left: 10px;">
<div id="line-chart-container"></div>
<hr>
<div style="width: 600px;" id="modality-selector-container">
<form id="modality-selector">
<div class="row table">
<div class="col-6">
<h7 style="margin-left: 10px;">Sales</h7>
<table id="tableSales" class="table table-hover">
<thead>
<tr>
<th>Sale</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="radio" name="modality-selector" id="rb-charneca" value="charneca" checked />
<label for="rb-charneca">CHARNECA</label>
</td>
<td>236.009,23</td>
<td>282.753,78</td>
</tr>
<tr>
<td>
<input type="radio" name="modality-selector" id="rb-joaoluis" value="joaoluis" />
<label for="rb-joaoluis">JOÃO LUIS</label>
</td>
<td>257.733,05</td>
<td>332.119,31</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</div>
</div>
</div>
</body>
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments