Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML element inside SVG not displayed [duplicate]

I am struggeling with the foreignObject in SVG. I want to add text inside a rect, and to get auto text wrap I have chosen to use HTML. The description of foreignObject can be found here.

I am working with D3, and this is my data:

var cds = [
{"uid":"U12","sid":"16","statement":"Movies","x":10,"y":10},
{"uid":"U20","sid":"17","statement":"Food","x":10,"y":170},
{"uid":"U22","sid":"15","statement":"Sport","x":10,"y":330}
];

I add a card for each datum and want to display the "statement" from the data.

var cardsize = { width : 150, height : 150 };

var svg = d3.select("body").append("svg:svg")
    .attr("width", 170)
    .attr("height", 490);

var card =  svg.selectAll("rect")
    .data(cds)
    .enter().append("svg:g")
    .attr("class", "card")
    .attr("transform", function(d,i) { 
        d.x = 10;
        d.y = 10+i*(10+cardsize.height);
        return "translate(" + d.x + "," + d.y + ")";
    });

card.append("svg:rect")
    .attr('width', cardsize.width)
    .attr('height', cardsize.height)

card.append("svg:foreignObject")
    .attr('width', cardsize.width)
    .attr('height', cardsize.height)
    .append('p')
    .attr("class","statement")
    .text(function(d) { return d.statement; });

The output of a card is this:

<g class="card" transform="translate(10,330)">
    <rect width="150" height="150"></rect>
    <foreignObject width="150" height="150"><
        p class="statement">Sport</p>
    </foreignObject>
</g>

I can't see the text in the (mac) browsers I have tested (Safari 6.0.2, Chrome 25.0.1364.99, Firefox 18.0.2). W3's validator doesn't like the output, and give me this error for each card:

Element foreignObject not allowed as child of element g in this context.

So my question is, where is the problem, and why is foreignObject not allowed as a child of a g element?

I am also wondering why .html(function(d) { return d.statement; }); doesn't work (no output). But .text(function(d) { return d.statement; }); works fine?

Here is a fiddle.

like image 486
swenedo Avatar asked Mar 01 '13 00:03

swenedo


People also ask

Can you put HTML inside SVG?

This can be solved by using the <foreignObject> SVG element in which you can add an HTML under a different namespace than the parent SVG and then you can also style the elements using CSS like so.

Can svgs be nested?

The SVG format allows for the nesting of SVG graphics. It is possible for an “<svg>” elements, to be placed within another “<svg>” element.

Can you put SVG inside I tag?

To embed an SVG via an <img> element, you just need to reference it in the src attribute as you'd expect. You will need a height or a width attribute (or both if your SVG has no inherent aspect ratio). If you have not already done so, please read Images in HTML.

Can svgs have links?

It is very similar to HTML's <a> element. SVG's <a> element is a container, which means you can create a link around text (like in HTML) but also around any shape.


1 Answers

Short answer

Specify the namespace in all tags in a foreignObject: replace .append("p") by .append("xhtml:p") and it will work: http://jsfiddle.net/EHzcR/2/


Long answer

In the context of foreign objects, <p></p> is not recognized as a xhtml paragraph. Why? Simply because the namespace xhtml is not included in the foreignObject context (a foreignObject might contain anything (xml formated data for example) so it doesn't consider the input as html if you don't explicitly say it.).

So the p tag is considered as a custom tag, not as a xhtml tag. Thus, as the web browser doesn't recognize the tag p in the tags it knows, so it just doesn't interpret it and its content, even if it exists, it won't throw an error as well because this content might be totally valid, just not for its direct use.

It is fun to see that you always specify the namespace svg even if it is not necessary and how we all forget that the p, div... also have a very popular namespace.

The selection.html() function doesn't work because in its implementation it uses the .innerHTML() function of the element, but here, the foreignObject, not being an html element, doesn't have such a function.

like image 161
Christopher Chiche Avatar answered Nov 15 '22 15:11

Christopher Chiche