I am trying to add tooltips to my D3 graph here:
http://jsfiddle.net/ericps/b5v4R/1/
but adding these mouseevents mess up how everything is rendered and I don't know why. It is a line graph with axis
dots.enter()
.append("circle")
.attr("class", "dot")
.attr("cx", open_line.x())
.attr("cy", open_line.y())
.attr("r",3.5)
.on("mouseover", myMouseOverFunction)
.on("mouseout", myMouseOutFunction);
commenting out both .on methods at line 144 makes everything render how I expect it to
any insight into this?
The tooltips are based on this fiddle
http://jsfiddle.net/ericps/E4vrX/
You're missing myMouseOverFunction.
Simply defining the function will render your graph correctly (with the axes), allowing you to properly define your MouseOver functionality.
var myMouseOverFunction = function() {}
You can see an updated response to your jsfiddle: http://jsfiddle.net/sahhhm/hQgbc/
This could be implemented differently, but just a quick change was to remove the .FILL definition in your CSS and instead populating that value when creating the dot itself.
Few problems:
mouse:
The d3.mouse(container) function gives mouse location relative to the container (a node). You specified d3.mouse(this), but this is referring to the circle node, while you want to refer to the svg container: d3.select("svg").node().
infobox:
The infobox div was not defined anywhere, so nothing to be shown.
See updated Fiddle: http://jsfiddle.net/b5v4R/4/
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 a D3 chart with an axis that I want to style via the original calls to create it. However it only seems to work on the first call. I'll show you what I mean:
When I create the axis via
svg.select('g.y.axis').call(yaxis)
.selectAll("path")
.attr("fill","none")
.attr("stroke", "#000")
.selectAll("line")
.attr("fill","none")
.attr("stroke", "#000");
Only the path is styled correctly. You can check out my jsfiddle to see what I mean. I know this may be slower than just having CSS styles but I need it to be styled in the original call for what I'm working on. Thanks in advance!
It's because d3.selectAll("foo").selectAll("bar") will try to find <bar>s that are inner elements to founded <foo>s. And in your case svg finds no <line>s in <path>s.
Just call separately:
svg.selectAll("path")...
svg.selectAll("line")...
UPD
To find path/line in .y.axis:
svg.select(".y.axis").selectAll("path")
or
svg.selectAll(".y.axis path")
I have a application where I draw a world map with D3 and I use latitude and longitude data from a different source to plot them on the map. Currently what I learnt from lot of google'ing is that i can plot points by appending "circle" to to the SVG, which works fine for first 15 to 20 seconds after my web page is opened, later everything gets too slow and sloppy.
I am not sure how to keep the performance of the page decent and not add a new DOM element for every single circle I append with SVG. Do I need to use some other technology to achieve this ? Please advice.
My code looks like below and I call this like 500 times every 5 seconds.
function draw_point(lat, lon, keyword) {
var x = projection([lon, lat])[0];
var y = projection([lon, lat])[1];
svg.append("circle")
.attr("cx", x)
.attr("cy", y)
.attr("r", 0.5)
.style("fill", "gold");
svg.append("text")
.text(keyword)
.attr("x", x)
.attr("y", y)
.style("fill", "gold")
.style("font-size", "10px")
.transition()
.duration(40)
.style("opacity", 0)
.remove();
}
To give a bit more context, I am trying to do something like this site http://tweetping.net/ In this page I see that new DOM element is not being added for every dot placed in the map, I am looking for something similar.
The page which you mentioned uses canvas element and not svg or d3.js. You might want to look into
fabricjs
paperjs
kinectjs
Additional clarification of #VivekKumarBansal's suggestion: The general rule is that SVG slows down as more elements are added, but making images larger or smaller doesn't affect speed. Canvas doesn't slow down as more elements are added, but increasing size does slow it down. d3.js can be used with Canvas, although it seems to be more common to use it with SVG.
Here is a simplified, for-the-public's-eyes version of my bar graph. I can't seem to add anything to it. I was following this tutorial trying to add the vertical lines from the x-axis and the text inside each bar to my graph, neither of which are showing up. Basically nothing really shows up that I try to add, but I was able to add the x-axis line which you can see in graph.js # line 65. You can see where I tried adding lines to the graph in my code at line 56 in graph.js. The x values are arbitrary as I was just trying to get them to show up. What am I doing wrong?
The problem is that your selector, "line", is not specific enough. There are already 'line' elements in the document; they form the axis ticks.
Because of that, your code is doing the following. It selects the existing line elements and binds the data your provided to those elements. Since those elements already existed, the enter() selection will be empty. This means that your append("line") command will never be executed:
var lines = svg.selectAll("line")
.data(data)
.enter().append("line")
You can provide more specificity by adding a class. For example the following should work:
var lines = svg.selectAll("line.rules")
.data(data)
.enter().append("line")
.attr("class", "rules");
More about the enter, update, and exit selections can be found in Mike's article Thinking with Joins.
I want to draw a directed graph where any node can link to any other node (ie no defined hierarchy) and I was using the force layout engine but it doesn't support click event handling. I want to be able to click on a node and have that node centered and everything else laid out around it.
Is that possible in D3?
EDIT:
According to the API documentation for d3.force.layout:
force.on(type, listener)
Registers the specified listener to receive events of the specified
type from the force layout. Currently, only "tick" events are
supported
which suggests that simply adding a click event handler will not work.
Also, a tree layout needs (as far as I know) a hierarchy and my data is more tangled.
After puzzling away at this, I have something close to what I wanted so I'll share.
Firstly, There is no click handler for the layout engine but that is not needed for this; I wanted to click on a node and have that become fixed so I need a click handler on the node.
Secondly, there is a "fixed" property at the node level described in the d3 API for the force layout engine.
Combining these two, I can add click handler to the node that locks or unlocks the node's position.
Using the example here I add the following to the CSS:
circle:hover { fill: red; }
and change the circle definition from:
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.call(force.drag);
to
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.on("click", function(d){ d.fixed = 1 - d.fixed; force.start(); })
.call(force.drag);
and now, when I mouse-over a circle, it turns red (showing me I captured it) and then if I click, it locks it in place. I can then repeat this on other circles and drag them to where I want.