Close

How to fix SVG end-marker attribute when using base url?

[Last Updated: May 18, 2018]

SVG JavaScript 

The problem

When using svg elements along with "marker-end = url()" attributes and you page also contains <head><base href="....." /></head> then you might have problems getting intended results.

In my case I was trying to defined an arrow head like this:

<defs>
<marker id='my_arrow' refX='0' refY='2' markerWidth='4' markerHeight='4'
orient='auto'>
<path d='M 0 0 L 4 2 L 0 4 z' fill='#666'/>
</marker>
</defs>

and then I was trying to attach the arrow to a line:

<line ... marker-end='url(#my_arrow)' />

The HTML <base> element is used to resolve all relative URLs to a specified location.

In some browsers, SVG marker-end="url(....) attribute is resolved relative to the "base" URL. If you have included base URL in your page then you might not get the expected results in some browsers.

In my case, i was having the problems with windows Edge and Firefox 50.

Chrome 54 and Opera 41 were working fine.


The fix

I tried to resolve the issue by pointing to external svg file where my marker was defined:

marker-end="url(/head.svg#my_arrow)"></line>

Here I encountered another issue, all browsers don't support external svg urls like above. This issue was opposite to my original issue: Edge and Firefox were accepting external svg url, whereas, Chrome and Opera where not.

The final fix I came up with: point the svg url references within the HTML page itself (what I was doing initially) and then with the help of Javascript/Jquery, change the url to the external ones if it's a problematic browser:

Within my html page

<defs>
<marker id='my_arrow' refX='0' refY='2' markerWidth='4' markerHeight='4'
orient='auto'>
<path d='M 0 0 L 4 2 L 0 4 z' fill='#666'/>
</marker>
</defs>

<line ... marker-end='url(#my_arrow)' />

I also saved 'head.svg' file at the root of my domain containing the same 'defs' part as above:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<defs>
<marker id='arrow_head' refX='0' refY='2' markerWidth='4' markerHeight='4' orient='auto'>
<path d='M 0 0 L 4 2 L 0 4 z' fill='#666'/>
</marker>
</defs>
</svg>

Javascript:

$( document ).ready(function() {
fixSvgEndMarkers();
});

function fixSvgEndMarkers(){
var browser = getBrowser();

if(!browser.toLowerCase().includes("chrome") &&
!browser.toLowerCase().includes("opera")){
$('line[marker-end]').each(function(){
$(this).attr('marker-end', 'url(/head.svg#arrow_head)');
});
}
}

function getBrowser(){
var ua= navigator.userAgent;
var b = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
var temp = '';
if(/trident/i.test(b[1])){
temp= /\brv[ :]+(\d+)/g.exec(ua) || [];
return 'IE '+(temp[1] || '');
}
if(b[1]=== 'Chrome'){
temp= ua.match(/\b(OPR|Edge)\/(\d+)/);
if(temp!= null) return temp.slice(1).join(' ').replace('OPR', 'Opera');
}
b= b[2]? [b[1], b[2]]: [navigator.appName, navigator.appVersion, '-?'];
if((temp= ua.match(/version\/(\d+)/i))!= null) b.splice(1, 1, temp[1]);
return b.join(' ');
}


This worked in my case. If you have the similar problem then this fix might help you. You may need to tweak JavaScript logic a little, based on your problem as you might have another issue related to 'base url' and svg elements.