Implementing <div> tooltips via d3 within an <svg> container - javascript

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);

Related

tooltip on chart not working

I've created a line-chart in D3 (with angular) and trying to implement tooltips. The problem is, when I append the tooltip DIV to directive element, it doesn't work, while it works fine if I append the div to body (which I don't want):
// not working
// d3.select(element[0]).append() doesn't work either
var div = g.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// works fine
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
Here is the JSFiddle. (line 69)
You can't use HTML elements (such as div) inside a SVG, unless you use a foreign object (not supported by all browsers). You can create your tooltip using a rect element.

How do you add and remove an information template by clicking on an SVG element in D3.js?

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.

Images not Generating D3

Here is my problem. My graph currently looks like this: Which is dandy. However, I want the black squares on top to be filled with pictures. Luckily I have a CSS file that has pictures linked with classes. I also have a JSON file that contains all the class names. All those class names are assigned to the squares and I can see the picture in the inspect element on Chrome. The only issue is the pictures don't appear in the square. (Also my axises broke, but that is secondary concern). CSS, JSON
This is where I'm assigning classes and creating the rectangles.
svg.selectAll(".div")
.data(data.chartData, function(d){return d.vNm;})
.enter().append("rect")
.attr("x", function(d){
return x(d.vNm);
})
.attr("y", function(d){
return (y(d.values.reduce(function(sum, d){
return sum + d.amount;
}, 0))) - 64.5;
})
.attr("width", 43)
.attr("height", 43)
.attr("class", function(d){return d.vId;})
.style("fill", function(d) { return segColor(d.data.type); });
One approach to solve your problem is to use html elements like div for the images above the chart instead of svg elements, so you can use the full power of css.
Luckily you don't need to calculate the position of those html elements by yourself, there are some libraries that help you position the images correctly above the bars in the chart.
Check out https://popper.js.org/ for example, you can just call its API for each bar you render using d3.js:
var popper = new Popper(barElement, onPopper, {
placement: 'top'
});
SVG elements do not follow exactly the same CSS rules as typical HTML elements.
In your case, background-image doesn't work.
The least painful way to achieve the effect would be to embed an <image> tag after the <rect>:
<image width="100" height="100" xlink:href="data:image/png;base64,...">
It means that you have to modify your JSON to store the image's base64 data in there instead of CSS.

how to select parent div of svg in D3.js

I have read several articles and tutorials but couldn't find a sufficient answer on how to select the parent div of the svg using d3.select. I basically just want to append a tooltip to the div which contains my chart like this.
//this selection probably doesn't make sense...
var tooltip = d3.select("#pie-svg").select(this.parentNode).append("div")
.attr("class", "piechart-tooltip")
.style("opacity", 0);
Along the lines of this question:
var tooltip;
d3.select("#pie-svg").each(function() {
tooltip = d3.select(this.parentNode).append("div")
.attr("class", "piechart-tooltip")
.style("opacity", 0);
});
The problem with the code you've posted in the question is that this won't be defined (or set to the right element) in this context. When used with .each(), it will.

AngularJS + d3js: Issues with resizing objects

I'm attempting to integrate AngularJS with d3 for dragging and resizing. I've managed to create a rect object that is draggable in an SVG element, and resizable using resize handles. The resize handles work as they should, but resizing is choppy when I try to resize in the north or east direction. I created the following Plunk as a demo of the issue: http://plnkr.co/tG19vpyyw0OHMetLOu2U. (I've simplified it to show the issue I've run into, so there's only one resize handle.)
Dragging works as it should, and resizing in the west and south directions works as well (not shown in the demo).
Figured I'd ask the community and see if anyone had run into this before. Thank you all.
The problem is that you're modifying the rect element itself and the enclosing g element. There's a very short delay between setting the size of the rect and the position of the g simply because this has to be done with two separate commands. During this delay, the cursor position relative the the drag rectangle changes, firing a new drag event with values that correspond to the inconsistent intermediate state. This is fixed immediately afterwards (as soon as the attributes of both elements have been adjusted) and a new drag event is fired that fixes the inconsistency, but it is noticeable as a flicker.
The easiest way to fix this is to change both size and position for the rect and nothing for the g element. This means adjusting the position of the drag rectangle as well and makes the code less nice, but avoids the timing/inconsistency problem.
So myrect becomes
var myRect = d3.select(document.createElementNS("http://www.w3.org/2000/svg", "rect"))
.attr("data-ng-width", "{{square.w}}")
.attr("data-ng-height", "{{square.h}}")
.attr("stroke", "yellow")
.attr("stroke-width", 3)
.attr("fill-opacity", 0)
.attr("data-ng-x", "{{square.x}}")
.attr("data-ng-y", "{{square.y}}");
and resizer
var resizer = myGroup.append("rect")
.attr("width", 5)
.attr("height", 5)
.attr("stroke", "blue")
.attr("stroke-width", 1)
.attr("fill-opacity", 0)
.attr("cursor", "nw-resize")
.attr("x", "{{square.x-2.5}}")
.attr("y", "{{square.y-2.5}}")
.call(nresize);
I've updated your code with this solution here.

Categories

Resources