Tuesday, January 17th, 2006

JavaScript Tip: Watch out for “+”

Category: Examples, JavaScript, Tip

Dynamic languages are the cool thing du jour. All dynamic languages are not born equal, and JavaScript is one that happens to be weakly typed. This means that you can keep throwing new types at a variable and JavaScript takes it in its stride.

You sometimes get into some problems though, and one of the common ones appears when you have weird behaviour with something + somethingelse.

We recently ran into an example of this issue when debugging an application that was running fine in all browsers bar Safari. We are good citizens and want everyone to be able to access the application so dug in.

It turned out that the root issue was seen in:


return el.offsetTop + getInnerTop(el.offsetParent);

When we debugged in Safari, we ended up seeing the result of this as: “203002348504397534050px0px0px”

The other browsers had sane results like “2233”

The problem was with types and Safari was adding together the world as Strings vs. numbers.

Glenn Vanderburg has lots of fun examples of wacky things happening here.

The simple fix in this case was to wrap the calls with parseInt:


return parseInt(getLeft(el)) + parseInt(getBorderLeft(el));

The only other change to make this Safari happy was to fix the calls to window.getComputedStyle(..) and be Safari friendly with document.defaultView.getComputedStyle(..).

there is a nice example of a cross browser getCssProp() over at Squid Fingers:

function getStyle(element, styleProp) {
    if (typeof element == 'string') element = document.getElementById(element);

	if (element.style[prop]) {
		// inline style property
		return element.style[prop];
	} else if (element.currentStyle) {
		// external stylesheet for Explorer
		return element.currentStyle[prop];
	} else if (document.defaultView && document.defaultView.getComputedStyle) {
		// external stylesheet for Mozilla and Safari 1.3+
		prop = prop.replace(/([A-Z])/g, "-$1");
		prop = prop.toLowerCase();
		return document.defaultView.getComputedStyle(element,"").getPropertyValue(prop);
	} else {
		return null;
	}
}

Posted by Dion Almaer at 12:21 pm
6 Comments

+++--
3.5 rating from 31 votes

6 Comments »

Comments feed TrackBack URI

Actually Safari’s implementation of getComputedStyle() is conform the standard. It just turns out that Firefox and Opera reference document.defaultView to window. From what I can see in the code example Safari doesn’t return numbers where you’d expect them, offset values don’t normally return as strings with a pixel unit. That would be the root cause, and that’s where the fix should be placed.

Comment by Mark Wubben — January 17, 2006

Is it just me, or is Safari the worst browser to code for when it comes to JavaScript? I usually use Firefox to code my projects, so I never have problems there (any problems are fixed before trying other browsers). Most stuff I try works out of the box in IE/Opera, but I seem to have the most problems with Safari. Part of my issue is that I don’t have access to Safari to test things, but it does seem that Safari has run astray in the JS world.

Comment by Jacob Munson — January 17, 2006

The JavaScript ‘+’ operator always reminds me of why I love Perl.

Comment by Ralesk Ne’vennoyx — January 17, 2006

This getStyle method at the end is very strange… what does it do? It takes an id or an element (why not be clear and have it take one or the other?) and it returns either the style or the currentStyle/computedStyle for that prop (or styleProp, little bug there).
Those are very different! Who would want a method that returns style or if absent currentStyle? Style could be set to “auto” while currentStyle returns “60px”. Which one do you need? The two are very different.

Comment by Lon — January 18, 2006

Oh and another thing:


The simple fix in this case was to wrap the calls with parseInt:
return parseInt(getLeft(el)) + parseInt(getBorderLeft(el));

This to me seems a very bad way of doing things. The methods getLeft and getBorderLeft should return integers. What are they returning now? “30px” or “2em”? You have to trust these methods and you can only trust them if they return proper integers, expressing whatever the express in a certain choosen unit, probably “px”. They should, after all, get you the left and borderLeft… What does that mean by the way… getBorderLeft? Do you mean getBorderLeftWidth?

Comment by Lon — January 18, 2006

I agree with Lon’s opinion on getLeft() and getBorderLeft(). If you and Numbers in JavaScript, you get a Number out. Based on the report, it seems like the original difficulty with Safari was that el.offsetTop and friends were Strings. And since these properties aren’t part of any spec, … But still, I think it would be in Apple’s interest to conform to status quo (especially for the non-spec things).

And on a side note, this isn’t about weak typing but dynamic typing (in this case). Python could have a similar issue here. JavaScript does tend to have weaker typing with some operators though (like “==” and such).

Comment by Tom — January 18, 2006

Leave a comment

You must be logged in to post a comment.