I want to add a tooltip to my existing linechart. I tried it in multiple ways but I cant't get it to work
I would like to end up with a tooltip like this: https://d3-graph-gallery.com/graph/interactivity_tooltip.html
I tried it multiple ways and now have the following:
g.append("g")
.selectAll("dotDemand")
.data(dataLine.filter(d => d.valueLine))
.enter()
.append("circle")
.attr("cx", function(d) { return plotDate(d.date) } )
.attr("cy", function(d) { return y(d.valueLine) } )
.attr("r", 4)
.attr("fill", "#439DD2")
.attr("stroke-width", 0)
.on('mouseover', function (d, i) {
d3.select(this).transition()
.duration('100')
.attr("r", 10)
.attr("stroke", "#000")
.attr("stroke-width", 5);
div.transition()
.duration('50')
.style("opacity", 0);
div.html("$" d3.format(".2f")(d.date))
.style("left", plotDate(d.valueLine) 10 "px")
.style("top", y(d.date) "px");
})
.on('mouseout', function (d, i) {
d3.select(this).transition()
.duration('200')
.attr("r", 4)
.attr("fill", "#439DD2")
.attr("stroke-width", 0);
//makes div disappear
div.transition()
.duration('200')
.style("opacity", 0);
});
But I'm doing something wrong.
My data looks like this:
Date,value
5/31/2022,12
6/1/2022,23
6/2/2022,24
6/3/2022,26
6/4/2022,32
6/5/2022,32
6/6/2022,35
6/6/2022,32
6/7/2022,31
6/8/2022,30
6/8/2022,21
6/10/2022,23
6/11/2022,13
6/12/2022,13
6/13/2022,14
6/14/2022,
6/15/2022,
6/16/2022,
6/17/2022,
6/18/2022,
6/19/2022,
6/20/2022,
6/21/2022,
6/22/2022,
6/23/2022,
6/24/2022,
6/25/2022,
6/26/2022,
6/27/2022,
And my full d3 linechart component looks like this:
import * as d3 from 'd3';
import { useEffect, useState } from 'react';
import csvFile2 from '../../data/testData.csv';
const BasicLinechart = (props) => {
const [dataLine, setDataLine] = useState([]);
console.log(csvFile2)
const { width, height } = props;
useEffect(() => {
if (dataLine.length > 0) {
drawLineChart();
} else {
getCSVDataLine();
}
}, [dataLine]);
// Read in csv
const getCSVDataLine = async () => {
const tempData = [];
await d3.csv(
csvFile2,
function (d) {
tempData.push({
date: d3.timeParse("%m/%d/%Y")(d.date),
valueLine: Number(d.value),
});
}
);
setDataLine(tempData);
};
const drawLineChart = () => {
const parseDate = d3.timeParse("%m/%d/%Y"),
formatDay = d3.timeFormat("%d"),
formatMonth = d3.timeFormat("%b");
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// set margins to have enough space for the axis
var margin = { top: 20, left: 50 };
// Add X axis in date format: it's a double x-axis with month year
var xDate = d3.scaleTime()
.domain(d3.extent(dataLine, function(d) { return d.date; }))
.range([ 0, width ])
var xAxisMonth = d3.axisBottom()
.scale(xDate)
.ticks(d3.timeDay.every(1))
.tickFormat(formatMonth);
// Define 'div' for tooltips
var div = d3.select("svg")
.append("div") // declare the tooltip div
.attr("class", "tooltip") // apply the 'tooltip' class
.style("opacity", 0);
// create the chart area
const svg = d3.select('.svg-basic')
svg.selectAll("*").remove();
// set base for axes
var g = svg.append('g')
.attr('transform', 'translate(' margin.left ',' margin.top ')')
.attr("class", "legendOrdinal");
g.append("g")
.attr("transform", "translate(0," (height 40) ")")
.call(xAxisMonth)
.selectAll("text")
.style("font-size", "12px")
.attr("stroke", "#595959");
// styling of the axes (possible later in styling sheet)
g.selectAll("path")
.style("stroke", "#595959")
g.append("g")
.select(".domain")
.attr("stroke", "#595959")
.attr("stroke-width", "6")
.attr("opacity", ".6");
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(dataLine, function (d) { return Math.max(d.valueLine); })])
.range([height, 10]);
// scale x axis for month and day
var plotDate = d3.scaleTime()
.domain(d3.extent(dataLine, function (d) { return d.date; }))
.range([0, width])
// Styling y
g.append("g")
.call(d3.axisLeft(y))
.attr("stroke", "#595959")
.selectAll("text")
.style("font-size", "12px")
.attr("stroke", "#595959");
// set lines coordinates
const valueLinePlot = d3.line()
.x(function (d) { return plotDate(d.date) })
.y(function (d) { return y(d.valueLine) })
.curve(d3.curveNatural)
// Add the line for prediction
g.append("path")
.datum(dataLine)
.attr("fill", "none")
.attr("stroke", "#439DD2")
.attr("stroke-width", 2.5)
.attr("d", valueLinePlot)
g.append("g")
.selectAll("dotDemand")
.data(dataLine.filter(d => d.valueLine))
.enter()
.append("circle")
.attr("cx", function(d) { return plotDate(d.date) } )
.attr("cy", function(d) { return y(d.valueLine) } )
.attr("r", 4)
.attr("fill", "#439DD2")
.attr("stroke-width", 0)
.on('mouseover', function (d, i) {
d3.select(this).transition()
.duration('100')
.attr("r", 10)
.attr("stroke", "#000")
.attr("stroke-width", 5);
div.transition()
.duration('50')
.style("opacity", 0);
div.html("$" d3.format(".2f")(d.date))
.style("left", plotDate(d.valueLine) 10 "px")
.style("top", y(d.date) "px");
})
.on('mouseout', function (d, i) {
d3.select(this).transition()
.duration('200')
.attr("r", 4)
.attr("fill", "#439DD2")
.attr("stroke-width", 0);
//makes div disappear
div.transition()
.duration('200')
.style("opacity", 0);
});
// text label for the y axis
g.append("text")
.attr("x", '-11%' )
.attr("y", '-6%' )
.style("text-anchor", "right")
.style("transform", "rotate(-90deg)")
.text("Total Units (#)")
.attr("stroke", "#919191")
.selectAll("text")
.style("font-size", "10px")
// text label for the x axis
g.append("text")
.attr("x", '73%' )
.attr("y",'70%' )
.style("text-anchor", "right")
.text("Date")
.attr("stroke", "#919191")
.selectAll("text")
.style("font-size", "10px")
}
return (
<div >
<svg className="svg-basic" width="800px" height="600px" />
</div>
)
}
export default BasicLinechart;
I think that it might be something the referencing to svg-basic
/ svg
/g
or something, but I can't figure it out.
CodePudding user response:
you have to create one absolute container first
i saw your code you have defined tooltip two times, first remove those two and this
let tooltip = d3
.select("#tooltipContainer")
.style("position", "absolute")
.style("top", 0)
.style("left", 0)
.style("display", "none");
here is you want to show tooltip div when you hover the dots on line chart then try like this
g.append("g")
.selectAll("dotDemand")
.data(dataLine.filter(d => d.valueLine))
.enter()
.append("circle")
.attr("cx", function(d) { return plotDate(d.date) } )
.attr("cy", function(d) { return y(d.valueLine) } )
.attr("r", 4)
.attr("fill", "#439DD2")
.attr("stroke-width", 0)
.on('mouseover', function (d, i) {
d3.select(this).transition()
.duration('100').attr("r", 10).attr("stroke", "#000").attr("stroke-width", 5);
tooltip.transition().duration(0).style("display", "block");
tooltip.html("$" d3.format(".2f")(d.date))
.style("left", d.pageX 10 "px")
.style("top", d.pageY 10 "px");
})
.on("mouseout", (e) => {
d3.select(this).transition().attr("r", 4);
tooltip.transition().duration(0);
tooltip
.style("left", "0px")
.style("top", "0px")
.style("display", "none");
})
this the code you have to add but remember you are using the react right you have to predefine your jsx elements
Note- don't ever use append
method in react instead of append you have to use select , join
methods
if you use the append method you will get duplicated copy of graph each time when your useEffect and useState changes
i will provide my recent line graph live code to you if you need you can modify according you your needs
here is my codepan javascript code https://codepen.io/codingdarci/pen/ZERpNOG
or you want this in same code in react you can mention me msg i will paste it here