Cross-Browser Grayscale image example using CSS3 + JS
There are a lot of examples and tutorials about how to create grayscale images using CSS , CSS3 and JS, but while developing a project for one of my clients, I found out that there is no solution that would be cross-browser compatible and would support the latest Internet Explorer 10 and 11 versions. This is the reason I came up with a solution of my own and decided to share it with you.
Update: This tutorial uses browser user agent detection which is fully functional but has been deprecated for a while so I have created another grayscale image tutorial that uses browser feature detection.
Grayscale and Internet explorer 10, 11
Grayscale filter has been natively supported by Internet Explorer since version 6, but recently Microsoft decided to remove this native CSS filter and since version 10, IE does not display grayscale images using the old technique.
This is the reason why we now have to turn to grayscale JS solution. Besides this, Microsoft has released the new IE11, which is trying to hide as a different browser. Now Internet Explorer 11 has changed its user-agent and pretends to be a Gecko or WebKit browser. This is a pretty nifty move by Microsoft in order to render new IE11 on most browsers as a Gecko browser and not like the previous versions with MSIE user-agent (we all know that there are a lot of websites that check for MSIE user-agent and bring up non-standard markup). So I had to overcome this new problem as well – checking the user-agent and firing only the SVG (Scalable Vector Graphics) grayscale JS solution that is intended for IE10+ (IE6-9 support CSS grayscale).
Grayscale on Opera and Safari
Opera and Safari browsers do not support CSS grayscale filters so these browsers must be treated separately just like IE10+. Only these browsers can be fixed using JS solution which has been kindly provided by James Padolsey
Cross-Browser Grayscale image solution
Include jQuery library
<script type='text/javascript' src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
This is the content of the HTML document – a simple container with images
<div class="image-container"> <img src="images/image-1.jpg" alt="" title=""/> <img src="images/image-2.jpg" alt="" title=""/> <img src="images/image-3.jpg" alt="" title=""/> </div>
Firefox 3.5+, Chrome, Internet explorer 6 – 9, Microsoft Edge
These browsers will be happy to serve us with CSS grayscale filters. Simply add these CSS lines in your stylesheet
img { filter: url('../js/filters.svg#grayscale'); /* Firefox 3.5+ */ filter: gray; /* IE6-9 */ -webkit-filter: grayscale(1); /* Webkit Nightlies, Google Chrome Canary and Microsoft Edge*/ } img:hover { filter: none; /* Applies to FF + IE */ -webkit-filter: grayscale(0); }
Opera 9+, Safari 4+, Internet Explorer 10+
As mentioned before, Opera and Safari browsers need to grayscale solution that uses JS
<script type='text/javascript' src='js/grayscale.js'></script> <script type='text/javascript' src="js/functions.js"></script>
That’s all about my cross-browser grayscale image solution setup. You can check out the demo or download the source. For those of you who want to get into more details, I have explained the magic behind the functions.js file, so that you could tweak the code as you need.
Tested and should work on:
- Firefox 3.5+
- Chrome, Safari 4+
- Opera 9+
- Internet Explorer 7, 8, 9, 10 and 11 (IE11)
- Microsoft Edge
View the demo or Download source
Update: This tutorial uses browser user agent detection which is fully functional but has been deprecated for a while so I have created another grayscale image tutorial that uses browser feature detection.
Explanation and contents of functions.js file
This function getBrowser() checks the browser user agent and after that adds corresponding class to the <body> tag – this way we can target browser with CSS or fire JS functions only on those browsers that we want (Except IE11 because of the new user-agent)
// Detection function to tell what kind of browser is used function getBrowser(){ var userAgent = navigator.userAgent.toLowerCase(); $.browser.chrome = /chrome/.test(userAgent); $.browser.safari= /webkit/.test(userAgent); $.browser.opera=/opera/.test(userAgent); $.browser.msie=/msie/.test( userAgent ) && !/opera/.test( userAgent ); $.browser.mozilla= /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent ) || /firefox/.test(userAgent); if($.browser.chrome) return "chrome"; if($.browser.mozilla) return "mozilla"; if($.browser.opera) return "opera"; if($.browser.safari) return "safari"; if($.browser.msie) return "ie"; }; // This block simply ads a corresponding class to the body tag so that we can target browsers with CSS classes if(getBrowser()=='mozilla'){ $('body').addClass('mozilla'); } else if(getBrowser()=='ie'){ $('body').addClass('ie'); } else if(getBrowser()=='opera'){ $('body').addClass('opera'); } else if (getBrowser()=='safari'){ // safari $('body').addClass('safari'); } else if(getBrowser()=='chrome'){ $('body').addClass('chrome'); };
Since IE11 cannot be detected like this because the new user agent on IE11 is trying to hide as Mozilla, we detect IE11 with this function
function getInternetExplorerVersion(){ var rv = -1; if (navigator.appName == 'Microsoft Internet Explorer'){ var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) rv = parseFloat( RegExp.$1 ); } else if (navigator.appName == 'Netscape'){ var ua = navigator.userAgent; var re = new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) rv = parseFloat( RegExp.$1 ); } return rv; }; if (getInternetExplorerVersion() >= 10){ $('body').addClass('ie11'); };
After we have created a way to tell what kind of browser is used by the user, we can start to target Grayscale image solutions with JS to the browsers that need it. I will start with Opera and Safari
// Grayscale images on Safari and Opera browsers if(getBrowser()=='opera' || getBrowser()=='safari'){ var $images = $("img") , imageCount = $images.length , counter = 0; // One instead of on, because it need only fire once per image $images.one("load",function(){ // increment counter every time an image finishes loading counter++; if (counter == imageCount) { // do stuff when all have loaded grayscale($('img')); $("img").hover(function () { grayscale.reset($(this)); }, function () { grayscale($(this)); } ); } }).each(function () { if (this.complete) { // manually trigger load event in event of a cache pull $(this).trigger("load"); } }); };
After this, all that is left is to deal with IE10+ browsers and these lines of JS do exactly that (using SVG JS solution)
// Grayscale images only on browsers IE10+ since they removed support for CSS grayscale filter if (getInternetExplorerVersion() >= 10){ $('img').each(function(){ var el = $(this); el.css({"position":"absolute"}).wrap("<div class='img_wrapper' style='display: inline-block'>").clone().addClass('img_grayscale').css({"position":"absolute","z-index":"5","opacity":"0"}).insertBefore(el).queue(function(){ var el = $(this); el.parent().css({"width":this.width,"height":this.height}); el.dequeue(); }); this.src = grayscaleIE10(this.src); }); // Quick animation on IE10+ $('img').hover(function () { $(this).parent().find('img:first').stop().animate({opacity:1}, 200); }, function () { $('.img_grayscale').stop().animate({opacity:0}, 200); } ); function grayscaleIE10(src){ var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var imgObj = new Image(); imgObj.src = src; canvas.width = imgObj.width; canvas.height = imgObj.height; ctx.drawImage(imgObj, 0, 0); var imgPixels = ctx.getImageData(0, 0, canvas.width, canvas.height); for(var y = 0; y < imgPixels.height; y++){ for(var x = 0; x < imgPixels.width; x++){ var i = (y * 4) * imgPixels.width + x * 4; var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3; imgPixels.data[i] = avg; imgPixels.data[i + 1] = avg; imgPixels.data[i + 2] = avg; } } ctx.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height); return canvas.toDataURL(); }; };
If you have any further questions, suggestions about cross-browser grayscale image example using CSS3 + JS, feel free to write them below or contact me personally.
Thank you for the excellent source.:D
$.browser has ben deprecated
Thanks, I know this, but for now this solution does the work :)
I have updated this tutorial and created another one that uses browser feature detection instead
Sometimes, the js solution, does not work, when i hit refresh. It returns undefined.
Hi!
Maybe you should try my new grayscale image tutorial. It is a bit different because it fires the grayscale funcion on images only after all of the images have been loaded.
I try to use grayscale filter with animated caption
like this:
http://css-plus.com/2010/05/how-to-create-an-image-caption-with-jquery/
or this:
http://www.newmediacampaigns.com/blog/jcaption-a-jquery-plugin-for-simple-image-captions#demo
but it doesnt work in ie11 (using functions.js)
it causes the caption to show up in the grayscaled photo for a sec and then it disappears since the colored photo is in front..
can you help me please?
thank you
Hello, Nick!
If you could give me a link to your solution, it would be a lot easier to get involved and help you cope with your issue.
It sounds like some kind of z-index or element position problem, but it is just a wild guess.
How can I change code to target juts images with specific class and not all images on site
Hola, Simon!
In that case you could add some kind of classes to the images that you want to apply grayscale.
And then modify the CSS:
And after that you will have to modify the JS as well.
Instead of
you will have to write
Many thanks for your code and sources. I’m working for days already to get this kind of functionality working on my website but without success. Can you please help me out. I’ve copied all your sources to my provider and tested it but the gray images are not visible, but when I hover them the coloured images appear.
The url is http://www.need4value.com/grayoriginal.html. Many many thanks for helping me on this.
Best regards Patrick
Hi, Patrick!
I have experienced the same issues working with grayscale images.
In order to fix this you should at first check if all the images have been loaded and fire grayscale function only after that.
This is all the magic behind it :)
Just thinking if it would be possible to detect browsers that support canvas but not the greyscale filter instead of doing user agent gymnastics…
Nope! This doesn’t work in IE. I tested your source code by downloading in IE11.
Hello!
This is kind of weird because I just tested it on IE11 and it works without problems.
Maybe you have enabled compatibility mode?
Nice solution for IE11. However, when I use it on a page with lots of images to grayscale – I’m talking > 300 images – IE 11 doesn’t respond for a while because the grayscale script is still loading. Is there a way to increase the speed of the script when we want to use it on a very large amount of images ?
Thanks! This is true, script does have a bit of a delay until it fires.
I guess, that if you are running this script on such a lot of images, you might want to load the images using lazyload technique and applying this Grayscale script only on those images that are newly loaded.
One other way that I haven’t tried is asynchronous loading of Grayscale function.
I will look into this and see if I can manage to get some improved results.
[…] couple of months ago I did a tutorial that showed how to create grayscale images using CSS and JS on all major browsers, including Internet Explorer 10 and 11 that no longer support CSS filters. […]
awesome script !!! is there any way to make blur work with the same way grayscale does?
I guess that you could create or find something that would be similar to this grayscale image solution, but I just haven’t had the need to build such a thing.
[…] This is a modified version of a tutorial “Cross-Browser Grayscale image example using CSS3 + JS“. […]
how to apply this script on background-image and on hover event? tnx :)
Hola, Kida!
I am affraid that this solution only will work on images.
Background images can’t be affected with this script.
BLESS YOU FROM RUSSIA, you’ve saved me. Great work! Thanks a lot.
p.s. : for those, who said about $.browser – you can use jqmigrate or moderniz, what’s problem people?
p.p.s: sorry for my english -___-
Hi, this is a wonderfully put explanation. However for a novice like me, can you explain how I implement this to my wordpress site. eg. what does “Include jQuery library” mean.
Hi, Jiskar!
If you are just starting off on WordPress, I guess you should look into WordPress theme customization, take a look at this example: http://www.siteground.com/tutorials/wordpress/wordpress_create_theme.htm
So at first you should get acquainted with this and after you have learned how to customize theme, you will be able to start implementing some additional functionality, like for example show off grayscale images in WordPress.
And as for the “Include jQuery library”, it means that you should include those 2 lines of code in your themes header.php section between the head tags.
I hope that this helps you at least a bit :)
Just wanted to say that Opera does support filter grayscale https://developer.mozilla.org/en-US/docs/Web/CSS/filter#Browser_compatibility
Hello!
Thanks for the note, but you should mention that only Opera since version 17+ supports these filters.
The majority still uses the old Opera versions that should still be considered.
Thanks for this, time & life saver!
Hi, I see where you mentioned if you only want to target certain images to change to a class in the CSS and JS instead of using img. How many places does this need to be done? I tried replacing in the CSS and in the JS $(‘.MyClass’).each(function(),
but all my images on the page are turning grayscale, and I only want certain ones to be.
Any suggestions?
Hello Bruce!
Firstly you should make adjustments in the CSS part and replace the img tag with a class:
And after that you should make adjustments to the functions.js file.
For Opera and Safari:
And then comes the IE10+:
Should be all that is needed. Hope this helps.
Thanks for the assistance Nauris. Everything looks great in Chrome, but in FF, the image area is blank until you hover over it, at which time a colored image appears. Is the SVG file what makes this work in FF? I may need to check the path to that, but I think it is correct. Also in IE I get colored images only? How can I troubleshoot? thxs!
Hello!
Yes, for Firefox it is the svg solution in CSS that makes images grayscale.
I have experienced problems with images not being loaded on IE and Opera because the script fires prior the images have been loaded, but I guess never in Firefox. It should not have any kind of problems whatsoever because the svg solution should be stable.
In order to troubleshoot your script you could try to change the CSS behavior. By default load all the images without grayscale and only on hover make the images grayscale. That way you at least will tell if the grayscale solution works and the path is correct. Ouh and by the way I dont know if you use it or dont, but in order to debug code I am using Firebug plugin.
Hi, Getting closer. Seems to work in Chrome, FF, and IE 9 and below now. Not sure what is going on with IE10 and IE11, but your demo works for IE10,11 so I know it’s good!
Hi!
Good to hear that you managed to get it working. For IE10+ there is this script that checks if the browser is IE and if the version iz 10 or 11. Maybe you should look into that and try to test what it outputs and does it determines that the browser is IE10 or IE11.
Check the code because if it determines that it is greater than Internet Explorer 10, it will add this class to the body:
And from then you will tell if you can move further and look into the next part of the code and see if there are some classes that might be wrong or maybe something else.
thanks for the hint. I checked the source in IE11 and the class is not being added to the body. but I don’t know why. I am using Drupal, so maybe there is a conflict with some other code being used in the theme.
Yes, it might be that there is a conflict with some other JS library. You could as well try the other solution for Grayscale images using Modernizr library. Maybe it will work better in combination with Drupal scripts http://www.majas-lapu-izstrade.lv/cross-browser-grayscale-image-example-using-css3-js-v2-0-with-browser-feature-detection-using-modernizr/
I figured out the problem, it had to do with the way JS closures work in Drupal. Thanks again for the code!
Hi, one more question or idea. Does this only work in the css and js. for img or a class of img? I ask because I have a div with a class of item wrapped around the image and a paragraph tag below the image with a specific class. The paragraph is being hidden and fades in on hover of the item div using JQuery. It would be a nice effect if hovering over the item div triggered both effects at the same time. Right now they are independent.
Hello!
Well the SVG part of the code that will work like you mention and can be triggered via div tag. But to get it working on IE10+, Safari and opera, you will have to come up with some extra code, it seems that it won’t work like that.
ok, one more hopefully not too irritating question. Any ideas on getting this to work on mobile so that the image changed on touch?
Hello, Bruce!
I guess it should be doable, but haven’t tried such a trick :)
Hi Nauris
i realized, that the script does not work with an later jQuery version?
The Contao-CMS i used, takes the version 1.11.0 from here http://code.jquery.com/ and with this it does not work in IE 10+. When i change it to your version 1.8.3 then it works but then a lot other stuff of the CMS go’s wrong.
I saw, that you use in your v2 Version the same jQuery Library. Is it possible to change the code in an easy way?
thanks for your help
Hi!
Have you tried adding this jQuery Migrate library?
It should allow you to use both the new 2.x Jquery version and the old 1.x Jquery
The solution does not work in IE10-11 if there are images from other domains on page:
Hello, Miroslaw
I have never had the need to test it like that.
Maybe you can give me the link to your website?
Hi,
thanks for this. But do you have to include jQuery UI?
Hello, Jordan,
You are corect, this Jquery UI is not needed here. thanks for the notice.
I have updated the code.
Its amazing.. cool post. How can i give to multiple class
Hello,
If you want the grayscale effect to be applied to multiple classes, then you can try replacing the existing line in the javascript code:
with something like this:
Hi there! Works great, but in safari (8.0.2) with columns ( -webkit-column-count ) seems to be some problems. only the first column is grayscalin’ the images, the other columns are getting ignored.
any idea?
Hi!
Is that happeining on an Apple computer?
And I am not really sure if I understand about which columns are you talking about, since the code does not have columns.
Thanks Nauris ,
I tried this solution.
In my case, User can change the images what he has uploaded.
But this soln. does not allow user to remove images which are gray scaled. As the img tag created, has the src={encrypted code} which does not allow me to change once images are Grayed out
Hello!
I am not quite sure what you have in mind, but this solution can be used if you would like to change the images for example some Content Management System (e.g. WordPress)
Hi Nauris,
Thanks for the script…
I tried testing the script on IE 11 with win8.
The functions.js throws an Error at line = indexsizeerror
var imgPixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
Hello,
I have tested it on Win8 with Internet Explorer 11, no such errors whatsoever for me.
+ It shows grayscale images and everything works splendid so I do not see that this is an issue
Hi Nauris,
I am trying to upload images using “jquery.uploadify-3.1.min.js”. While doing a browse and upload for images, the script throws an exception at line
var imgPixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
with canvas.width & canvas.height going 0 (zero).
Secondly, there is problem when I try to upload images with more than 4 MB size as it takes time to generate the URL.
Your script is really helpful, but I am facing these few issues. Can you plz help in resolving them.
Hi, Sameer, it might be that you have run into trouble with some other JS plugins that you are using on your project. Or the other possible reason is that you are trying to make images grayscale on the fly – you might want to try to setup and fire Grayscale functions on the image strictly after it has been uploaded, if you will try to do that simultaneously, you might run into such problems, meaning that JS script does not receive actual data about image height and width.
And I wouldn’t suggest you to upload 4MB image files, that is way too much than we usually need since the page load times will be depressing.
Hope that this helps.
Can you update to work with latest 2.1.3 version of Jquery? not supported with any later version than 1.8.3 of jquery.
Hello John,
Changing Jquery version will definitely take some effort in order to test the grayscale functionality across multiple browsers and devices. I can’t promise you anything right away since I am currently doing work on some other projects.
Hi Sir,
First i want to tell great thanks for you sharing this one,it helps me a lot but
im getting IndexSizeError in IE 10 and IE11,
please help me, i am new to this type of error
Hi,
This is not the first comment of Internet Explorer error, but it doesn’t affect the grayscale functionality so I see no real reason to start debugging this weird browser.
To be honest, I don’t see any reason why anybody would need to use this prehistoric browser with bad reputation.
For me – if it works on Internet Explorer, then it is enough for me. I am not trying to fix every JS error that it thinks is an error (:
Hello Sir I’ve been working on this but there’s a n error on IE10 onwards as img_wrapper which wraps the img on IE 10 & IE 11 takes width and height way more than the img original width and height.
Please help us through this problem
Hi, I am affraid that currently I will not be able to deal with this issue since this was only a side project and my hands are full of work.
But thank you for the information, if I will have time, I will look into this issue.
Hello guys, I have just added support for Microsoft Edge browsers. Since Microsoft Edge supports CSS filters we don’t have to use JS or svg grayscale solutions. And by the way, please note that currently Microsoft Edge comes with disabled CSS filters, you have to enable them at first to use them.
Hi Nauris,
Thanks for the amazing tutorial.
The demo link works in all Browser except Microsoft Edge. I am testing on my windows 10 laptop?
Any ideas what may the reason why it is not working in Microsoft Edge.
Thanks and Regards,
Meher Bala
Hi Meher,
Thanks for the kudos :)
If you are on Microsoft Edge, you will have to first enable CSS Filters which by default are disabled (do not ask me why) :)
Enter in the address bar this about:flags
It’s working fine for img tag. How to archive this with css background image?
Hello, I am afraid that this tutorial is not for background images.
jQuery has removed $.browser from 1.9 and their latest release. And this solution does not work with the current versions of jQuery
Great job..
Thank you. Still I am facing a problem.
I want to apply cross-browser greyscale in div tags. can you help me. I tried different ways. But its not working in internet explorer.
hope you can help me..
NOT WORKING IN IE 11!!
Hey Anon,
Thanks for your feedback.
Could you please test the Demo on the IE11 and let me know if that is working ok? I just tested it out and for me all images are grayscale.
Hi Nauris,
I have a problem with Jquery.min file because i have used 3.2.1 jquery.min file and you have 1.8 so that this is not working with 3.2.1 js file please help me
Hi, I wonder if you would be able to use Jqery Migrate for this: https://github.com/jquery/jquery-migrate ?