Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SVGs inside iframes having strange inconsistency problems

I need to render some SVGs inside iframes, and I need to do it using the srcdoc attribute. For some reason, something about the way I'm doing it is causing certain attributes of the SVG to not display properly.

In the screenshot below, the code in the iframe is identical.

screenshot

Here's the code to reproduce this:

<svg height="150" width="300">
  <defs>
    <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
    </linearGradient>
  </defs>
  <ellipse cx="100" cy="70" rx="85" ry="55" fill="url(#grad1)" />
</svg>

<br><br>

<iframe width="300" height="150" srcdoc='<svg height="150" width="300">
  <defs>
    <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
    </linearGradient>
  </defs>
  <ellipse cx="100" cy="70" rx="85" ry="55" fill="url(#grad1)" />
</svg>'></iframe>

It looks like it has something to do with linearGradients, or with urls, and how they behave in this situation.

I was recently having some other problems with iframes and srcdocs, and I posted this on SO, and got a good response: iframes rendering mysteriously differently from regular web pages? -- this helped with a lot of inconsistencies, but does not seem related to this one in particular.

I'm using Chrome/Webkit.

like image 716
MikeC8 Avatar asked Nov 01 '14 22:11

MikeC8


1 Answers

This appears to be an unfortunate result of how iframe srcdoc documents resolve internal target links.

Documents created using srcdoc are supposed to be given the special base URL about:srcdoc.

However, within that document, the base URL for resolving external links or stylesheets is the parent document's URL. Which could mean that the ID should be resolved within the parent DOM instead of the local DOM. But that's clearly not happening, since in your original example there was a valid gradient with that ID in the parent document. Now, that could be a result of cross-origin restrictions blocking resources from a separate document, but I'm not getting corresponding errors on the console.

Things get really wonky (and cross-browser inconsistent) when you start using target fragments for other things.

In this fiddle, reproduced as a snippet below, I use target fragments to

  • Reference the gradients from both documents (one for stroke and one for fill; neither luck in either Chrome (v38) or Firefox (v33) (IE doesn't support srcdoc at all).

  • Re-use elements from both documents; neither <use> renders in FF, Chrome renders the local ellipse re-use, indicating in this case it resolves the fragment according to the local document.

  • Define the destination of a hyperlink. In this case, FF gives me a POST error, but Chrome successfully navigates to the target in the parent document (note that the :target styles get applied). Of course, that only works if the parent document has a navigable URL (i.e., a saved JS Fiddle, but not a stack snippet).

All of which is to say that, this is an absolute mess of ambiguity in the specs and inconsistency in the implementations, and I would recommend finding an alternative, such as using a data URI as the src of the iframe. Be aware that this still won't work in IE (which doesn't support data URIs for iframes), and that you'll need to URL-encode any # and % characters within the document.

iframe srcdoc and URL fragments test case

ellipse:target {
    stroke:red;
}
<svg height="150" width="300">
  <defs>
    <linearGradient id="grad1" >
      <stop offset="0" style="stop-color:rgb(255,255,0);stop-opacity:1" />
      <stop offset="1" style="stop-color:rgb(255,0,0);stop-opacity:1" />
    </linearGradient>
  </defs>
  <ellipse id="parentEllipse" cx="100" cy="70" rx="85" ry="55" fill="url(#grad1)" />
</svg>

<br><br>

<iframe width="300" height="150" srcdoc='<svg height="150" width="300">
  <defs>
    <linearGradient id="grad2" >
      <stop offset="0" style="stop-color:rgb(255,255,0);stop-opacity:1" />
      <stop offset="1" style="stop-color:rgb(255,0,0);stop-opacity:1" />
    </linearGradient>
  </defs>
<a xlink:href="#parentEllipse">
  <ellipse id="ellipse" cx="100" cy="70" rx="85" ry="55" fill="url(#grad1) blue"  stroke="url(#grad2) green" stroke-width="10px" /></a>
  <use xlink:href="#ellipse" y="50"/>
  <use xlink:href="#parentEllipse" x="50" />
</svg>'></iframe>
like image 193
AmeliaBR Avatar answered Dec 04 '22 19:12

AmeliaBR