-
-
Save cod3cow/61beeca2cce94a70c9df to your computer and use it in GitHub Desktop.
/* | |
* Replace all SVG images with inline SVG | |
*/ | |
jQuery('img.svg').each(function(){ | |
var $img = jQuery(this); | |
var imgID = $img.attr('id'); | |
var imgClass = $img.attr('class'); | |
var imgURL = $img.attr('src'); | |
jQuery.get(imgURL, function(data) { | |
// Get the SVG tag, ignore the rest | |
var $svg = jQuery(data).find('svg'); | |
// Add replaced image's ID to the new SVG | |
if(typeof imgID !== 'undefined') { | |
$svg = $svg.attr('id', imgID); | |
} | |
// Add replaced image's classes to the new SVG | |
if(typeof imgClass !== 'undefined') { | |
$svg = $svg.attr('class', imgClass+' replaced-svg'); | |
} | |
// Remove any invalid XML tags as per http://validator.w3.org | |
$svg = $svg.removeAttr('xmlns:a'); | |
// Replace image with new SVG | |
$img.replaceWith($svg); | |
}, 'xml'); | |
}); |
wrote this in plain js for anyone who wants it.
document.querySelectorAll('img.svg').forEach((el) => {
const imgID = el.getAttribute('id');
const imgClass = el.getAttribute('class');
const imgURL = el.getAttribute('src');
request({url: imgURL}).then((data) => {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(data, 'text/html');
let svg = xmlDoc.querySelector('svg');
if (typeof imgID !== 'undefined') {
svg.setAttribute('id', imgID);
}
if(typeof imgClass !== 'undefined') {
svg.setAttribute('class', imgClass + ' replaced-svg');
}
svg.removeAttribute('xmlns:a');
el.parentNode.replaceChild(svg, el);
})
});
@z1haze sadly it didn't work for me. I get Uncaught ReferenceError: request is not defined at document.querySelectorAll.forEach
Is there anyway to get this to work with external url sources? I have my images pulled from an external subdomain and for some reason this does not work when using an external URL. Is it possible so that either local or external url source can be used? Thank you.
I have an issue on FF:
on ajax call browser throws XML Parsing Error: not well-formed
.
I've already tried jQuery ajax call (instead of jQuery.get) with predefined headers, dataType, contentType and overriding mimeType, but it doesn't work either.
Server returns image with the same Content-Type: "image/svg+xml;charset=UTF-8".
Image is loaded.
But with parsing error.
This problem occurs only on FF.
Maybe somebody will have any idea?
$.ajax({ url: imgURL, contentType: "image/svg+xml; charset=UTF-8", headers: { Accept: "image/svg+xml; charset=UTF-8" }, success: success, dataType: 'xml', beforeSend: function (xhr) { xhr.overrideMimeType("image/svg+xml; charset=UTF-8"); } });
Hi guys! For one of my projects which based on create react app
I had to use SVG. And I did not want to eject webpack settings.
So I did a new implementation of the idea: Replace image with inline SVG using React.
Check this out:
import React from 'react'
import PropTypes from 'prop-types'
import { isEmpty } from 'lodash'
export default class Svg extends React.Component {
static propTypes = {
src: PropTypes.string.isRequired,
className: PropTypes.string,
alt: PropTypes.string
};
static defaultProps = {
alt: ''
};
storageName = '@storage/svg';
constructor(props) {
super(props);
// set up initial data
const { src } = props;
const markup = this.storage(src);
const isLoaded = !isEmpty(markup);
this.state = {
markup, isLoaded
};
// if we have no image in a cache
// then load svg
if (!isLoaded)
this.loadSvg(src);
}
storage = (src, markup) => {
const storage = JSON.parse(localStorage.getItem(this.storageName)) || {};
if (isEmpty(markup))
return storage[src];
localStorage.setItem(this.storageName, JSON.stringify({
...storage,
[src]: markup
}))
};
loadSvg = src => {
fetch(src)
.then(res => res.text())
.then(markup => {
// save svg markup to local storage
this.storage(src, markup);
// show svg :)
this.setState({
markup, isLoaded: true
})
})
.catch(err => {
console.warn(
`Can't fetch svg image ${src} because of: \n\n${err.toString()}`
)
})
};
render() {
const { src, alt = '', ...props } = this.props;
const { isLoaded, markup } = this.state;
if (isLoaded) {
return (
<div {...props} dangerouslySetInnerHTML={{ __html: markup }}/>
)
}
return (
<img src={src} alt={alt} {...props}/>
)
}
}
and here is my take on it, in typescript, where it is unittestable
use it like this:
new InlineSvg().execute();
and add the class "svg-inline", to the svgs you want to inline
export default class InlineSvg {
private imageElements: HTMLImageElement[];
private svgElements: HTMLImageElement[];
private classToReplace: string = "svg-inline";
constructor() {
this.imageElements = Array.from(document.querySelectorAll<HTMLImageElement>("img")) as HTMLImageElement[];
this.svgElements = this.imageElements.filter((x: HTMLImageElement) =>x.classList.contains(this.classToReplace));
}
execute(): void {
this.svgElements.forEach((x: HTMLImageElement) => {
this.getSvg(x, x.src);
});
}
replace(source: HTMLImageElement, target: HTMLElement): void {
// add replaced image's ID to the new SVG
if(typeof source.id !== "undefined") {
target.id = source.id;
}
// add replaced image's classes to the new SVG
source.classList.forEach((x: string) => {
if(x===this.classToReplace) { return; }
target.classList.add(x);
});
target.classList.add("replaced-svg");
// remove any invalid XML tags as per http://validator.w3.org
target.removeAttribute("xmlns:a");
// replace image with new SVG
source.replaceWith(target);
}
getSvg(source: HTMLImageElement, url: string): void {
const http: XMLHttpRequest = new XMLHttpRequest();
http.open("GET", url, true);
http.setRequestHeader("Content-Type", "text/xml; charset=UTF-8");
http.onload = () => {
if(http.readyState === http.DONE && http.status === 200) {
const target: HTMLElement = http.responseXML.documentElement;
this.replace(source, target);
}
};
http.send(null);
}
}
Another simple but important addition could be to also get the replaced image's alt tag.
var imgAlt = $img.attr('alt');
// add replaced image's alt tag to the new SVG
if(typeof imgAlt !== 'undefined') {
$svg = $svg.attr('alt', imgAlt);
}
@z1haze @engray I've adjusted the plain Javascript version to work with fetch.
document.querySelectorAll('img.svg').forEach((el) => {
const imgID = el.getAttribute('id');
const imgClass = el.getAttribute('class');
const imgURL = el.getAttribute('src');
fetch(imgURL)
.then(data => data.text())
.then(response => {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(response, 'text/html');
let svg = xmlDoc.querySelector('svg');
if (typeof imgID !== 'undefined') {
svg.setAttribute('id', imgID);
}
if(typeof imgClass !== 'undefined') {
svg.setAttribute('class', imgClass + ' replaced-svg');
}
svg.removeAttribute('xmlns:a');
el.parentNode.replaceChild(svg, el);
})
});
this code doesn't work on internet explorer any solution for this issue??
Great code btw, really useful.
If you need it in ES6:$('img.svg').each((i, e) => { const $img = $(e); const imgID = $img.attr('id'); const imgClass = $img.attr('class'); const imgURL = $img.attr('src'); $.get(imgURL, (data) => { // Get the SVG tag, ignore the rest let $svg = $(data).find('svg'); // Add replaced image's ID to the new SVG if (typeof imgID !== 'undefined') { $svg = $svg.attr('id', imgID); } // Add replaced image's classes to the new SVG if (typeof imgClass !== 'undefined') { $svg = $svg.attr('class', `${imgClass}replaced-svg`); } // Remove any invalid XML tags as per http://validator.w3.org $svg = $svg.removeAttr('xmlns:a'); // Check if the viewport is set, if the viewport is not set the SVG wont't scale. if (!$svg.attr('viewBox') && $svg.attr('height') && $svg.attr('width')) { $svg.attr(`viewBox 0 0 ${$svg.attr('height')} ${$svg.attr('width')}`); } // Replace image with new SVG $img.replaceWith($svg); }, 'xml'); });
Thank you @VinceVachon, I found a small error here:
$svg = $svg.attr("class",
${imgClass}replaced-svg);
I added a space between the original classes and "replaced-img" new class:
$svg = $svg.attr("class",
${imgClass} replaced-svg);
While the plain JS version provided by @usame-algan gives me an error: "fetch(...) is undefined" and is not working for me...
Anyway thank everybody for contributing on this useful tool!
This is a great solution. Is there any way to use this with owl carousel? When new carousel items loading, those are not rendering to SVGs.
this code doesn't work on internet explorer any solution for this issue??
Yeah don't use Internet Explorer ;)
hello...this code is working finely but i want it to run on second page dynamicaly of my project in ionic2