Skip to content

Instantly share code, notes, and snippets.

@schmidt1024
Created June 3, 2015 13:59
Show Gist options
  • Save schmidt1024/61beeca2cce94a70c9df to your computer and use it in GitHub Desktop.
Save schmidt1024/61beeca2cce94a70c9df to your computer and use it in GitHub Desktop.
Replace all SVG images with inline SVG using jQuery
/*
* 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');
});
@z1haze
Copy link

z1haze commented Apr 19, 2018

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);
    })
  });

@engray
Copy link

engray commented May 29, 2018

@z1haze sadly it didn't work for me. I get Uncaught ReferenceError: request is not defined at document.querySelectorAll.forEach

@Third-Hyperion
Copy link

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.

@ndq11
Copy link

ndq11 commented Jun 20, 2018

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"); } });

@nikitatrifan
Copy link

nikitatrifan commented Jun 30, 2018

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}/>
        )
    }
}

@bondo11
Copy link

bondo11 commented Oct 26, 2018

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);
    }
}

@danielgroner
Copy link

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);
}

@usame-algan
Copy link

usame-algan commented Nov 12, 2019

@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);
          })
});

@amohamdy
Copy link

amohamdy commented Jan 2, 2020

this code doesn't work on internet explorer any solution for this issue??

@subfighter3
Copy link

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!

@panatapattu
Copy link

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.

@Warface
Copy link

Warface commented Jan 19, 2023

this code doesn't work on internet explorer any solution for this issue??

Yeah don't use Internet Explorer ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment