I have a D3 line chart in my application and on hovering on the line a text is shown above the line displaying the line name. After that I had implemented a legend alongside with the chart and legend allows making lines appear and disappear upon clicking on legend item. My Sample code is given below. But now it has caused a problem like this. As now I'm setting the opacity of the line which was selected from legend to zero, the line gets disappeared. But if I hover in the area where the line was earlier still it shows the on hover text. Does anyone know a solution to avoid this problem.
You can see this issue by clicking on both items in the legend, Then all lines get disappeared. But if you move the mouse over the chart, where the lines were earlier, you will see that text will show on hovering
Example : https://jsfiddle.net/yasirunilan/ymavtsbj/4/
var data = [{
name: "USA",
values: [{
date: "2000",
price: "100"
},
{
date: "2001",
price: "110"
},
{
date: "2002",
price: "145"
},
{
date: "2003",
price: "241"
},
{
date: "2004",
price: "101"
},
{
date: "2005",
price: "90"
},
{
date: "2006",
price: "10"
},
{
date: "2007",
price: "35"
},
{
date: "2008",
price: "21"
},
{
date: "2009",
price: "201"
}
]
},
{
name: "Canada",
values: [{
date: "2000",
price: "100"
},
{
date: "2001",
price: "110"
},
{
date: "2002",
price: "145"
},
{
date: "2003",
price: "241"
},
{
date: "2004",
price: "101"
},
{
date: "2005",
price: "90"
},
{
date: "2006",
price: "10"
},
{
date: "2007",
price: "35"
},
{
date: "2008",
price: "21"
},
{
date: "2009",
price: "201"
}
]
}
];
var width = 500;
var height = 300;
var margin = 50;
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;
/* Format Data */
var parseDate = d3.timeParse("%Y");
data.forEach(function(d) {
d.values.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
});
/* Scale */
var xScale = d3.scaleTime()
.domain(d3.extent(data[0].values, d => d.date))
.range([0, width - margin]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data[0].values, d => d.price)])
.range([height - margin, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
/* Add SVG */
var svg = d3.select("#chart").append("svg")
.attr("width", (width + margin) + "px")
.attr("height", (height + margin) + "px")
.append('g')
.attr("transform", `translate(${margin}, ${margin})`);
/* Add line into SVG */
var line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.price));
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('id', d => d.name.replace(/\s+/g, '') + "-line")
.attr('class', 'line-group')
.on("mouseover", function(d, i) {
svg.append("text")
.attr("class", "title-text")
.attr("font-weight", "bold")
.style("fill", color(i))
.text(d.name)
.attr("text-anchor", "middle")
.attr("x", (width - margin) / 2)
.attr("y", -30);
})
.on("mouseout", function(d) {
svg.select(".title-text").remove();
})
.append('path')
.attr('class', 'line')
.attr('d', d => line(d.values))
.style('stroke', d => color(d.name))
.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 */
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.attr('id', d => d.name.replace(/\s+/g, '') + "-circle")
.style("fill", d => color(d.name))
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.on("mouseover", function(d) {
d3.select(this)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.text(`${d.price}`)
.attr("x", d => xScale(d.date) + 5)
.attr("y", d => yScale(d.price) - 10);
})
.on("mouseout", function(d) {
d3.select(this)
.style("cursor", "none")
.transition()
.duration(duration)
.selectAll(".text").remove();
})
.append("circle")
.attr("cx", d => xScale(d.date))
.attr("cy", d => yScale(d.price))
.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);
});
/* Add Axis into SVG */
var xAxis = d3.axisBottom(xScale).ticks(5);
var yAxis = d3.axisLeft(yScale).ticks(5);
svg.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${height-margin})`)
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append('text')
.attr("y", 15)
.attr("transform", "rotate(-90)")
.attr("fill", "#000")
.text("Total values");
var keys = []
// Add the Legend
var legend = d3.select("body").selectAll(".legend")
.data(data.map(d => d.name))
.enter().append("font")
.attr("class", "legend") // style the legend
.style("color", color)
.style("margin-left", 10 + "px")
.style("padding-left", 10 + "px")
.html(d => d)
d3.selectAll(".legend")
.on("click", function(d) {
keys.includes(d) ?
keys.splice(keys.indexOf(d), 1) :
keys.push(d)
d3.select(this).style("opacity", () => keys.includes(d) ? .5 : 1)
d3.select("#" + d.replace(/\s+/g, '') + "-line")
.transition().duration(100)
.style("opacity", () => keys.includes(d) ? 0 : 1);
d3.select("#" + d.replace(/\s+/g, '') + "-circle")
.transition().duration(100)
.style("opacity", () => keys.includes(d) ? 0 : 1);
})