SVG goes black when cloned

I played with the clone() function from jQuery when I discovered strange behavior.

This code reproduces the problem. The first div contains SVG. Two buttons allow you to make / destroy an SVG clone in the second div. Repeating this twice, the circle turns black.

enter image description hereenter image description here

HTML

 <div id="orgdiv"> <svg width="200" height="200" style="margin:0"> <linearGradient id="r" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#00ffff"></stop> <stop offset="100%" stop-color="#ffff00"></stop> </linearGradient> <circle cx="100" cy="100" r="80" style="fill:url(#r)" /> </svg> </div> <input type="button" value="copy"> <input type="button" value="clear"> <div id="copydiv"></div> 

Js

 $('input[value="copy"]').click(function(){ $("#copydiv").html($("#orgdiv").clone()); }); $('input[value="clear"]').click(function(){ $("#copydiv").empty(); }); 

jsFiddle here

Note:

  • cloning using jQuery or Javascript results in the same error.
+6
source share
3 answers

I have a hunch that this could be because you are cloning a linear gradient that has an id attribute (which, of course, must be unique throughout the document). This can confuse the SVG processor.

My theory seems to be confirmed by changing the identifier on the clone path:

 $('input[value="copy"]').click(function () { var clone = $("#orgdiv").clone(); // Change the ID of the clone, so we don't have duplicate IDs clone.find("#r").attr('id', 'unique'); $("#copydiv").html(clone); }); 

... which seems to prevent the problem. (In this case, the clone still gets the gradient, even if the identifier of its cloned gradient has changed since it detects the original gradient by its identifier.)

JSFiddle here: http://jsfiddle.net/2K4xv/2/

I assume that what happens โ€œunder the hoodโ€ in your case is that the processor picks up the second gradient filter element that you create for use with the first circle, and then lose the link to it when it is destroyed by your empty() , thereby leaving you with a circle, but without a gradient. That is why this happens only for the second time, probably, down to the details of the implementation of SVG rendering of interaction with an HTML5 document.

More information on this in this earlier question .

+7
source

Once you clone it, you have two elements with the identifier "r", one in the original and one in the clone. All identifiers must be unique, so the document is no longer valid.

+2
source

@Matt Gibson introduces a hack: typing new identifiers, and then duplicates the html with redundant linearGradient definitions that reference the original linearGradient .

Fortunately, you need to do this;)

A distinctive feature of svg tags is that they are their own crazy little containers. therefore, you can reference material from outside from uri.

& So, if you work in cloning, instead of worrying about identifiers, you can abstract from the template model and reuse its ad-infinitum:

 $(document).ready(function(){ $('input[value="copy"]').click(function () { $("#copydiv").append($(":first", "#orgdiv").clone()); }); $('input[value="clear"]').click(function () { $("#copydiv").empty(); }); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <!-- ------------------- platonic horse ------------------------- --> <div style="visibility:hidden; position:absolute; width:0"> <svg> <g id="my-funky-svg-defs"> <defs> <radialGradient id="gradient" cx="25%" cy="25%" r="100%" fx="40%" fy="40%"> <stop offset= "0%" stop-color="hsla(313, 80%, 80%, 1)"/> <stop offset= "40%" stop-color="hsla(313, 100%, 65%, 1)"/> <stop offset="110%" stop-color="hsla(313, 100%, 50%, 0.7)"/> </radialGradient> </defs> <title>smarteee</title> <circle class="face" cx="200" cy="200" r="195" fill="url(#gradient)" /> <ellipse class="eye eye-left" cx="140" cy="150" rx="10" ry="40" fill="#131313"/> <ellipse class="eye eye-right" cx="260" cy="150" rx="10" ry="40" fill="#131313"/> <path class="smile" d="M120,280 Q200,330 280,280" stroke-width="10" stroke="#131313" fill="none" stroke-linecap="round"/> </g> </svg> </div> <!-- ---------------------- prototype ----------------------- ---- --> proto <input type="button" value="copy"/> <hr/> <div id="orgdiv"> <svg width="20px" height="20px" viewBox="0 0 400 400" style="margin:20px;"> <use xlink:href="#my-funky-svg-defs"></use> </svg> </div> <!-- ------------------------- clones ----------------------- ---- --> clones <input type="button" value="clear"/> <hr/> <div id="copydiv"></div> 

Also here is the fork of your jsfiddle .

+1
source

Source: https://habr.com/ru/post/956944/


All Articles