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).
Related
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)
I'm trying to move an element in D3, in order to correspond to a circle underneath. Basically, when the user zooms on the page, the circles shrink (which allows them to remain visually appealing and separated).
I want to build a function that fires with the zoom event, that keeps the images centered within the circles. The circles are centered on their center points. However, as the images shrink, they appear to move to the left because their anchors are in the top-left corner.
I need a solution that might involve adding their sacrificed width and height to their relative "x" and "y" attributes. How would I implement a function like this? Or is there a better way?
The blockbuilder is here: http://blockbuilder.org/KingOfCramers/125cc79bce7dea48b21786b37302d258
Here is the relevant bit of code (the icon variable is the starting width of the image):
function zoom() {
var iconMove = icon/d3.event.transform.k;
g.attr("transform", d3.event.transform)
d3.selectAll(".storyImages")
.attr("width", `${iconMove}px`)
.attr("height", `${iconMove}px`)
.attr("x", // Keep this centered)
.attr("y", // Keep this centered)
d3.selectAll("circle")
.attr("r", function(){
return cirSize/d3.event.transform.k
})
}
Thanks for any help you can provide!
If you can position them to start, you can update them the same way on zoom, just with the new width/height of each item. You initially append each item with these attributes:
.attr("x", (d) => projection([d.lat,d.lon])[0] - icon/2)
.attr("y", (d) => projection([d.lon,d.lat])[1] - icon/2)
.attr("width", `${icon}px`)
.attr("height", `${icon}px`)
Which offsets the icon from the x,y values returned by the projection by half the icon's width and height - centering it on the projected point. Note: Your x value is set with d.lat, d.lon rather than d.lon, d.lat, also, your csv has lng, rather than lon as a header, so d.lng should be used).
To keep the icon centered on the point, just update the icon using the new icon width/height (which in your case is located in iconMove) and the new projected point:
.attr("x", (d) => projection([d.lng,d.lat])[0] - iconMove/2)
.attr("y", (d) => projection([d.lng,d.lat])[1] - iconMove/2)
.attr("width", iconMove)
.attr("height", iconMove);
Here's an updated block (I wasn't able to figure out how to save a new block builder block).
So I have a line chart in D3js. I've added svg circle on the points. If user hover over that circle they see a tooltip.
https://jsfiddle.net/jhynag08/38/
But I want them to see tooltip when they are close to the circle(maybe in 5-10px range). I know I could add a background rectangle(full width and height) and do something like this -> https://jsfiddle.net/53aLmt7r/1/
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", mousemove);
But I also have bar chart inside line chart. So If I try to do this method, I no longer able to get tooltip of bar chart when I hover over it.
Is there a way to just have some kind of "active area" around the line chart where hover works?
One easy solution would be to give the circle a stroke style and make it transparent like this:
.style("stroke","transparent")
.style("stroke-width","15px")
Here's an updated fiddle
When there is an SVG element that is in the DOM, it's possible to get its bounding box using the getBBox function, as illustrated in this example:
http://bl.ocks.org/mbostock/1160929
Is it possible to get the bounding box without actually adding an element to the DOM?
In other words, can I calculate what the bounding box of some text would be if it was attached to certain node without actually attaching it?
The goal is to iteratively add labels to a graphic while avoiding overlapping text.
There is no way to calculate the height of a text before displaying it. The reason is that their might me many things that influence the height of the text (css classes, font present or not in the computer ...).
The easiest way to achieve it is to create the text hidden, get its height and then calculate the position.
How about
Adding the text
Get Bounds
Removing the text.
Something like this:
//add the text
var text = svg.append("text")
.attr("id", "text-to-remove")
.attr("x", 480)
.attr("y", 250)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.style("font", "300 128px Helvetica Neue")
.text("Hello, getBBox!");
//get bbox
var bbox = text.node().getBBox();
//remove the text
d3.select("#text-to-remove").remove();
//use bbox
var rect = svg.append("rect")
.attr("x", bbox.x)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height)
.style("fill", "#ccc")
.style("fill-opacity", ".3")
.style("stroke", "#666")
.style("stroke-width", "1.5px");
working code here
Hope this helps!
In the process of learning D3.js.
Is it possible using a force layout to place a circle within another circle shape as per the picture. I am hoping to transition between a single circle per node to a display showing two circles per node. The size of the effective donut is used to illustrate another variable in the data.
Is this possible?
You don't even need to use anything other than a basic svg circle, as you find in most examples. Just bind the data to it, apply a stroke, and set the stroke-width attr to your other variable. Or r - otherVar, I'm sure you can figure that part out.
If this doesn't satisfy, build your own shape. The 'g' svg element is a container element, and lets you build whatever you like. Add two circles to a g, fill them how you like. Make sure to add them in the right order, since svg has no concept of 'on top', things just get painted in the order that you add them.
edit: okay, quick demo so you can learn some syntax. I didn't add any comments but hopefully the code is very verbose and straightforward. Find it here.
d3/svg is something that you have to just bash your head against for a while. I highly recommend spending some time creating a sandbox environment where you can quickly test new things, save, refresh browser to see results. Minimizing that turnaround time is key.
Thanks to roippi I was able to create a group containing two circle shapes.
var nodeCircles = svg.selectAll("g")
.data(nodes);
// Outer circle
var outer = nodeCircles
.enter()
.append("circle")
.attr("class", "node_circle")
.attr("r", function(d) { return d.radius_plus; })
.style("fill", function(d) { return d.color_plus; })
.style("opacity", 0);
// Inner circle
var inner = nodeCircles
.enter()
.append("circle")
.attr("class", "node_circle")
.attr("r", function(d) { return d.radius; })
.style("fill", function(d) { return d.color; })
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
.on("mouseover", mouseOver)
.on("mouseout", mouseOut)
.call(force.drag);
Outer circle visibility is toggled via a button.
As mentioned, I use a desktop based IDE to run/test visualisation languages. Currently the IDE supports studies written in D3.js, Raphael, Processin.js, Paper.js and Dygraphs. Picture below...