d3js SVG:title tooltip doesn't show up
I have a graph which contains a lot of circles, now I would like to insert to each circle a tooltip but it doesn't work neither as title in circle nor as tooltip box. I cannot find my mistake:
var circleSmall = canvas.append("circle")
.attr("cx", 869)
.attr("cy", 693)
.attr("r", 10)
.attr("fill", "green")
.append("svg:title").text("Your tooltip info")
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
http://jsfiddle.net/WLYUY/57/
The most important obstacle here was your rects. They were appended AFTER the circles and were preventing mouse events from reaching the circles. So, first thing to do is:
/* do this or append circles AFTER appending recs */
rect {
pointer-events: none;
}
I have added the mouseover and mouseout events necessary to show/hide the tooltip.
Complete FIDDLE here.
NOTE: For demonstration, I have painted the one circle receiving the events in large orange with an orange-red border (you can't miss it). This is just an example...normally you would apply the event listeners to all circles. And this brings me to a sanity check: you are currently appending shapes "manually" but I assume you will eventually do it based on data binding, one of the main points of D3.
Related
Currently learning d3.js v5
I am working with a graph that displays a tooltip when an area of the graph is hovered over, along with highlighting the graph area selected. The issue I have noticed is that whenever any bar or line is hovered over, the tooltip won't display, and the area is no longer highlighted until I move off that area.
I was wondering if there is a way to have the mouseover element read the SVG element under the bars and lines or if I also need to add event listeners to the bars and lines.
I could potentially add another rect element that is invisible over all the other elements so that it is triggered first, however I also want to add interactivity to the bars and lines in the graph.
d3.csv("test.csv").then(function(dataset) {
backBar.selectAll("rectBack")
.data(dataset)
.enter()
.append("rect")
.attr("class","rectBack")
.attr("x",(d)=>xScale((d.year)))
.attr("y",margin.top)
.attr("width",(w)/dataset.length)
.attr("height",h-topBottom)
.attr("opacity",0)
.attr("fill","lightgrey")
.on("mouseover",function(d){
d3.select(this)
.attr("opacity",0.3);
showTooltip(d);
})
.on("mouseout",function(){
d3.select(this)
.attr("opacity",0);
removeTooltip();
})
function showTooltip(d){
d3.select("body")
.append("div")
.attr("class","tooltip")
.attr("opacity",0.5)
.html("<h5>"+d.year+"</h5>")
.style("top", yLineScale(d.line1) + svgPos.offsetTop)
.style("left", xScale(d.year) + svgPos.offsetLeft)
console.log(d);
}
function removeTooltip(){
d3.selectAll(".tooltip")
.remove("*");
}
}
svgPos is a variable that holds the coordinates of the SVG
yLineScale and xScale are both scales for the X and Y axis respectively
Link to image of graph: https://imgur.com/a/7rJFQjd
So, I am thinking something along the lines of a tool tip or a pop-up, except with much more information in it, and I also want every information box to be in the same location on the page. Overall, I just want to be about to click on an SVG (that represents some data element), and on a click event, trigger and information box to pop up in front of it; maybe even give it it's own close function if it covers the SVG. I know I have seen this many times before, but the exact name is escaping me at the moment.
Right now, I have a circle:
node.append("circle")
.attr("cx", sankey.nodeWidth()/2)
.attr("cy", function(d){return d.dy/2;})
.attr("r",function(d){return Math.sqrt(d.dy);})
.style("fill", function(d){return d.color = color(d.name.replace(/ .*/,""));})
.style("stroke", function(d){return d3.rgb(d.color).darker(1);})
.style("stroke-width",5)
.on("mouseover", tip.show)
.on("mouseout", tip.hide)
.on("click", changeColor(function(d){return d.color;}))
.append("title")
I'm hoping to write something like:
.on("click",function(d){/*Create a rect element*/})
.append("text", "This element represents the "+d.name+" element and will weigh approximately "+d.weight+"lbs.")
.attr("cx", 50)
.attr("cy", 50)
.on("click_exit?" function(d){this.remove();})
I am not certain how to approach this, but I just want a text box to display on click and have an exit button (or some closing method) to close it. Can I add something like the above code to my previous code, or must I create an entire new svg element completely disjoint from it?
I've done things similar to this before, i've found that SVG can be a real pain when not doing actual d3 graphical stuff. I would work with html in this case.
What i did was create an html element with the desired template, absolutely position it where i wanted, and then set display:none or opacity:0.
Then for your onClick function
.on("click",function(d){
d3.select('.my-info-box p')
.append(d)
.style('display','block')
})
This is the bare bones functionality. The takeaway here is to use html and not svg. To close it you can position a × in the top right/left of the box, and then onClick, close the info box.
I created two different SVGs. One contains a graph with data points, the other one contains three lines. The lines color are supposed to be dependent on the selected data point and I have not managed to get this done yet (more details below). The jsfiddle can be found here: jsfiddle.
What I would like to do is to change the color of the three lines when I mouseover the data points. I managed to change the color of all lines to the same color but would actually like to use the color that is associated to the respective data point but I don't know how I can pass the color data which are stored in myColors to the function where I set the lines' color.
The relevant code is shown below. I add a graph with datapoints to mySvg and when I mouseover the data points, I change their color to black and the color of the lines in the other SVG to green. However, instead of changing all lines' color to green, I would actually like to change their colors to the colors defined in myColors (see the above linked jsfiddle to find the data). How could I do this?
var circles = mySvg.selectAll("circle")
.data(lineData)
.enter()
.append("circle");
var circleAttributes = circles
.attr("cx", function (d) { return xScale(d.x); })
.attr("cy", function (d) { return yScale(d.y); })
.attr("r", 6)
.style("fill", 'red')
.on('mouseover', function(d){
d3.select(this).style("fill", 'black');
d3.select('#myLines').selectAll("line").attr("class","sweepline").style("stroke", 'green');
})
.on('mouseout', function(d){
d3.select(this).style("fill", 'red');
});
As with many d3 problems this one is easily solved using data binding. Your custom colors could be bound to the lines you append to the second SVG. Since your array myColors, consisting of the arrays of custom colors per line, has the same structure as your other arrays like names, x1Val, y1Val and so forth, it can be easily integrated in the data array coords used for binding information to your lines:
var coords = d3.zip(names, x1Val, y1Val, x2Val, y2Val, myColors);
This data per line can later on be used in the mouseover event handler for your circles setting the stroke style on the lines.
.on('mouseover', function(d,i) {
// ...
d3.select('#myLines')
.selectAll("line")
.style("stroke", function(d) {
return d[5][i].color;
});
})
The callback determines the color by
accessing the array of custom colors, which is at position 5 of the data array bound to the lines, hence d[5],
getting the ith object of this array of colors. The i is the index of this circle, which is passed as parameter to the event handler and made available to the stroke callback by a closure,
getting property .color from this object
Check the updated JSFiddle for a working example.
Furthermore, I have updated the mouseout handler to delete the previously set stroke style causing the lines to be reset to their default color set by class sweepline. This behaviour, at least to my eyes, seemed to be missing.
d3.select('#myLines')
.selectAll("line")
.style("stroke", null);
Successfully created a heatmap using d3.
Here's the FIDDLE.
I have some basic idea on using d3's mouseover events. But now I wanted move a step ahead.
This is what I'm looking for. When I hover on a legend, I wanted the hovered legend's respective data to be highlighted in the chart.
Can someone help me to achieve it?
You're not binding the data to the legend, which makes this task a bit more difficult, but you can still do it fairly easily. The idea is to assign a class defined by the fill color to the rect elements and then select accordingly in the mouseover handler. The code looks like this.
// for the rectangles
.attr("class", function(d) {
return "hour bordered " + "color-" + colorScale(d.value).substring(1);
})
// for the legend
.on("mouseover", function(d, i) {
svg.selectAll("rect.color-" + colors[i].substring(1)).style("stroke", "blue");
})
.on("mouseout", function(d, i) {
svg.selectAll("rect.color-" + colors[i].substring(1)).style("stroke", "white");
});
Complete example here.
I'm relatively new to D3, svg, and javascript in general, so please bear with me :]
I have been experimenting with D3 for creating plots and graphs. I have created a small plot using D3 and have been attempting to make it compatible with IE8. Here is a link to the more-or-less working build of my graph.
http://jsfiddle.net/kingernest/YDQR4/1/
After some research, I quickly realized that the only way running D3 on IE8 would be at all feasible is by using other APIs in conjunction with D3. Luckily, I found that someone had already put in some work into a project called "r2d3" which, from my understanding, uses raphael to paint the canvas on the IE8 window instead of using SVG (which apparenly was not supported in IE8).
I have been able to get items drawn on the screen, which is half the battle. However, I'm having many issues, particularly with my tooltip. My tooltip is written as a DIV container that floats and changes position/opacity on hover of the data circles. This seems to work fine in other browsers, but with r2d3, I have not been able to get it working. I suspect this is because of the fact that I am creating the div tooltip outside of the (in the #main div). However, I have tried placing tooltips inside of the SVG container with no avail. I then did more reseach and discovered I would have to wrap a div container inside a tag, but after some experimentation with this, I still wasn't able to get the tooltip to work correctly in IE. I attempted to wrap the in a SVG group (), and altered the positioning of this object instead, but this did not seem to work either, and simply through numerous exceptions when trying to append the foreignObject tag to a group.
At this point I'm sort of stuck, and was wondering if anyone had any suggestions as to how I may be able to successfully implement the tooltips. I've also noticed that using d3.select(this) inside my functions, when attempting to select a particular data point (in this case, a circle) seems to present a number of issues when attempting to access or modify that item's attributes, but I think this is a whole other issue entirely.
Any help is greatly appreciated!
Example of how I'm currently creating the tooltips:
//Create tooltip element
var tooltip = d3.select("#main")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("opacity", 0);
function mousemove()
{ //Move tooltip to mouse location
return tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");
}
//Mouseover function for circles, displays shortened tooltip and causes other circles to become opaque
function mouseover()
{
var myCircle = d3.select(this);
d3.select(this).attr("class", "dataCircleSelected"); //Color circle green
tooltip.html( //Populate tooltip text
"Username: " + d3.select(this).attr("username") + "<br/>" +
"Session ID: " + d3.select(this).attr("sessionid") + "<br/>" +
"Impact CPU: " + d3.select(this).attr("impact")
)
.transition()
.duration(250)
.style("opacity", .7);
//After 1000ms, make other circle opaque
svg.selectAll("circle")
.filter(function(d, i){ //return every other circle
return !d.compare(myCircle[0][0].__data__);
})
.transition().delay(1000)
.style("opacity", .2);
}
Have you tried using foreignObjects AND explicitly using the xhtml namespace for html tags in the foreignObject (write xhtml:div instead of div) as explained here: HTML element inside SVG not displayed ?
This would give something like that for the tooltip definition
var tooltip = d3.select("#main").append("foreignObject")
.append("xhtml:div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("opacity", 0);