Align text node centrally(horizontally) inside group element - javascript

I have this group element and a text node inside it:
var legend = svg_container.append('g');
var legend_text = legend.append('text').attr('y', 0)
//other elements inside 'g'
This is just part of the code. In actuality, I have other child nodes inside the group element created on the fly after the text node(these other elements decide the width of the group element).
How do I position the text node centrally inside the group element?

To center it, you can use the text attribute text-anchor="middle" with the x and y attributes for positioning:
legend_text.attr("text-anchor","middle");
legend_text.attr("x", 0);
legend_text.attr("y", 0);
Here, your text will be fixed to the center origin of your group legend.
In fact, it won't be perfectly centered, to do so, you should set y to the half of the font-size attribute of your text.
Hoping I've correctly understood your problem !
EDIT: With your indication, I see two solutions :
My favorite one would include a modification of the legend group, by adding him a transform="translate(x,y)" attribute,
where the x and y are the center of your svg element. This one may not work with the rest of your code, but I find that doing so in general is a clean way to have multiple drawings living together in a single svg.
The other one is simplier by far, and I think will answer to your problem for now :
You replace the x and y values by the center of your legend group. The little problem here is the calculation of thoses values, because you can't read them directly from the element.
In fact, a g element doesn't have width or height attributes, it's only a container for applying attributes for a group of svg elements. That's why I recommend using my first solution.

Another way of centering it in the group using bbox
//make a g
var svg = d3.select("body").append("svg").attr("width", "900")
.attr("height", "900").append("g");
//add circle to it
svg.append("circle").attr("cx", 50).attr("cy", 50).attr("r", 30);
svg.append("circle").attr("cx", 250).attr("cy", 150).attr("r", 30);
//get bbox
var bb = svg.node().getBBox();
var centery = bb.y + bb.height/2;
var centerx = bb.x + bb.width/2;
svg.append("text").text("Hello").attr("x",centerx).attr("y", centery)
Working code here

Related

In D3, how does one place text inside shapes that already have text attached to them?

