Search code examples
htmlcsssvgdata-urisvg-filters

Inlining SVG filters as data URI


I'm trying to add a SVG filter using a data URI in the CSS but cannot get the effects applied to my image.

It seems that it should be supported as both data URI and SVG filters are supported in all major browser according to "caniuse". The filter works just fine if I save it as a SVG file and link to the file in the css instead.

I've tried with encoding the SVG as base64 and url encoding the file as well but to no avail.

The following is a example of such filter making the image to which the filter is applied black and white.

<svg xmlns="http://www.w3.org/2000/svg">
    <filter id="light">
        <feColorMatrix type="matrix" result="grayscale--light"
            values="1 0 0 0 0
                    1 0 0 0 0
                    1 0 0 0 0
                    0 0 0 1 0">
        </feColorMatrix>
    </filter>
</svg>

After encoding the SVG I create a class with the following style

.grayscale--light {
    filter: url("data:image/svg+xml;base64,base64encodedString")
}

And in my HTML

<img src="/path/to/my/image.jpg" class="grayscale--light">

Am I doing something horribly wrong or is it not supported anymore? All articles I find on the topic is from around 2014.


Solution

  • First of all, you would have to reference the filter id at the end of the data URI, like so: ...VyPg0KPC9zdmc+#filterID). Now it works in Firefox, but not in Chrome which throws an error:

    Older versions of Chrome may throw an error (thanks @Daniel Weiner):

    Unsafe attempt to load URL (...) from frame with URL <URL>. Domains, protocols and ports must match.
    

    So Chrome doesn't like the data URI, and wants a proper URL with a domain that matches the current site. There is a way to do that: Instead of data-URI-encoding the SVG, put it in a Blob and generate a URL for it in JavaScript:

    const svg = '<svg xmlns="http://www.w3.org/2000/svg"><filter id="filterID"> ... </svg>',
          blob = new Blob([svg], { type: 'image/svg+xml' }),
          url = URL.createObjectURL(blob);
    
    myImage.style.filter = `url('${url}#filterID')`;
    

    https://jsfiddle.net/5ec0z2rq/