Using the assignment operator instead of the equality operator

The previous version of the Find Position script didn't work quite correctly,
since it often ignored the last step in position calculations: the one from the <body>
to the <html> element. Part of the reason was that its code was too complicated.

The problems with this script used to generate a lot of comments. Eight months ago I changed the
script somewhat, and comments dropped off to zero, meaning I'd done it right. Meanwhile I've taken another
look at it and changed it a bit more.

In any case, the changed script now uses a new approach (that is, it was new to me eight months ago).
It now uses the assignment operator = instead of the equality operator ==
that you'd expect:

while (obj = obj.offsetParent)

I always planned to write a blog entry about this approach, because I feel this little trick should
become general knowledge. So here it is (eight months too late, but anyway):

Assignment versus equality

A mistake novel JavaScripters frequently make is confusing the assignment and equality operators
= and ==. If you want to compare two values, you should use a double equal
sign since that's how JavaScript works. If you accidentally use a single equal sign, you don't compare
values but assign a value to a variable.

For example:

if (x == 3) {
doSomething();
}

This is correct: you compare the value of x to 3. If x in fact has the
value 3, the doSomething() function is executed.

This is wrong:

if (x = 3) {
doSomething();
}

Now you do not compare x and 3, but instead you assign the value 3 to x.
The doSomething() function is always executed, regardless of the original value of x.
This can be quite confusing to novel scripters who think they've done it right.

Now why is doSomething() executed? That happens because the assignment operator = also
returns a value: the value you just set the variable to. In our example, the x = 3 statement does
two things:

It assigns the value 3 to x.

It then returns the same value 3 to whichever JavaScript construct asked for it. In this case,
it's returned to the if () statement.

The if () statement expects to receive a boolean value true or false.
If it receives true the statement doSomething() is executed. If
it receives false the statement is not executed.

However, now if () receives the value 3, which is a number and not a boolean.
That's perfectly OK to JavaScript. If any operator
or statement receives a value of the wrong type, JavaScript silently converts the value to the correct type.
Chapter 5 of the book treats these situations in detail.

The number 3 is converted to the boolean true and doSomething() is executed.
In fact, almost every number is converted to true, and therefore doSomething()
will be executed no matter which value you assign to x.

The only exceptions are 0 and the special value NaN
(Not a Number). These two are converted to false.

Usually you don't want all this complicated stuff to happen. Usually you just want to compare two values and take action
based on whether they're equal or not. That's why you generally use the equality operator ==
in such cases.

However, there are a few cases where deliberately substituting the assignment operator =
is a good idea.

Using assignment instead of equality

This script takes an object, calculates its offset relative to its offsetParent, and then
moves to this offsetParent to do the same. It continues doing this until there's no more
offsetParent to be found. When it's done we've found the actual position of the initial object
relative to the uppermost container in our page (usually the <html> element).

In order to do all this, we have to check continuously if the current object has an offsetParent. If
there is, we have to repeat all actions; if there isn't, the function is ready.

Why does it work?

The while (obj = obj.offsetParent) line takes care of this. Since it uses the assignment
operator =, obj receives a new value: it now points to the offsetParent
of the previous object so that we can easily repeat the calculations in the body of the do {}
statement.

As in the x = 3 example above, this new value of obj is also sent on to the
while () statement. That statement now receives an object, but needs a boolean. That's no
problem: any object is converted to boolean true.

Once that value has been received, the while () statement orders the do{}
to run once more and add the offset of the new object. That's exactly what we want.

But what about the last step? Eventually obj is going to point to the <html>
element, which is the topmost element in the document hierarchy and does not have an offsetParent.

That's no problem, either. If we reach the point where obj doesn't have an offsetParent
any more, the statement obj = obj.offsetParent returns the value undefined, since
all object properties that are not defined have this value.

So obj now becomes undefined. More to the point, this value is also returned to
the while () statement, and it is converted to boolean false. (After all, undefined
is a fancy way of saying "It's not there", and such a statement should obviously convert to false.)

Therefore, as soon as the function is unable to find an offsetParent of the current element,
its calculations stop. That, too, is exactly what we want.

