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!
Related
Still being rather inexperienced in using d3.js I have hit a road block.
Hoping anybody out there can help me.
I am trying to display a picture upon mouse click on a node in a graph.
Ideally, I'd like to click on several nodes and display images. Double click on a node should remove the image. And clicking on the background removes all displayed images. But one step at a time.
What I have done so far is:
Succeeded in using tooltips. I was also able to change the size of a circle node upon mouse clicking on it.
I used as toy project Mike Bostock's force-directed graph example: https://bl.ocks.org/mbostock/4062045 .
I am using d3.js v4
Based on an example on the web I was able to add pictures to all nodes:
Add different images for nodes in d3 v4 network graph
I tried to tailor this example to my needs. First I added:
var defs = svg.append('defs');
Further more:
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); })
.call(node_drag)
.on("click", function(d){
defs.append("pattern")
.attr("x", 0)
.attr("y", 0)
.attr("width", 12)
.attr("height", 12)
.append("image")
.attr("xlink:href", 'https://assets-cdn.github.com/images/modules/open_graph/github-octocat.png')
.attr("width", 12)
.attr("height", 12)
.attr("x", 0)
.attr("y", 0);
})
In my browser the html indicates that the image is added:
But the image is not showing in the browser.
At this point I am turning to you and hope for some hints how I can accomplish displaying an image as node upon clicking with my mouse cursor on it.
Any input is highly appreciated,
Markus
Your inspector is showing that the image was appended to the pattern, but it was never linked to the circle element.
Instead of appending the pattern to the defs inside the click function, you should just append the pattern with a given ID...
var defs = svg.append('defs');
defs.append("pattern")
.attr("x", 0)
.attr("y", 0)
.attr("id", "myPattern")//ID here
.attr("width", 12)
.attr("height", 12)
.append("image")
.attr("xlink:href", 'https://assets-cdn.github.com/images/modules/open_graph/github-octocat.png')
.attr("width", 12)
.attr("height", 12)
.attr("x", 0)
.attr("y", 0);
... and then, inside the click, just change the fill of the circle according to that ID:
.on("click", function() {
d3.select(this).attr("fill", "url(#myPattern)")
})
Here is Bostock's bl.ocks with those changes (I made the circles bigger so you can better see the image): https://bl.ocks.org/anonymous/0e653b6d21c8d57afa234d5d1765efe0/78ba15e533a2b8f8e6575a30d97b27d156ce2faf
I'm building a simple bar chart using d3.js and it works fine.
However, when I try to display texts on each bar, nothing happens. The console doesn't return any error so I can't understand the problem.
My code is available here, I tried to display simple text like "Hello" but still nothing shows up.
Problem:
Appending texts to a rect element in SVG.
Solution:
Append the texts to the SVG or to a group.
Instructions:
You are appending your texts to the rectangles. Nothing will show up as an error in the console because there is no error to show, but the texts won't appear.
You have to create a variable for the texts:
var texts = svg.selectAll(".mytexts")
.data(data)
.enter()
.append("text");
And then setting the attributes:
texts.attr("class", "value")
.attr("x", function(d) { return widthScale(d.vacant); })
.attr("y", heightScale.rangeBand() / 2)
.attr("dx", -3)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(function(d) { return format(d.vacant); });
Don't forget to change the CSS accordingly.
I am making a very basic force-directed graph with labels using the d3.js library. Here is part of my code:
var node = d3.select("#canvas").selectAll(".node")
.data(data_nodes)
.enter()
.append("g")
.attr("class","node");
var nodes = node.append("circle")
.style("fill","green")
.style("r",10);
var text = node.append("text")
.attr("dx", 20)
.attr("dy", ".35em")
.style("z-index",1)
.text(function(d){
return d.name;
});
This goes well at the beginning but due to the shifting location of nodes that occurs with the ticking, these text elements that are supposed to serve as labels get covered up by other nodes. I would like to know if there is a way to solve this problem.
I've made a fast stand-alone part of the project I have as to show you my problem. I have this circle that represent a node in my graph and what I want is to have on mouseover a box appearing with some text and image. This far I have the node, the box and the text. My problem is that the image doesn't appear, there is only a border of the space dedicated to the image and nothing inside.
I don't know if I'am using the right order of appending things?
I`m using a div that is created initially and it's invisible, then I create the circle and bind a listener to it for the mouseover event. The listener appends to the div a text field and the image. For the image it gives the href attribute and on mouseout it renders the div invisible again. Would you help me find what is going wrong in all this....thank you!
//the box, invisible for now
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("width","60px")
.style("height","28px")
.style("padding","2px")
.style("font","12px sans-serif")
.style("border","0px")
.style("border-radius","8px")
.style("background", "lightsteelblue")
.style("visibility", "hidden");
//the svg container
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500);
//the node
svg.append("circle")
.attr("class", "logo")
.attr("cx", 225)
.attr("cy", 225)
.attr("r", 20)
.on("mouseover", function(d){
tooltip.text("some text");
tooltip.append("image")
.attr("href","https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width","16px")
.attr("height","12px");
tooltip.style("visibility", "visible");
})
.on("mousemove", function(){return tooltip.style("top", (event.pageY-
10)+"px").style("left",(event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
You need to change image to img and href to src. Complete working example here.
I'm using d3.js to generate some rects which are directly above one another, in this fashion:
var greenRed = d3.select(".green-red").append("svg")
.attr("height", 120);
greenRed.append("rect")
.attr("fill", "green")
.attr("x", 0)
.attr("y", 0)
.attr("height", 50)
.attr("width", 300);
greenRed.append("rect")
.attr("fill", "red")
.attr("x", 0)
.attr("y", 50)
.attr("height", 50)
.attr("width", 300);
I've noticed that depending on which colours are stacked on top of one another, there is either a very thin whitespace present between the rectangles, or a sort of "overlap" of the two colours.
You can see what I mean in this fiddle: http://jsfiddle.net/ysim/PrC7X/
You can see that for .green-green and .green-grey there's no issue (to the naked eye, anyway); but for .green-blue and .red-blue, there is an overlap, and for .green-red, there is an extra whitespace.
I've tried adding .attr("stroke-rendering", "crispEdges") (suggested here) and .attr("stroke", "none") to the rect elements, as well as wrapping both the rect elements in a g element within the svg and adding .attr("stroke-rendering", "crispEdges") to that (suggested here), but neither of those solutions work.
What's causing this extra whitespace/overlap, and how do I go about fixing it so that the colours are neatly aligned, like in the first two cases?
try setting the stroke-width property to 0
That's antialiasing. Add style="shape-rendering: crispEdges" to the <div> elements and it will go away. You could add it to the shapes themselves instead if you want either as an attribute or a style.
The other thing to do is to add 0.5 to the y co-ordinates of your shapes There's more information about why that works here