I am working with react D3 charts, and I have created charts and it is working fine.
What I have done
I have several charts which are updating with in some time intervals, something like live data
So here to achieve this I out use effect and updating my charts every second, and my data in charts updates correctly.
I have given one tooltip on hover over the the bar, so that user can check the data for each bar or line.
Using below code to show the tooltip
.on("mousemove", function (event, d) {
// this whole code is when I hover that perticular bar
d3.select(this)
.transition()
.duration("50")
.attr("opacity", 0.6)
.attr("x", (a) => xScaleBars(a.timeline) - 3)
.attr("width", xScaleBars.bandwidth() + 6)
.style("filter", "url(#glow)");
div.transition().duration(50).style("opacity", 1);
div
.html(
`</text><text"></br> value : ${d.dataToShow}
<br/>
</text><text"></br> Month : ${d.month}
`
)
.style("left", event.pageX - 58 + "px")
.style("top", event.pageY - 140 + "px");
})
.on("mouseout", function (d, i) {
// this is when I move cursor out of that bar
d3.select(this)
.transition()
.duration("50")
.attr("width", xScaleBars.bandwidth())
.attr("x", (a) => xScaleBars(a.timeline))
.style("filter", "none")
.attr("opacity", "1");
div.transition().duration("50").style("opacity", 0);
})
Issue I am facing
The issue is when I hover over one chart component it shows the tooltip, and than when I hover over the other both shows at the same time.
What I am trying to do is to show the tooltip when I hover the one bar of any chart and than hide it,I tried below code
d3.select("svg").selectAll(".tooltipCHart").remove();
But it doesn't resolve my issue, I think I am missing some small part
here is my code sandbox which I tried
The problem is that you're creating a new tooltip div every time you re-render the chart.
A better approach is to have a hidden tooltip div in the beginning (in the render / return from your function component) and then just modify its contents and style (opacity: 1) on mouseover and mouseout. Keep track of the tooltip div using a ref.
See working codesandbox (I only modified chart1, you can make similar changes to chart2)
Related
I am working with D3 Charts, and by all work I have completed the chart and did some enhancement as well.
What I am doing
I have one function which shows tooltip, whenever I hover over any bar I am showing some info to the user.
My chart shows live data, so if any new data is coming it automatically creates a new bar.
SO when I hover over any bar then data is showing fine and perfect
My issue
So as I mentioned above in my case the data comes after some interval continuously, so I am getting data and adding it to previous data
The issue is when i Hover the last bar the tooltip shows fine, and when the new data comes so now two tooltips showing and it goes on and on
But in my coding I am writing code to remove the tooltip on mouseout, but still getting this issue
My code
This is what I am doing
.on("mousemove", function (event, d) {
// this whole code is when I hover that perticular bar
d3.select(this)
.transition()
.duration("50")
.attr("opacity", 0.6)
.attr("x", (a) => xScaleBars(a.timeline) - 3)
.attr("width", xScaleBars.bandwidth() + 6)
.style("filter", "url(#glow)");
div.transition().duration(50).style("opacity", 1);
div
.html(
`</text><text"></br> value : ${d.dataToShow}
<br/>
</text><text"></br> Month : ${d.month}
`
)
.style("left", event.pageX - 58 + "px")
.style("top", event.pageY - 140 + "px");
})
This above code is when I hove rover
Below is the code when I do mouseout
.on("mouseout", function (d, i) {
// this is when I move cursor out of that bar
d3.select(this)
.transition()
.duration("50")
.attr("width", xScaleBars.bandwidth())
.attr("x", (a) => xScaleBars(a.timeline))
.style("filter", "none")
.attr("opacity", "1");
div.transition().duration("50").style("opacity", 0);
})
I have tried display none as well as visibility property, but nothing works
I don't know what I am doing wrong
is My code right or wrong this also I am not getting properly
I have put all of my code in code sandbox Please have a look
PS: I am using use Effect with set Timeout for each 1 second to make it update every second
I have checked all the related suggestion by stack overflow, but none helped me out may be I am wrong but I did not found the solution.
Edit / Update
The answer suggested below is not working, if I have more than once chart
I used d3.selectAll(".tooltipCHart").remove();
So when there are two charts on hover of first chart's bar it shows tooltip, but when I hover over the other chart it is not showing
Please have a look here
here in above code sandbox in first chart it does not show tooltip but in second it is showing
Vivek, I have gone through the code and found out that most of the code written is fine. Besides that the reason of multiple tooltips is that you are rendering everything whenever data changes, so you are appending the tooltip div everytime, it means below code will execute multiple times which display multiple tooltips in case where our code did not perform mouseout execution. You can check this by inspecting elements.
var div = d3
.select("body")
.append("div")
.attr("class", "tooltipCHart")
.style("opacity", 0);
Simple solution will be to remove all the tooltips before appending a new one.
d3.selectAll(".tooltipCHart").remove();
It will resolve the issue of multiple tooltips.
But for your chart my confusion is whether is it a good idea to show tooltip or not as the data is always moving toward left so it will be difficult to keep the tooltip for long time.
I have created a stacked and grouped bar chart with React + d3. The first time it loads you can click on any legend and it will toggle the graph. However, once you hover over and the graph and the tooltip is displayed, then the tooltip overlaps with the legend and you cannot click on legend to toggle anymore. The attached image shows how it overlaps.
The full code demo is demo
Please can anyone help?
z-index does not work in your case. But what works is setting height to 0 on mouseout and setting height back to auto on mouseover.
.on("mouseover", function (d, i) {
tooltip.transition().duration(50).style("opacity", 1).style("height", "auto");
})
.on("mouseout", function (d, i) {
tooltip.transition().duration(500).style("opacity", 0).style("height", "0%");
});
I have a donut chart made using D3.js. I would like to change the color of the arc when hovering over the corresponding text.
I know how to change the color of the first, or all, but the corresponding one.
Here's the code so far. The lines that do the highlighting are the following:
.on("mouseover", function(d,i){
//d3.select(".donutArcSlices").transition().style("fill", "#007DBC");
//d3.selectAll(".donutArcSlices").transition().style("fill", "#007DBC");
div.transition()
.duration(200)
.style("opacity", .9);
div .html(d.name)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
d3.select(".donutArcSlices").transition().style("fill", "#3E4750");
div.transition()
.duration(500)
.style("opacity", 0);
});
If I add the first commented line, when I hover over any of the texts in the arcs, the first arc changes colors. If I remove the comments on the second line, then all of the arcs change colors upon hovering over any of the texts.
Give each path an unique ID:
.attr("id", function(d,i){ return "donut"+i})
And use it when hovering:
d3.select("#donut" + i).transition().style("fill", "#007DBC");
Here is your fiddle: https://jsfiddle.net/d6839s03/
PS: your mouseout function is making everything gray.
You can filter the correct path like this :
d3.selectAll(".donutArcSlices").filter(function(e, j){ return i === j}).style("fill", "#007DBC");
See https://jsfiddle.net/o98b8fsj/
Awesome answer for Gerardo. Anyone trying it with D3js v5 can use the same underlying concept.
I am using D3js V5 + Angular 6 with the following code snippets-
add id attribute to the node or any other element you want to manipulate -
this.svg.append('g').attr('id' ,(d,i)=>'elementname'+i)
then just make the changes on the d3js event of your choice -
.on('mousehover', (d,i)=> d3.select('#elementname'+i).style('fill','red')
.on('mouseout', (d,i)=> d3.select('#elementname'+i).style('fill','black')
I'm trying to determine that when I hover over a certain legend item, how do I know which one I hover over.
// draw legend colored rectangles
legend.append("rect")
.attr("x", width + 170)
.attr("width", 18)
.attr("height", 18)
.on("mouseover", function(d) {
})
.style("fill", color);
Right now, there are 3 rects in the legend. How do I get the id of the rect that I hover over?
Inside the mouseover handler, this is the DOM element that triggered the event. So you can do something like
.on("mouseover", function(d) {
d3.select(this).attr('id');// presumes that <rect> has an id!
})
To assign id to rect, you call .attr('id', 'some_id') on it.
But if you're at a stage where you don't already have ids on the rects (despite what you titled your post), then consider using d3's data binding and the enter, update, (exit) selections in order to create your legend, and use d in the "mouseover" function to determine which legend element is being interacted with (instead of using ids on the DOM element).
I can't seem to figure out how to update bar labels when I re-sort ranking data; essentially the label names will all remain the same, but their order will change.
Originally I have:
// University Names
labelsContainer = chart.append('g')
.attr('transform', 'translate(' + (uniLabel - barLabelPadding) + ',' + (gridLabelHeight + topMargin) + ')')
.selectAll('text')
.data(sortedData)
.enter()
.append('text')
.attr('x', xoffset)
.attr('y', yText)
.attr('stroke', 'none')
.attr('fill', 'black')
.attr("dy", ".35em") // vertical-align: middle
.attr('text-anchor', 'end')
.text(barLabel);
I sort the data differently, which I still call sortedData. The rectangles and rest of the graph updates successfully...save for the labels (which I have on only one rectangle bar column.)
In a new function I tried:
// update University Names (this overwrites, however... I want to select the existing label instead of appending text on top of the original text)
labelsContainer = chart.append('g')
.attr('transform', 'translate(' + (uniLabel - barLabelPadding) + ',' + (gridLabelHeight + topMargin) + ')')
.selectAll('text')
.data(sortedData)
.enter() // using transition ... or selecting the group ... does not allow the new text to appear!
.append('text')
.attr('x', xoffset)
.attr('y', yText)
.attr('stroke', 'none')
.attr('fill', 'black')
.attr("dy", ".35em") // vertical-align: middle
.attr('text-anchor', 'end')
.text(barLabel);
The issue here is that this just adds the new (correct) labels on top of the existing ones, instead of replacing them.
Using transition() I'm able to update the rest of the graph, but not the labels.
Any ideas of how to fix? Happy to provide more info/context if need be...
UPDATE 12/24: JSFiddle: http://jsfiddle.net/myhrvold/BVB2d/
JSFiddle showing transition, but with labels being overwritten: http://jsfiddle.net/myhrvold/BVB2d/embedded/result/
I know that by appending, I'm overwriting; but in attempting to replace, nothing happens and the original text remains, so the idea here is that I'm showing that I am at least generating the correct new labels and putting them in the right place...it's just that I'm not substituting them from my original labels...
You're completely repeating your code when you update your data -- including the chart.append('g') which creates a new group and then adds text labels to it. Because you've just created this as a new group, when you select inside it you can't select any of the labels you created the first time, so instead you end up creating all new labels.
To fix: first, as #musically_ut suggested, give each of your groups a unique class name. Then, in your update method select this group and the text elements it contains using chart.select(g.univerity-labels-container).selectAll("text"). However, you'll find you still have problems because you've got everything chained to an enter(); since you don't expect any new elements to be added when sorting, just take out that line. *
That should get it working, but the program is still painfully complex for what you're trying to do. For starters, all of this could work a lot better as an HTML table which would handle a lot of the layout for you. More importantly, you could save a lot of work if, instead of grouping elements by column you grouped them by row. That way, you would only have to join the data once, to the group, instead of doing separate data joins for each variable. If I have a chance in the next few days, I might try to write up a from-the-ground up explanations of how to approach this. In the meantime, google "d3 sortable table" for a couple examples, or look at the source code for this NYT graphic by Mike Bostock.
*For updating with an enter() step, I find most tutorials don't describe the update process very clearly, but I wrote up a step-by-step breakdown here yesterday.