Need some advice about a best-approach for this problem:
I've fallen madly in love with RaphaëlJS, as it has made SVG realizable for me and my coding since it has managed to bring IE into the fold.
However, I don't like having to include a .js
file for every SVG graphic I want to render on the page.
So, I poked around on the internet to see if I could find something that was more "dynamic", and I found this: (the below is my edited version of code I found here: http://groups.google.com/group/raphaeljs/msg/ce59df3d01736a6f)
function parseXML(xml) {
if (window.ActiveXObject && window.GetObject) {
var dom = new ActiveXObject('Microsoft.XMLDOM');
dom.loadXML(xml);
return dom;
}
if (window.DOMParser) {
return new DOMParser().parseFromString(xml, 'text/xml');
throw new Error('No XML parser available');
}
}
(function($) {
$.fn.render_raphaels = function(options) {
var defaults, options, counter, img_path, doc, root, vb, dims, img, node, path, atts, container, new_svg, inline;
defaults = {};
options = $.extend(defaults, options);
counter = 0;
inline = false;
// find all the img's that point to SVGs
$('img[src*="\.svg"]').each(function() {
$(this).fadeOut(1000);
img_path = $(this).attr('src');
if (!$(this).attr('id')) new_svg = true;
if ($(this).hasClass('inline')) inline = true;
container = jQuery('<div/>', {
id: $(this).attr('id') ? $(this).attr('id') : 'svg-' + (counter + 1),
'class': $(this).attr('class') ? $(this).attr('class') : 'svg'
}).hide().insertBefore($(this));
$.get(img_path, null, function(doc) {
doc = parseXML(doc);
root = $(doc).find('svg')[0];
dims = [root.getAttribute('width'), root.getAttribute('height')];
if(new_svg) container.css({ width: dims[0], height: dims[1] });
if(inline) container.css('display', 'inline-block');
img = Raphael(container.attr('id'), parseInt(dims[0]), parseInt(dims[1]));
$(root).find('path').each(function() {
node = this;
path = img.path($(this).attr('d'));
$(['stroke-linejoin','stroke','stroke-miterlimit','stroke-width','fill','stroke-linecap']).each(function() {
if($(node).attr(this.toString())) {
path.attr(this, $(node).attr(this.toString()));
} else {
path.attr(this, 0);
}
});
if($(node).attr('style')) {
atts = $(node).attr('style').split(';');
for(var i=0; i < atts.length; i++) {
bits = atts[i].split(':');
path.attr(bits[0],bits[1]);
}
}
});
}, 'text');
$(this).remove(); // removes the original image after the new one has been redrawn
container.fadeIn(2000);
});
};
})(jQuery);
In essence, this allows me to just write a normal image tag with the .svg
graphic, and the jQuery plugin will automatically replace it with the Raphaël-rendered version.
This works great in non-SVG compliant browsers, like IE, but in modern browsers that actually already support SVG graphics, the image tag works as-is (without Raphaël), so when Raphaël loads, it unloads the existing image, then fades in the Raphaël version... essentially creating a flicker. I tried to downplay this by fading in the new version, but I'm still faced with the problem that the old one shows, is hidden, then is shown again.
I need a way to reconcile the desired behavior in the problematic browsers like IE and the undesired behavior in the modern, standards-compliant browsers like Safari 4 and Firefox 3. But, I want to do this in a manner that I don't have to significantly change the way I code (why I use the plugin in the first place).
I know that SVG is still a bit cutting edge, but does anyone have any thoughts about how I can get around this?
DISCLAIMER: If at all possible, I'd like to stay away from browser targeting... I'm looking for a manageable and functional workflow solution, not a browser hack.
2ND DISCLAIMER: I don't want a solution based on Flash; I want to be as "native" as possible, and I regard javascript to be much more so than Flash. (This is why I'm so excited about Raphaël, because I can stay away from Flash).