Check whether the browser supports RGBA (and other CSS3 values)

When using CSS, we can just include both declarations, one using rgba, and one without it, as mentioned in my post on cross-browser RGBA backgrounds. When writing JavaScript however, it’s a waste of resources to do that (and requires more verbose code), since we can easily check whether the browser is RGBA-capable, almost as easily as we can check whether it suppports a given property. We can even follow the same technique to detect the support of other CSS3 values (for instance, multiple backgrounds support, HSLA support, etc).

The technique I’m going to present is based on the fact that when we assign a non-supported CSS value on any supported CSS property, the browser either throws an error AND ignores it (IE-style), or simply ignores it (Firefox-style). Concequently, to check whether RGBA is supported, the algorithm would be:

  1. Get the color (or backgroundColor, or borderColor or any property that is widely supported and accepts color values) value of the style object of any element that exists in the page for sure (for instance, the first script tag) and store it in a variable.
  2. Assign an RGBA color to the color property of that element and catch any errors produced.
  3. Assign to a variable whether the color of that element did change (boolean true or false).
  4. Restore the previous color to the color property, so that our script doesn’t interfere with the designer’s decisions.
  5. Return the stored result.

and it would result in the following code:

function supportsRGBA()
{
	var scriptElement = document.getElementsByTagName('script')[0];
	var prevColor = scriptElement.style.color;
	try {
		scriptElement.style.color = 'rgba(1,5,13,0.44)';
	} catch(e) {}
	var result = scriptElement.style.color != prevColor;
	scriptElement.style.color = prevColor;
	return result;
}

Performance improvements

The code above works, but it wastes resources for no reason. Every time the function is called, it tests RGBA support again, even though the result will never change. So, we need a way to cache the result, and return the cached result after the first time the function is called.

This can be achieved in many ways. My personal preference is to store the result as a property of the function called, named 'result':

function supportsRGBA()
{
	if(!('result' in arguments.callee))
	{
		var scriptElement = document.getElementsByTagName('script')[0];
		var prevColor = scriptElement.style.color;
		try {
			scriptElement.style.color = 'rgba(0, 0, 0, 0.5)';
		} catch(e) {}
		arguments.callee.result = scriptElement.style.color != prevColor;
		scriptElement.style.color = prevColor;
	}
	return arguments.callee.result;
}

Making it bulletproof

There is a rare case where the script element might already have rgba(0,0,0,0.5) set as it’s color value (don’t ask me why would someone want to do that :P ), in which case our function will return false even if the browser actually supports RGBA. To prevent this, you might want to check whether the color property is already set to rgba(0,0,0,0.5) and return true if it is (because if the browser doesn’t support rgba, it will be blank):

function supportsRGBA()
{
	if(!('result' in arguments.callee))
	{
		var scriptElement = document.getElementsByTagName('script')[0];
		var prevColor = scriptElement.style.color;
		var testColor = 'rgba(0, 0, 0, 0.5)';
		if(prevColor == testColor)
		{
			arguments.callee.result = true;
		}
		else
		{
			try {
				scriptElement.style.color = testColor;
			} catch(e) {}
			arguments.callee.result = scriptElement.style.color != prevColor;
			scriptElement.style.color = prevColor;
		}
	}
	return arguments.callee.result;
}

Done!

  • http://thinkweb2.com/projects/prototype/ kangax

    Thanks for writing this.

    I took a bit different approach – without the need to check script element’s style and then having to restore it. Simply creating a dummy element (and later null’ing it) seems to work just fine (and it doesn’t require script element to be present in a document ;) )

    I’m also not sure why you need to cache the result when it can be simply stored in a boolean. That boolean is what you would use later on in the app (so the check is only executed once)

    var IS_RGBA_SUPPORTED = (function(){
    var value = ‘rgba(1,1,1,0.5)’,
    el = document.createElement(‘p’),
    result = false;
    try {
    el.style.color = value;
    result = /^rgba/.test(el.style.color);
    } catch(e) { }
    el = null;
    return result;
    })();

  • http://leaverou.me Lea Verou

    Hello kangax! I’m honored to have you in my blog! :)

    About your solution:
    1. I agree on the createElement part, should probably change that.
    2. I don’t like the idea of checking rgba support (or any other feature support) from the start, although I’m aware that this is the approach favored by most libraries. The reason is, that it might not be actually needed. I understand that the overhead isn’t probably significant, but if you think about checking multiple features and bugs, and the list will constantly grow, it will end up being significant one day. And what will you do then? Tell the users to change all their scripts to use a function instead of a constant (since it will take lots of years to be able to assume getter support)?

  • http://www.reigndesign.com Shakakai

    I agree with you that you should be lazy about checking supported properties. However, you might want to think about saving the result of the function after its first run, and then serving that back on all subsequent calls.

    Something akin to this:

    var supportsRGBa = (function(){
    var supported;
    var check = function(){
    if( supported != null ) return supported;
    //regular function here
    };
    return check;
    })();

    Haven’t tested that function but it should work.

  • http://leaverou.me Lea Verou

    Hello Shakakai! Thanks for commenting.

    What do you mean by “I agree with you that you should be lazy about checking supported properties.”?

    As for saving the result of the function, if you read my whole article, I do save it – just in a different way, since I don’t want the function to be run at all until it’s actually needed. ;)

  • Xavier

    This work for me :

    colorSupport = (function()
    {
    var
    p = document.createElement(‘p’),
    s = { rgb:false, rgba:false, hsl:false, hsla:false };
    try { p.style.color = ‘rgb(1,1,1)’ ; s.rgb = /^rgb/.test(p.style.color); } catch(e) { };
    try { p.style.color = ‘rgba(1,1,1,0.5)’ ; s.rgba = /^rgba/.test(p.style.color); } catch(e) { };
    try { p.style.color = ‘hsl(1,1,1)’ ; s.hsl = /^hsl/.test(p.style.color); } catch(e) { };
    try { p.style.color = ‘hsla(1,1,1,0.5)’ ; s.hsla = /^hsla/.test(p.style.color); } catch(e) { };
    s.alpha = ( s.rgba || s.hsla ) ? true : false ;
    p = null;
    return s ;
    })()

    // return:
    { rgb:false|true, rgba:false|true, hsl:false|true, hsla:false|true, alpha:false|true }

    // call like:
    if( colorSupport.alpha ) …
    if( colorSupport.rgba ) …

  • Pingback: Detect CMYK in Browser | Dane Balia

  • Derek

    Just updating this to something without all the try-catch stuff. This is summerized from Modernizer open source.

    //alpha supported test
    var div = document.createElement(‘div’);
    div.style.cssText = “background-color:rgba(150,255,150,.5)”;

    if(!!~(” + div.style.backgroundColor).indexOf(‘rgba’)){
    //supported
    }