Wednesday, July 15, 2009

Browser differences and jQuery, (oh, yes... they exist)

I've been doing more with jQuery lately and I can't say how much I'm enjoying it. The other day I was able to take an old JavaScript function that was over 20 lines long and refactor it down to 4 lines... sweet! And one of the nice benefits of using jQuery has been not having to worry about coding to specific browser variations. I did come across something recently, though, that is inconsistent between the browsers which I thought I would share.

It has to do with the way CSS attributes are retrieved for an element. Sometimes the .css() method will return different values for the same style definition (or no value at all) depending on the browser. I'm probably expecting too much of jQuery in terms of it's browser agnostic implementation - but I was honestly surprised. Consider the following HTML page:

<html>
<head>
<style type="text/css">
.ugly
{
background-color: #3A9F0E;
border: solid 1px #FFD800;
color: #fff;
-moz-border-radius: 10px;
blur: 1;
}
</style>
</head>
<body>
<h1 class="ugly">test</h1>
<div id="dvDisplay" />
</body>
</html>

A very basic but easy to grasp example. Ugly, but easy to grasp. There is a H1 tag which has the "ugly" class applied. The ugly class has some style definitions (some valid/some not... more on that shortly). There's an empty DIV tag, too. We're going to use that - just watch.

Now let's add a little JavaScript to illustrate my jQuery concerns. It's going in the head section and looks like this:

<script src="js/jquery-1.3.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
var message = "";
var property = ["border-color",
"border-bottom-color",
"-moz-border-radius",
"-moz-border-radius-bottomleft",
"background-color",
"font-weight",
"blur"];
property.sort();

for (var i = 0; i < property.length; i++) {
message += property[i];
message += " = '";
message += $(".ugly").css(property[i]);
message += "' <br />";
}

$("#dvDisplay").html(message);
});
</script>

The script is going to run when document.ready event fires, build an array of some CSS properties, sort them (because I'm too lazy to put them in the right order) and then loop over those properties to see how the jQuery .css method returns them. Note that the first and second pair of properties (border-color | border-bottom-color and -moz-border-radius | -moz-border-radius-bottomleft) are going after similar values - it's just one is more specific. Note, also, how the style declarations for the ugly class define each of these. Anyway, each property is appended to the output message which is then displayed in that DIV we left open (see, I told you we would use it).

So what does the output look like? Well, despite the browser agnostic behavior of jQuery it depends which browser you're in. Here are the results I saw (all browsers running on XP professional):

Firefox (3.0.11)
-moz-border-radius = ''
-moz-border-radius-bottomleft = '10px'
background-color = 'rgb(58, 159, 14)'
blur = ''
border-bottom-color = 'rgb(255, 216, 0)'
border-color = ''
font-weight = 'bold'

Internet Explorer 8 (both browser modes and all document modes)
-moz-border-radius = '10px'
-moz-border-radius-bottomleft = 'undefined'
background-color = '#3a9f0e'
blur = '1'
border-bottom-color = '#ffd800'
border-color = '#ffd800'
font-weight = '700'

Google Chrome (2.0.172.33)
-moz-border-radius = 'null'
-moz-border-radius-bottomleft = 'null'
background-color = 'rgb(58, 159, 14)'
blur = 'null'
border-bottom-color = 'rgb(255, 216, 0)'
border-color = ''
font-weight = 'bold'

Safari (3.2.2)
-moz-border-radius = 'null'
-moz-border-radius-bottomleft = 'null'
background-color = 'rgb(58, 159, 14)'
blur = 'null'
border-bottom-color = 'rgb(255, 216, 0)'
border-color = ''
font-weight = 'bold'

So, where are the differences? What strikes me is that even though the -moz-border-radius property is a Mozilla specific style, the output in Firefox is an empty string. Only the specific corner (-moz-border-radius-bottomleft) has the value now. I can only assume the style definition I'm using is a shortcut like the border definition. That would explain why Firefox, Chrome and Safari all return an empty string when checking border-color but can return border-bottom-color. IE will give me either.

The other interesting thing is that while IE8 doesn't know squat about the -moz-border-radius property can tell me what I wanted the value to be (but unsurprisingly, can't provide the specific corner value). So IE8 seems to be able to access the style declarations even if it doesn't do anything with them. This brings me to the declaration of the blur property. I wanted to know if it was possible to use CSS styles to store values to be retrieved later by jQuery to be applied to a drop shadow effect (rather than hard coding in the script). And it appears that this approach would work with IE8 but not the others. Which is how I identified the different behaviors of the browsers in jQuery.

So time for one last assumption: I really believe jQuery is doing its best to get the style attributes but the browsers must be preventing it from doing that. This does not dimish my appreciation of jQuery, but makes me recognize different browsers continue to plague web development.