Reputation: 469
I have this SVG container with paths. I want to edit it, so the paths' fill will be a pattern. This is my failed attempt:
I add a gradient:
$('svg defs').prepend('<linearGradient id="MyGradient"><stop offset="5%" stop-color="#F60" /><stop offset="95%" stop-color="#FF6" /></linearGradient>');
And then change the paths' fill:
$(base + ' svg path').each(function() {
this.setAttribute('fill','url(#MyGradient)')
}
This doesn't work. What am I missing?
Upvotes: 10
Views: 17944
Reputation: 14729
I just want to drop by and say I have found a more elegant solution that allows you to keep using jQuery with SVG elements but without the jQuery SVG library (which is no longer being updated and has some problems with jQuery 1.8 or higher). Simply use a function like this:
createSVGElement= function(element) {
return $(document.createElementNS('http://www.w3.org/2000/svg', element));
}
it creates a SVG element on the SVG namespace and encapsulates it with jQuery, once the element is created in the right namespace you can use it freely with jQuery:
You can then use the function in this manner:
var $myGradient= createSVGElement('linearGradient')
.attr( {
id:"MyGradient"
});
//if you dont need `defs`, skip this next line
var $myDefs = createSVGElement('defs');
createSVGElement('stop')
.attr({
offset: "5%",
"stop-color": "#F60"
})
.appendTo($myGradient);
createSVGElement('stop')
.attr({
offset:"95%",
"stop-color":"#FF6"
})
.appendTo($myGradient);
//Use this if you already have `defs`
$('svg defs').prepend($myGradient);
//Use this if you dont have `defs`
$('svg').prepend($myDefs);
$('svg defs').prepend($myGradient);
It's not as compact as you might want it to be since you have to create each element by hand, but its a lot better than manipulating everything with DOM methods.
A small note, jQuery .attr() function assumes all attributes are lowercased, which is not the case for SVG elements (for example the viewBox
attribute in <svg>
tags). To get around that, when setting attributes with uppercased letters use something like this:
$("svg")[0].setAttribute("viewBox", "0 0 1000 1000");
Upvotes: 3
Reputation: 303301
Your problem (what you are "missing") is that jQuery creates new elements in the XHTML namespace, while SVG elements must be created in the SVG namespace. You cannot use raw code in a string for SVG elements.
The simplest (no-plugins) method is to stop leaning on jQuery so much and just use simple DOM methods to create the elements. Yes, it's more verbose than just using jQuery to magically construct your elements for you...but jQuery does not work in this case.
createGradient($('svg')[0],'MyGradient',[
{offset:'5%', 'stop-color':'#f60'},
{offset:'95%','stop-color':'#ff6'}
]);
$('svg path').attr('fill','url(#MyGradient)');
// svg: the owning <svg> element
// id: an id="..." attribute for the gradient
// stops: an array of objects with <stop> attributes
function createGradient(svg,id,stops){
var svgNS = svg.namespaceURI;
var grad = document.createElementNS(svgNS,'linearGradient');
grad.setAttribute('id',id);
for (var i=0;i<stops.length;i++){
var attrs = stops[i];
var stop = document.createElementNS(svgNS,'stop');
for (var attr in attrs){
if (attrs.hasOwnProperty(attr)) stop.setAttribute(attr,attrs[attr]);
}
grad.appendChild(stop);
}
var defs = svg.querySelector('defs') ||
svg.insertBefore( document.createElementNS(svgNS,'defs'), svg.firstChild);
return defs.appendChild(grad);
}
Alternatively, you can include Keith Woods' "jQuery SVG" plugin that has a lot of convenience methods for common SVG operations, including the ability to create linear gradients.
Upvotes: 24
Reputation: 74076
I think you'll have to use the SVG plugin for jQuery (found here). When adding SVG elements using the "normal" jQuery library, probably the namespaces get mixed up.
Try the following:
svg.linearGradient( $('svg defs'),
'MyGradient',
[ ['5%', '#F60'], ['95%', '#FF6']] );
(Not exactly sure, however. You might need to fiddle around a bit with that code.)
EDIT
Just created this fiddle in order to test the thesis (as suggested by @Phrogz). Indeed it returns http://www.w3.org/1999/xhtml
as the namespace for the inserted <linearGradient>
, which is the wrong namespace and thus validates my above speculation.
Upvotes: 3
Reputation: 469
Found a solution. It's a bit ugly, but doesn't require the use of additional plugins.
Apparently, a pattern has to be included in the tag when the SVG is first created (it's probably only read then).
Thus, replacing the SVG tag's wrapper's contents with themselves works (base
being that wrapper):
$(base).html($(base).html())
Upvotes: 3