aug
aug

Reputation: 11714

SVG Text responsive positioning

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:

enter image description here

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

Answers (3)

cuixiping
cuixiping

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

t.888
t.888

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

Scott Cameron
Scott Cameron

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

Related Questions