Reputation: 11714
So I have an SVG rectangle that has SVG text inside. The SVG Text that is being placed in the textbox includes things that come from a user's profile. This information is sent from the server and I want it to have a specific positioning like so:
So far I've managed to do this fairly easily... but the thing is sometimes people don't fill out their profiles completely (i.e. they leave our their date of birth, or location) so it ends up creating an SVG text that is blank.
<g display="default">
<text x="10" y="30">Alexander the Great</text>
<text x="10" y="40"></text>
<text x="10" y="50">Greece </text>
</g>
I can assume they always have their name but if the date of birth is not provided, I want it so that my textbox is smart enough that it will move Location right under the Name.
Right now I enter in specific x and y attributes to my text node which is why this is proving a little difficult. Is there a way to make it so that the positioning is more responsive? Through CSS or through SVG attributes? I am generating the SVG elements through D3.js so if I need to, I could simply set if statements to check but I kind of want to avoid this. Thanks.
Upvotes: 2
Views: 1343
Reputation: 25381
Use tspan within text element. It solves your problem perfectly.
<text y="30">
<tspan x="200">Alexander the Great</tspan>
<tspan x="200" dy="1em"></tspan>
<tspan x="200" dy="1em">Greece </tspan>
</text>
Upvotes: 1
Reputation: 3902
Assuming you are receiving JSON data for profile entries, you could stuff an array with non-empty entries and then generate the text nodes from the array.
record = {
"name": "Alex G.",
"dob": "",
"location": "Greece"
};
// This provides and array of objects with both key names and values.
var props = d3.entries(record).filter(function(d) {
return d.value.length > 0;
}).map(function(d) {
return d.key + ": " + d.value;
});
Then you can add the data to the DOM with d3:
d3.select('g[display="default"]')
.data(props)
.enter().append('text')
.attr('x', 10)
.attr('y', function(d, i) {return (i+1) * 10;})
.text(function(d) { return d; });
EDIT: Incorporated Adam Pearce's suggestion for a more idiomatic method of constructing the props array.
Upvotes: 1
Reputation: 5323
You could potentially use an ordinal Y scale and a data bind to control the number and position of the <text>
elements.
Because you care about the number of elements and not necessarily the contents, your scale could be based on array index:
var yScale = d3.scale.ordinal()
.domain([0, 1, 2])
.range([30, 40, 50]);
If you put your data into an array (or an array of arrays for multiple users) you could do something like this (assumed only one user):
d3.selectAll("text")
.data(profile)
.enter().append("text")
.attr("x", 10)
.attr("y", function(d, i) { return yScale(i); })
.text(function(d) { return d; });
If your "profile" array is ["Alexander the Great", "Greece"]
you will get just Name and Location together. If you include an element for Data of Birth, then you'll get all 3 fields.
Upvotes: 3