Design, CG Graphics & Inspiration
cssHooks – Expanding the Set of CSS Properties

cssHooks – Expanding the Set of CSS Properties

In this article I will tell you about the jQuery.cssHooks object, which jQuery contains by default. The expansion of this object allows you to add new properties or values written in the . css () method, which are not natively supported by certain browsers. Perhaps the availability of many CSS hooks is not new, but for me it was a small opening.

For those who are too lazy to read further, I cite the main idea. Let’s suppose you want to add to jQuery CSS property chuck-norris:

$.cssHooks.chuckNorris = {
	get: function(elem) {
		//do some manipulations with elem component, and get value
		return value;

	},
	set: function(elem, value) {
		//do some manipulations with elem component, set the value
	}
}

$(el).css(‘chuck-norris’, Infinity);
//или $(el).css({‘chuck-norris’: Infinity});
alert($(el).css(‘chuck-norris’)); //Infinity

Next I will describe in details the “modification” of background-color property to support the rgba in older versions of IE, and adding a new, non-existent in the specification property of background-alpha for easy installation of transparent background color. In IE, the color transparency will be implemented through the use of filter’s properties, adding the element of the gradient, which consists of two identical colors.

This method has two drawbacks.

Firstly, the gradients don’t work for units with auto width, you will have to apply the hack:

$('div').width('100%');

Secondly, filters operate on the background images. Instead of the expected (first figure) in older versions of IE you get something like this (second figure).

First, we need a (parseColor) function, which will parse the color in different formats and return the object {r, g, b, a}. The following options are considered:

• When an empty string or transpatent entered the input
• The string in # aarrggbb format (for gradients in IE)
• The string in #rrggbb format
• The string in #rgb format
• The string in rgb[a](r, g, b [, a]) format

On the second stage we will create a class Color, which takes an html element, and contains a number of functions that are needed in the future.

