Question

I was playing with the clone() function from jQuery when I found out a strange behavior.

This code reproduces the problem. A first div contains an SVG. Two buttons allows doing/undoing a clone of the SVG into a second div. Trying it two times makes the circle to turn black.

enter image description here enter 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 with jQuery or Javascript leads to the same bug.
Was it helpful?

Solution 3

@Matt Gibson presents a hack: drumming up new ids and then having duplicated html with redundant linearGradient definitions referencing the original linearGradient.

Happily, you do you need to do this ;)

Part of the strength of the svg tags is that they're their own mad little containers. so you can reference stuff externally from an uri.

&So if you're in the cloning business, then instead of worrying about ids, you can abstract out the template model & re-use it 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's a fork of your jsfiddle.

OTHER TIPS

I have a hunch it may be because you're cloning the linearGradient, which has an id attribute (which should, of course, be unique throughout the document.) This might be confusing the SVG processor.

My theory seems to be borne out by changing the ID on the way through the clone:

$('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 appears to prevent the problem. (In this case the clone still gets a gradient even though the ID of its cloned gradient has changed, because it's finding the original gradient by its ID.)

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

I'm guessing that what's happening "under the hood" in your case is that the processor is picking up the second gradient filter element that you're creating for use with the first circle, and then losing its reference to it when it's destroyed by your empty(), thus leaving you with the circle but no gradient. Exactly why this only happens second time through is probably down to the implementation details of the SVG renderer's interactions with the HTML5 document.

There's more information on this issue in this earlier question.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top