I am currently working on the following d3 example where I'd like to place text inside of the arcs, similar to this example. However, whenever I try to append text to anything, the text just doesn't display. I've looked at the developer console, and it appears to be there, but it won't visually display on the screen. I used all the code provided in the first example, except I tried to add the following the the arc elements:
("the d3 element").enter().append("svg:text").text("???")
("the d3 element").enter().append("text").text("???")
("the d3 element").append("svg:text").text("???")
("the d3 element").append("text").text("???")
Aside from cutting off some of the styling changes, it seems like no matter where I put any of this code, it just doesn't want to work for me. I would appreciate and help!
"whenever I try to append text to anything, the text just doesn't display": Text cannot be appended to most svg elements. You can append text to the svg itself or a g, but you cannot append it to a path, rect, circle, etc.
One of the most common methods of dealing with this is to use g elements to place a shape and text while binding data to the g. Using a transform on theg will translate shape and text - great for things like circles and rectangles.
There are several other approaches you can use to overlay text on svg elements:
Use the positioning attributes of an element to set the x and y attributes of text so that you can place text over top of an element.
Use a path as a text path to place text (as in your example)
Use utility methods such as centroid (for arcs or geopaths for example)
Find the bounding box of elements and place elements using this information.
These options help place, but won't make sure that the text falls within the bounds of a shape - that is different complication.
For arcs, one option is to use a circular path as a text-path where the circle has a radius between that of the inner and outer radius of your arc - then place the text using a text offset that reflects the start angle - or make an arc for each piece of text. The general mechanism is shown below (note it can't use a circle element as svg textPaths must follow paths):
var svg=d3.select("body")
.append("svg")
.attr("height",400)
.attr("width",400)
.attr("transform","translate(200,200)")
var arc = d3.arc()
.innerRadius(50)
.outerRadius(100)
.startAngle(0)
.endAngle(2);
var arcText = d3.arc()
.innerRadius(75)
.outerRadius(75)
.startAngle(0)
.endAngle(2);
var arc = svg.append("path")
.attr("d",arc)
.attr("fill","steelblue")
var textPath = svg.append("path")
.attr("d",arcText)
.attr("id","textpath")
.attr("fill","none")
.attr("stroke","black");
var text = svg.append("text")
.append("textPath")
.attr("xlink:href","#textpath")
.text("title")
.attr("startOffset", "25%") // the bottom of the arc is from 50%-100%, the top from 0 to 50%
.style("text-anchor","middle")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
The above mechanism is very similar to the example you link to - it appends text using the arcs as text paths. The example you reference uses text paths of the visible arc(donut) segments themselves - and offsets the x,y positions to move the text into the arc itself (as opposed to on the arc).
Note that I've used v4, as opposed to v3 as in the linked example.

How to use a svg inside a svg?

I am using D3.js and i try to create a svg inside an svg.
For example my first svg is this:
var svg = d3.selectAll('body')
.append('svg')
.attr('width',500)
.attr('height',500);
Then i want to create a second svg inside this first one and i want it to appear at the upper right corner of my first svg. How is that possible? I thought about the attributes of width = 100 and height = 100 for the second svg. The reason for this question is, that i use the force-layout in D3.js and it can be realy big depending on the input of data. So i want to put the graph itself in the first big svg and other informations like texts in the smaller second svg. If a solution with div elements could be better, please let me know.
Just append another SVG within the first.
var svg = d3.selectAll('body')
.append('svg')
.attr('width',500)
.attr('height',500);
var innerSVG = svg.append('svg')
.attr('width',100)
.attr('height',100);

Align svg text inline with d3.js legend

I'm trying to use Susie Lu's legend plugin.
Here is a plnk of work so far;
http://plnkr.co/edit/wrOWPYu4PAqwr8f5OOjw?p=preview
Image below is what I would like to achieve (just layout, content/colour doesn't matter)
I've tried the standard float:left + display: inline on the classes attached to the text/rectangles but it didn't work for me. Maybe I was making a mistake. I'm not sure If I should be doing this inside the d3 script or in the css file anyway?
Hope this is a simple fix - any help is much appreciated!
Thanks
Those attributes (float:left + display: inline) only work on html elements, what the legend library here produces is svg
It's possible, but needs a little bit of work
http://plnkr.co/edit/YRFRWEtMFDmje06Mg5KY?p=preview
var cells= d3.selectAll(".cell");
var cellGap = legendLinear.shapeWidth()+(legendLinear.shapePadding()/4);
cells.select("text")
.attr ("transform", "translate("+cellGap+" 13)")
.style ("text-anchor", "start")
;
var offset = 0;
cells.each (function(d,i) {
var d3sel = d3.select(this);
var textWidth = d3sel.select("text").node().getComputedTextLength();
var offsetInc = textWidth + legendLinear.shapeWidth() + legendLinear.shapePadding();
d3sel.attr("transform", "translate("+offset+" 0)");
offset += offsetInc;
});
This firstly moves the text element for each legend 'cell' to the right of the colour swatch rather than below.
Then it moves the g elements that hold the label and swatch horizontally apart dependent on the width of the label (and the swatch and the declared padding) using .getComputedTextLength() and a running total for the offset.

How to translate all text in svg container that are already rotated

Is there a short way of adding the translate to the text's transform attr without removing the rotation?
currently I add rotation when the text is created:
.attr('transform', rotate(270 xValue, yValue);
Note that this is done in a for loop since there are numerous texts
but later on the user should be able to drag the text horizontally. Dragging one text should move all the texts across. Doing the following removes the rotate attribute from above:
svgContainer.selectAll("text").attr("transform", "translate(" + [tx, ty] + ")");
is there a short way where I can select all the text and just append the translate attr to the rotate? Or do I need to loop through all the Texts and manually change each one?
I have just came to a working solution:
I've placed all the text inside a "g" element. and then translated the "g" element.

How to get the position and width of a tspan element

I am working with d3js where lib create nodes and links according to data.
At some place have some specific requirement to add multiple text in same line like url "www.xyz.com/home/user" (here 3 strings "www.xyz.com","/home","/user"). They are not separate nodes so I can't find position with d3. It's just a <text> element with 3 <tspan> children.
<text id="TaxFilingResource_URL" class="label url" dy="24" dx="30">
<tspan id="TaxFilingResource.URL.1">www.xyz.com</tspan>
<tspan id="TaxFilingResource.URL.2">/home</tspan>
<tspan id="TaxFilingResource.URL.3">/user</tspan>
</text>
and displaying like this below
www.xyz.com/home/user
I need to get the position and width of each <tspan> element. My code is
var el = document.getElementById(d.source);
x = el.getStartPositionOfChar('').x (or)
x = el.getClientRects().left;
that give relative position on text inside g element , but in some browser and on mac it will return absolute position.
Is there any right way to find position and width of tspan in JavaScript that worked with all browsers ( IE must > 9th version).
In SVG 1.1 tspan doesn't have a getBBox method, but in SVG2 it does, I've reported a chromium bug for it reporting the wrong value, http://code.google.com/p/chromium/issues/detail?id=349835.
This would give you the proper position and dimensions if the browsers implemented SVG2:
var bbox = document.getElementById("TaxFilingResource.URL.2").getBBox();
See jsfiddle for a full example.
For now, you can do a workaround using the methods that are available in SVG 1.1:
var home = document.getElementById("TaxFilingResource.URL.2");
var extent = home.getExtentOfChar(0); // pos+dimensions of the first glyph
var width = home.getComputedTextLength(); // width of the tspan
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.x.baseVal.value = extent.x;
rect.y.baseVal.value = extent.y;
rect.width.baseVal.value = width;
rect.height.baseVal.value = extent.height;
See jsfiddle.
If you need to transform to another place in the tree:
var dest = document.getElementById("dest"); // some arbitrary container element
var fromHometoDestmatrix = home.getTransformToElement(dest);
rect.transform.baseVal.appendItem(
rect.transform.baseVal.createSVGTransformFromMatrix(fromHometoDestmatrix));
dest.appendChild(rect);
See jsfiddle.

Categories