var Color = function(el){
    
    //function returns an object of the three elements:
    //whether the browser is IE8-, if there is a gradient in the styles, whether this gradient is enabled
    var ieDetect = (function(){
        var ua = navigator.userAgent;
        var result = {}
        
        result.isOldIe = ~ua.indexOf('MSIE 6')    || ~ua.indexOf('MSIE 7') || ~ua.indexOf('MSIE 8');
        result.hasGradient = result.isOldIe &&
                ~el.style.filter.toLowerCase().indexOf('gradient');
        result.isGradientEnabled = result.hasGradient &&
                !!el.filters.item("DXImageTransform.Microsoft.gradient").enabled;
            return result;
    })();
    
    //conversion of the object with decimal color values into the object with hexadecimal
    var colorToHex = function (objColor) {
        var hex = {};
        for (var i in objColor) {
            hex[i] = objColor[i];
            if (i==='a') {
                hex.a = Math.round(hex.a*255);
            }
            hex[i] = hex[i].toString(16);
            //add '0 'if the resulting value is of a single character
            hex[i] = hex[i].length == 2 ? hex[i] : '0' + hex[i]; 
        }
        
        return hex;
    }
    
    
    //converts the object {r, g, b, a} in a string with the specified format
    var colorToString = function(objColor, format) {
        var hexColor = colorToHex(objColor);
        
        switch(format) {
            case 'rgb':     return 'rgb(' + objColor.r + ',' + objColor.g + ',' + objColor.b + ')';
            case 'rgba': return 'rgba(' + objColor.r + ',' + objColor.g + ',' +
                        objColor.b +',' + objColor.a + ')';
            case '#6':     return '#' + hexColor.r + hexColor.g + hexColor.b;
            case '#8':     return '#' + hexColor.a + hexColor.r + hexColor.g + hexColor.b;
        }
    }
    
    //Converts one color format to another
    var convertColor = function(color, format) {
        var colorObj = parseColor(color); 
        return colorToString(colorObj, format)
    }

    //adds getComputedStyle support for older browsers
    //below you'll see for what purpose
    if (!window.getComputedStyle) {
        window.getComputedStyle = function(el, pseudo) {
            this.el = el;
            this.getPropertyValue = function(prop) {
                var re = /(\-([a-z]){1})/g;
                if (re.test(prop)) {
                    prop = prop.replace(re, function () {
                        return arguments[2].toUpperCase();
                    });
                }
                return el.currentStyle[prop] ? el.currentStyle[prop] : null;
            }
            return this;
        }
    }
...

Now we’ll move to the installation of the color value. I hope it won’t be so difficult for our readers to understand the code, despite its nesting.

this.setBackgroundColor = function(color) {
    var    newColor,
        newColorObj = parseColor(color);
    if(ieDetect.isOldIe) { // If IE
        if(newColorObj.a < 1) { // If alpha is less than 1
            el.style.backgroundColor = 'transparent';
            newColor = colorToString(newColorObj, '#8');
            if(ieDetect.hasGradient) { // If there is a gradient filter
                el.filters.item("DXImageTransform.Microsoft.gradient").enabled = true;
                el.filters.item("DXImageTransform.Microsoft.gradient").startColorstr = newColor;
                el.filters.item("DXImageTransform.Microsoft.gradient").endColorstr = newColor;
            } else { // If there is no gradient filter
                el.style.filter +=
                       "progid:DXImageTransform.Microsoft.gradient(enabled='true', startColorstr=" +
                       newColor + ", endColorstr=" + newColor + ")";
            }
        } else { // If alpha equals 1
            newColor = colorToString(newColorObj, '#6');
            if(ieDetect.hasGradient) { // If there is a gradient filter
                el.filters.item("DXImageTransform.Microsoft.gradient").enabled = false;
                el.style.backgroundColor = newColor;
            } else { // If there is no gradient filter
                el.style.backgroundColor = newColor;
            }
        }
    } else { // if it’s not IE
        if(newColorObj.a < 1) { // If alpha is less than 1
            newColor = colorToString(newColorObj, 'rgba');
            el.style.backgroundColor = newColor;
        } else { // If alpha equals 1
            newColor = colorToString(newColorObj, '#6');
            el.style.backgroundColor = newColor;
        }
    }
}

The next part of the code is responsible for getting the color values. It’s a lot easier.

this.getBackgroundColor = function() {
    var color;
    if(ieDetect.isGradientEnabled) { // If it is IE and the gradient is enabled
        color = el.filters.item("DXImageTransform.Microsoft.gradient").startColorstr;
        return convertColor(color, 'rgba');
    } else {
        //the hack was taken from here: http://snipplr.com/view/13523/getcomputedstyle-for-ie/
        //instead we could use $(el).css('background-color');
        //but we reassign this property, and there appears an infinite recursion
        color = el.style.BackgroundColor ||
                 window.getComputedStyle(el,null).getPropertyValue('background-color');
        return color;
    }
}

Now the main part. We add a backgroundColor hook.

$.cssHooks.backgroundColor = {
    get: function(elem) {
        var color = new Color(elem);
        return color.getBackgroundColor();
    },
    set: function( elem, value ) {
        var color = new Color(elem);
        color.setBackgroundColor(value);
    }
}

Add a backgroundAlpha hook

$.cssHooks.backgroundAlpha = {
    get: function(elem) {
        var color = new Color(elem);
        var colorStr = color.getBackgroundColor();
        var colorObj = parseColor(colorStr);
        return colorObj.a;
    },
    set: function(elem, value) {
        var color = new Color(elem);
        var colorStr = color.getBackgroundColor();
        var colorObj = parseColor(colorStr);

        //if the value of the style is a number, then  jQuery attributes 'px'; fix
        colorObj.a = String(value).replace('px', ''); 
        color.setBackgroundColor('rgba('+
                colorObj.r+','+colorObj.g+','+colorObj.b+','+colorObj.a+')');
    }
}

All is done, now you can use it.

p{background-color: #991111; ...}
div{background: url(...) ...}
<div>
	<p>...</p>
	<p>...</p>
</div>
$('p').width('100%');
$('p:eq(0)').css({'background-color':'rgba(0,111,221,0.9)'});
$('p:eq(1)').css('background-alpha', 0.5);

As we can see, for objects with names in the «camel case» style jQuery automatically adds support for “hyphen” style.

Conclusion

If you want to add support for a property that is not supported (or partially supported) by various browsers, you should forget about what you can do with the plug-in creation and use $. CssHooks instead.

Links:
The final version: finom.ho.ua/bgalpha/
cssHooks in alternative documentation: jqapi.com/#p=jQuery.cssHooks

  • FINoM,
  • September 19, 2011

SHARE THIS POST

Subscribe for the hottest posts

Subscribe to our email newsletter for useful tips and freebies.