Skip to content

Instantly share code, notes, and snippets.

@Kamshak Kamshak/README.md
Created Jan 18, 2019

Embed
What would you like to do?
Fixes SVG Gradients for Safari by changing fill URLs to the current pathname. Also fixes xlink:href URLs.

Snippet to fix SVG issues in Angular (2+) with Safari, Firefox and Chrome

This listens to Angular route changes and on route change does the following:

  1. Replaces the link in <use xlink:href="#some-id"></use> by a path prefixed version
  2. Replaces the fill property in referenced SVGs by a path prefixed version
  3. Replaces the style in <svg style="fill: url(#gradient)"> with a prefixed version

Adapted from Gist by Leon Derijke

ngOnInit() {
this.router.events
.pipe(
filter(x => x instanceof NavigationEnd),
takeUntil(this.destroy$)
)
.subscribe((evt: NavigationEnd) => {
this.zone.runOutsideAngular(() => {
setTimeout(() => {
if (!SERVER) { // you should disable this for HMR
setTimeout(() => {
this.changeSvgPaths();
}, 50);
}
}, 0);
});
});
}
/**
* SVG Fixer
*
* Fixes references to inline SVG elements when the <base> tag is in use.
* Firefox won't display SVG icons referenced with
* `<svg><use xlink:href="#id-of-icon-def"></use></svg>` when the <base> tag is on the page.
*
* More info:
* - http://stackoverflow.com/a/18265336/796152
* - http://www.w3.org/TR/SVG/linking.html
*
* One would think that setting the `xml:base` attribute fixes things,
* but that is being removed from the platform: https://code.google.com/p/chromium/issues/detail?id=341854
* https://gist.github.com/leonderijke/c5cf7c5b2e424c0061d2
*/
private changeSvgPaths() {
/**
* Current URL, without the hash
*/
let baseUrl = window.location.pathname
.replace(window.location.hash, '')
.replace(/\/$/gi, '');
/**
* Find all `use` elements with a namespaced `href` attribute, e.g.
* <use xlink:href="#some-id"></use>
*
*/
[].slice
.call(document.querySelectorAll('use[*|href]'))
/**
* Filter out all elements whose namespaced `href` attribute doesn't
* start with `#` (i.e. all non-relative IRI's)
*
* Note: we're assuming the `xlink` prefix for the XLink namespace!
*/
.filter(function(element) {
return (
element
.getAttributeNS('http://www.w3.org/1999/xlink', 'href')
.indexOf('#') !== -1
);
})
/**
* Prepend `window.location` to the namespaced `href` attribute value,
* in order to make it an absolute IRI
*
* Note: we're assuming the `xlink` prefix for the XLink namespace!
*/
.forEach(function(element: HTMLElement) {
const oldHref = element.getAttributeNS(
'http://www.w3.org/1999/xlink',
'href'
) as string;
const idPart = oldHref.slice(oldHref.indexOf('#'));
element.setAttributeNS(
'http://www.w3.org/1999/xlink',
'xlink:href',
baseUrl + idPart
);
});
[].slice
.call(document.querySelectorAll('svg'))
.filter(function(element: SVGElement) {
return (
'fill' in element.style && element.style.fill.indexOf('url(') !== -1
);
})
.forEach(function(element: SVGElement) {
let attrVal = element.style.fill;
element.style.fill = `url(${baseUrl}${attrVal.slice(
attrVal.indexOf('#')
)}`;
});
[].slice
.call(document.querySelectorAll('*[fill]'))
/**
* Filter out all elements whose namespaced `fill` attributes doesn't
* which doesnt have cross referenced values
*
* Note: we're assuming the `url(` prefix for the cross referenced fills !
*/
.filter(function(element) {
return element.getAttribute('fill').indexOf('url(') !== -1;
})
/**
* Insert `window.location` to the `fill` attribute value,
* in order to make it an absolute IRI
*
*/
.forEach(function(element) {
let attrVal = element.getAttribute('fill');
element.setAttribute(
'fill',
`url(${baseUrl}${attrVal.slice(attrVal.indexOf('#'))}`
);
});
}
@yacineblr

This comment has been minimized.

Copy link

yacineblr commented Aug 8, 2019

I'ts work for Angular 8, thanks you !!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.