Detecting click location - javascript

I'm attempting to display something (ultimately a menu) when a point is clicked in a DevExtreme chart. I've started by using a bar chart for simplicity.
What I want to do is, when the user clicks on a bar to display something else in the DOM at that particular point. I've tried to set this up and got most of the way, the problem I've got that I'm not sure how to solve is regarding the co-ordinates.
The example above shows where I clicked, and the red circle that I've appended which appears at the top of the bar. The code to add this is quite simple:
var clicked = function(p) {
var element = p.element[0];
var group = d3.select(element)
.select("svg")
.append("g")
.attr("transform", "translate("+ [p.target.x, p.target.y] +")")
.append("circle")
.attr({ cx : 0, cy: 0, r: 10, class: "circle"});
};
Simply taking the co-ordinates of the target clicked element. Obviously this seems to be the top corner. Is there any way that anyone can think of to obtain the actual clicked location?
I've got a demonstration fiddle forked off one of their examples here: http://jsfiddle.net/IPWright83/ho2euurh/2/

Try this fiddle:
.attr("transform", "translate("+ [p.jQueryEvent.pageX, p.jQueryEvent.pageY] +")")
This gives you the coordinates of the clicked location.

Related

Multiple tooltips not showing correctly in react

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)

Mouse out event not working properly in JavaScript D3

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.

Trying to analyse why this transition is not happening

I have the following adapted d3.js visual and I'm unable to work out why the transition does not fire. The ars should rotate around to different sizes when the radio button is clicked.
It seems that clicking the radio button is changing the titles of the arc (if I hover the cursor over each)
Is this section of code to blame?
// check if svg group already exists
var svg = d3.select("#sunGroup");
if (svg.empty()) {
var svg = d3.select("#sunBurst")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr({
'transform': "translate(" + width / 2 + "," + height * .52 + ")",
id: "sunGroup"
});
}
Here is the full (not) working example:
https://plnkr.co/edit/hpvREU?p=preview
This is the transition I'm trying to hold onto: plnkr.co/edit/NnQUAp?p=preview
What I trying to do is move the logic at line 128 (starting d3.selectAll("input").on("change", function change() {...) out of this function
An easy fix to your problem is to remove all children of the SVG whenever you switch data types:
d3.select("#sunBurst").selectAll('*').remove()
That way, you are binding the new data to new elements. You can also remove the svg.empty() and d3.select('#sunGroup') code. This simple fix lets you switch between pie charts, and is in the spirit of the code you currently have. Here's the users pie chart.
However, this may not be the best way to do what you're trying to achieve. As a reference, see Mike Bostock's General Update Pattern series (link is to first in the series) for how to update your SVG.

Remove empty tags in svg document

I have created a html page using d3.js to create and control a rather complex svg. One part of the page responds to the mouse movement, and depending upon where the mouse is, another part of the page displays a scatter plot. This scatter plot is erased and redrawn every time the mouse moves. This functionality works well. However, I had to add a clipPath via defs and when the path gets updated the clipPathalso needs to clear as the mouse moves. This results in hundreds of empty tags in the page like this: <defs></defs> as the mouse moves.
Question: Is there a way to clear out these empty tags? I don't like them in there, and I assume at some point they may have an effect on performance.
Here are the bits of code which result in these empty tags:
svg.append('rect') // outline x slice region (done once)
// this is both an aesthetic feature and is used as the clipPath
.attr({x: lPad,
y: tPad + conHeight + gap,
width: xslWidth,
height: xslHeight,
id: "xViewport",
stroke: 'black',
'stroke-width': 1.5,
fill:'white'});
// everything below here responds to the mouse and constantly changes
// prepare to draw: remove existing path and clipPath
d3.selectAll(".xslice") // these 'removes' work but leave the empty tags
.remove();
d3.selectAll(".xViewport")
.remove();
d3.selectAll("#xClipBox")
.remove();
// snip: lots of data preparation skipped
// the data used depends on mouse position which changes constantly
// now ready to draw
EDIT: Added this logic. It keeps the document from being polluted with empty tags, but it no longer clips.
if (d3.select("#xClipBox").length) {
// already exists, no need to do anything
console.log("xClipBox exists")
} else { // create it if missing
var clip = svg.append("defs").append("clipPath")
.attr("id", "xClipBox")
clip.append("use").attr("xlink:href", "#xViewport");
}
ORIGINAL:
//var clip = svg.append("defs").append("clipPath") // EDIT: never changes
// .attr("id", "xClipBox")
// clip.append("use").attr("xlink:href", "#xViewport"); // EDIT: never changes
//var xSlice = svg.append("g") // EDIT: never changes
// .attr("clip-path", "url(#xClipBox)")
//.attr("class", "xViewport")
CONTINUING:
xSlice.append("path") // EDIT: this part responds to the mouse position
.attr("transform", "translate(" + lPad + ","
+ (tPad + conHeight + gap) + ")")
.attr({width: xslWidth,
height: xslHeight,
"class": "line",
"class": "xslice",
"d": slice(xy)})
// All of this repeats each time the mouse moves

Cannot make labels work on zoomable d3 sunburst

After fiddling around for several hours now, I still cannot make labels work in my D3 Sunburst layout. Here's how it looks like:
http://codepen.io/anon/pen/BcqFu
I tried several approaches I could find online, here's a list of examples I tried with, unfortunately all failed for me:
[cannot post link list because of new users restriction]
and of course the coffee flavour wheel: http://www.jasondavies.com/coffee-wheel/
At the moment i fill the slices with a title tag, only to have it displayed when hovering over the element. For that I'm using this code:
vis.selectAll("path")
.append("title")
.text(function(d) { return d.Batch; });
Is there something similar I could use to show the Batch number in the slice?
--
More info: Jason Davies uses this code to select and add text nodes:
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style(...)
...
While the selection for his wheel gives back 97 results (equaling the amount of path tags) I only get one and therefore am only able to display one label (the one in the middle)
Needs some finessing but the essential piece to get you started is:
var labels = vis.selectAll("text.label")
.data(partition.nodes)
.enter().append("text")
.attr("class", "label")
.style("fill", "black")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.text(function(d, i) { return d.Batch;} );
You can see it here
The trick is that in addition to making sure you are attaching text nodes to the appropriate data you also have to tell them where to go (the transform attribute with the handy centroid function of the arc computer).
Note that I do not need vis.data([json]) because the svg element already has the data attached (when you append the paths), but I still have to associate the text selection with the nodes from each partition.
Also I class the text elements so that they will not get confused with any other text elements you may want to add in the future.

Categories