1274Days

1274 days since this post was last revised. Specific details are likely out of date.

SVG provides the ability to create a filter that can then be applied to HTML elements.

Until today, I’d never had the need to combine multiple SVG filters in one. Turns out there is a wonderfully simple way to achieve this with SVG.

After a little Googling, Stack Overflow turned up this nugget and once implemented I thought it was worth a little more explanation.

SVG filters actually form the basis of the CSS filters that are making their way into modern browsers. However, while CSS filters can only be used in the very latest browsers, SVG filters have a wider reach.

Consider an image:

queen@2x

OK, now suppose we want to blur that image and adjust the hue. For browsers that support CSS filters we can string a few CSS filters together:

.image {
    filter: blur(6px) hue-rotate(90deg);
}

In supporting browsers we get this:

queen@2x

What if we want to achieve the same effect with a SVG filter? There are two ways to achieve this. Either way, we need to create our filter with SVG. Let’s do that first. Here’s how an SVG filter might look with multiple filter effects:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <defs>
        <filter id="myfilter" x="0" y="0">  
            <feColorMatrix in="SourceGraphic" type="hueRotate" values="90" result="A"/>
            <feGaussianBlur in="A" stdDeviation="6"/>
        </filter>
    </defs>
</svg>

We just slap that in the DOM somewhere and set it to a height of zero so it doesn’t take up space in the DOM (you can’t use display: none; as then Firefox can’t render it). The important thing to note in that SVG is how the result of one filter is passed to another in the defs.

First we are rotating the hue and creating a result attribute with a value of A:

<feColorMatrix in="SourceGraphic" type="hueRotate" values="90" result="A"/>

Then the next filter can pick up that result value as the source for its work and add a blur to it:

<feGaussianBlur in="A" stdDeviation="6"/>

See the in="A" part there? That’s where it is picking up the result we specified in the first fe. You can chain things on and on with this technique and there are shed loads of different SVG filters you can use to get some bonkers effects. Microsoft has a good ‘playground’ page if you want to try some out.

Whatever the filter you create, once made, you should be able to just do this:

.thing {
 filter: url(‘#myfilter’);
}

That certainly works in Chrome. Open this example in Chrome to see. However, Firefox just renders the filtered image white. No idea why – at this point I’m assuming it’s because Firefox isn’t smart enough to look for the filter in the document the styles are being applied (I think it wants to see an absolute path from style sheet to ID in markup).

There is a workaround to this Firefox issue and that is sticking the filter into the CSS as a data uri:

filter: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"><filter id=\"myfilter\" x=\"0\" y=\"0\"><feColorMatrix in=\"SourceGraphic\" type=\"hueRotate\" values=\"90\" result=\"A\"/><feGaussianBlur in=\"A\" stdDeviation=\"6\"/></filter></svg>#myfilter');

It’s exactly the same SVG markup as we had in the HTML before but with all whitespace removed and quotes escaped.

That way works perfect in Firefox but sadly not Chrome/Safari. You could get it in both places by doing both (in the DOM for Safari/Chrome and as a data uri in the style sheet). However, there are better ways. Firstly it’s possible to use an absolute url in the style sheet (I’m using a localhost address here, amend that to your need):

filter: url('http://localhost:8888#myfilter');

Or add the relative filter reference in a style attribute of the actual page/template it is being applied:

<style>
.BlurMe {
    filter: url('#myfilter');
}
</style>

Either of those two options gets you the desired result; a custom multiple effect SVG filter you can apply to any HTML on your page.

It’s worth pointing out that although IE9+ supports SVG it doesn’t allow you to apply an SVG filter to an image in this manner. You can only achieve the same effect by adding an <image> inside the SVG itself. More succinctly, you can’t apply SVG filters via CSS in IE – even IE11.

Yet another option is to take the SVG out of the DOM and save it as an a file e.g. ‘filter.svg’. Then you can reference that SVG relatively:


filter: url('../img/filter.svg#myfilter');
queen@2x

That ‘reaches into’ the SVG and uses the ID within as a filter. That method also works in Firefox and Chrome/Safari.

Ben Frain Developer, Author: 'Enduring CSS', 'Responsive Web Design with HTML5 & CSS3'.