Advantages

I find this way of working quite elegant. We have to get the new offsetParent and we also
have to check if there is a new offsetParent at all. The while (obj = obj.offsetParent)
statement handles both jobs at once.

Of course we could do the same with the equality operator ==. For instance:

You assume that the <html> element is the first one that doesn't have an
offsetParent. That assumption is dangerous because some older browsers (notably IE5)
consider the <body> element the topmost one.

This function doesn't make the very last check. As soon as obj points to the
<html> element it stops, and doesn't add the offsets of the <html>
element itself. Usually this element doesn't have any offsets, but as CSS wizardry progresses it might
acquire them.

The div=x[i] behaves exactly like the obj = obj.offsetParent. It assigns
a new value to div, except when there's no more value to be found (i.e. we've gone through
all <div>s in the document). At that point the assignment operator =
returns undefined, which is converted to false and stops the for ()
loop.

This way of working is supposed to have an advantage: the browser doesn't have to calculate x.length
every time the loop restarts. Although I doubt whether the removal of this calculation saves a lot of
processing time, it's nonetheless something to keep in mind.

All in all, you can use the assignment operator = for more than just assigning new values
to variables. Try it in your scripts. If you do it right you've solved several problems at once.

With the last example you give, ('div = x[i]') it works fine in this particular case, but will not work for arrays which have null entries since it will exit as soon as it hits a null. Writing loops in several different ways depending on the content of the arrays is, again, quite confusing when you come to read someone's code and work out what they were trying to do.

This practice is usually discouraged. The big problem is readability.
While working in team, other developpers may be confused by the "=" sign, and may even think it is a mistake and replace it with "=="...

In Java, the compiler gives you a warning for that ! (in java, this only work with boolean as integer are never converted into boolean...)

Anyway in my sense, using the implicit conversion from integer to boolean should be avoided because it lacks clarity.

This point isn't new. I think it's not confusing if you KNOW what the engine is doing (set the var value to a non boolean and then implicitly casting to boolean). So every good developer knows the difference between =, == (and ===).

But ...
"do {
// do stuff
} while (obj = obj.offsetParent);"
... sometimes throws errors if the value is undefined. So an own line with a check often is better.

Walking backwards through a live NodeList is cheaper because you don't have to access the length property, but just compare with 0.

Accessing the length property is very expensive in this case because the browser needs to re-compile the complete list (you never know if some node has appeared or disappeared) to calculate the length.

In general you should avoid using getElementsByTagName as much as you can. It's expensive and why would you want to find all elements with a certain nodeName? CSS can do that for you much faster and simpler.

What if I want to attach an event handler (say 'onclick') to all links in a menu (#menu)?
Of course I'm curious about your speed-of-light script ;-) but especially how you're going to use CSS for that.

I was wondering: is it always faster to attach one handler to a parentNode and check for the node each time the event is triggered (with some W3C/IE incompatibilies) than to initially attach a handler to each specific node and not having to do the check later on, regardless of the number of nodes?

I didn't say all solutions involve CSS, in this case you don't need CSS.

I don't know whether it's faster to attach at node or aggregate level, but it hardly matters when the user physically moves the mouse to click somewhere. The amount of time involved in the user causing the event itself is so much more than traversing a few parentNodes looking for some className...

Benefits are: no tedious administration, no detaching, no updating when new HTML is inserted, more extensible, easier, cleaner, whatever... Just better in my view.

I almost always attach only on body-level.

So once more then: why would you ever need to use getElementsByTagName? You don't if you think about what you want to do a bit more.

About the last example: the advantage of the method is hardly exclusive to this system. Another solution is to use:

if(i=array.length-1;i>=0;i--){...}

has the exact same effect, the length is read only once, and on top of that comparison with zero being equivalent to boolean testing, you have the same speed gain, without the inconvenient of stopping on empty array elements.

while, for and recursion have been mentioned and although I am a fan of recursion, in the case of the javascript engine, I find that something like "var i=length; while(--i) ... " to be ``fairly'' elegant and quite fast in comparison to ``for'' construct and recusion (in javascript)
Just my 2 centimos