Question

I'm creating a specific implementation for a d3.js graph, and it seems that when creating an SVG document dynamically, linearGradients are not working on Webkit browsers (tested only on Chrome, of the Webkit family), while Gecko is showing the expected behavior.

How do I conclude it has to do with dynamic generation, or d3? I tried copying the resulted document into an empty page, and the gradient came to life.

The d3 code initializes the document first:

// in construction:
// ...
svg = d3.select(el)
    .append('svg:svg')
    .attr('width', width)
    .attr('height', height),
defs = svg.append('svg:defs'),
linearGradient1 = defs.append('svg:linearGradient')
    .attr('id', 'linear-gradient-1')
    .attr('x1', 0)
    .attr('y1', 1)
    .attr('x2', 6.123233995736766e-17)
    .attr('y2', 0),
linearGradient1Stop1 = linearGradient1.append('svg:stop')
    .attr('offset', '0%')
    .attr('stop-color', '#e3e5e8'),
    // several other stops here ...

… than refreshes the renderer, e.g. after adding "nodes" to the graph data struct, on demand (note: selfRef.node is simply a handle to the d3 selector of all the nodes):

// called e.g. when adding new nodes:
refresh: function() {
    // ...
    // add new nodes
    var g = selfRef.node.enter().append('svg:g').attr('class', 'node');

    g.append('svg:rect')
        .attr('width', 144)
        .attr('height', 42)
        .attr('rx', 3)
        .attr('ry', 3)
        .attr('fill', 'url(#linear-gradient-1)')
        .style('stroke', '#4d4d4d')
        .style('stroke-width', 1)

    // ...
}

Here's the generated document, it's nothing special:

<svg width="1889" height="400">
    <defs>
        <linearGradient id="linear-gradient-1" x1="0" y1="1" x2="6.123233995736766e-17" y2="0">
            <stop offset="0%" stop-color="#e3e5e8"></stop>
            <stop offset="11%" stop-color="#e6e8ec"></stop>
            <stop offset="59%" stop-color="#eff2fa"></stop>
            <stop offset="100%" stop-opacity="0.6" stop-color="#f2f6ff"></stop>
        </linearGradient>
    </defs>
    <g>
        <g class="node" 
           transform="translate(1113.425033222223,312.1412317958371)">
            <rect width="144" height="42" rx="3" ry="3" 
                  fill="url(#linear-gradient-1)" 
                  style="stroke: #4d4d4d; stroke-width: 1px;"></rect>
            <!-- some other shapes of the node... -->
        </g>
        <!-- etc. etc., some more node groups here... -->
    </g>
</svg>

Things I have tried

  • Pulling the element referencing the gradient outside any group.
  • Adding version declaration to the SVG with varying values (maybe it only appears on 1.1?)
  • Wrapping the reference in quotes (i.e. fill="url('#linear-gradient-1')") or omitting the dashes from the id, thinking Webkit is less lenient here.

Note

Robert Longson mentioned that case matters in this post, and, weird enough, it seems that when pasting the document in an empty page (in the Chrome's dev-tools), the linear gradient element transforms to camel-case format (though it doesn't show in the DOM view, all is lowercase there). I discovered this after diffing the results of my d3 generated code and the pasted static document. What's up with that?


TL;DR

How come generating dynamic SVG gradients don't work when running in Chrome, and how to fix this?

Was it helpful?

Solution

Seems like the Safari developers marked the bug as works for me, based on it working in xhtml. For Safari it seems you'd have to serve your webpages with a mime type of application/xhtml+xml or petition the Safari developers to reopen the bug. FWIW it seems like a bug to me.

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