Search code examples
javascriptjqueryhtmlsvgclone

SVG turns black when cloned


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.

Solution

  • @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.