When setting the position of a positioned element by assigning values
to the CSS top
and left
properties of the
element's style
object it is necessary to provide the
value as a string with the units of the length (usually "px")
following the value. Modern standards compliant browsers require this,
older and more tolerant browsers may assume that values without units
represent "px" units but they can handle string values which
include the CSS units. Unfortunately some browsers want the
top
and left
values provided as numbers and
complain (or don't react) if they are provided as strings with units.
A technique for handling this requirement is based on the observation
that when a browser expects the top
and left
values to be set as a number of pixels a typeof
test of
either the top
or left
properties will return
"number" while if that same test returns "string"
the browser either will require the CSS units to be appended to the
values set as a string or it will successfully handle a value in that
format.
Once the object on which the top
and left
values are to be set is available one of its top
or
left
properties can be tested with tyoeof
and an appropriately scoped variable set to either the string
"px"
or the number zero.
var cssUnitsOrZero = (typeof styleObjectRef.top == 'string')?'px':0;
Then when the position has been calculated (as a number) it can be
assigned to the appropriate property with the value derived as the
result of the test applied to it using the +
operator.
Because the +
operator is a duel role addition and
string concatenation operator, and decides which action it will take
based on its operands, the application of the +
operator
to a position value as a number will depend on the type of the value
derived from the preceding typeof
test on the
top
or left
properties.
styleObjectRef.top = numericPosition + cssUnitsOrZero;
/* If the - numericPosition - value was 125, for example, and -
cssUnitsOrZero - was zero, the value assigned would be:-
styleObjectRef.top = (125 + 0);
- which is the number 125. If - cssUnitsOrZero - was "px" then
the assignment would be:-
styleObjectRef.top = (125 + "px");
- which is the string "125px".
*/
If the tested top property was not a string cssUnitsOrZero
will be the number zero and the +
operator will have two
numeric operands and will perform numeric addition, assigning a numeric
value to the property, unmodified by the addition of zero. But if the
tested property was a string, so cssUnitsOrZero
was
assigned the string value "px"
, the
+
operator will recognise that one of its operands is a
string and type-convert the other (the calculated position value) into
a string and perform concatenation, appending the
"px"
CSS units string to the string
representation of the calculated position and then assign that value to
the property.
This fulfils the requirement to provide CSS units where needed while
providing the vlaues in numeric form whenever that is the type of value
expected. It is inefficient to perform the typeof
test on
a relevant property whenever a value is to be assigned, it is better to
perform the test once and then re-use the same
cssUnitsOrZero
variable for all subsequent assignments.
The setTimeout
and setInterval
functions
provided by web browsers are commonly used with a string of javascript
source code as their first argument, frequently a string that does no
more than call a separately defined function:-
function invokedWithSetTimeout(){
... // do something.
}
...
var timer = setTimeout("invokedWithSetTimeout()", 100);
The string argument is interpreted and executed after the interval
specified as the second argument (in milliseconds). In the preceding
code the invokeWithSetTimout
function would be called.
On more recent browsers (easily the majority these days) the first
argument to the setTimeout
and setInterval
functions can be a reference to a function. Instead of having to
interpret and execute a string of javascript source code the function
referred to is just executed after the interval. That will be more
efficient as considerably smaller overhead would be involved in the
execution of the function.
var timer = setTimeout(invokedWithSetTimeout, 100);
Unfortunately a number of older browsers such as IE 4 and Opera 5 do
not understand the function reference versions of
setTimeout
and setInterval
, leaving the use
of string arguments as apparently the best method for cross-browser
support. However, it is in the nature of loosely typed javascript that
when a native code function such as setTimeout/Interval
is
expecting a string argument but is instead passed a value of another
type that value will be type-converted into a string. That doesn't help
much when the process of type-converting a function into a string
results in an implementation dependent representation of the function,
as is normally the case.
Type-converting of a function to a string is done with the
function's toString
method, usually
Function.prototype.toString
. Methods of a
prototype
can be overridden by methods of the function
object itself and an overriding toString
method can return
any value its creator desires. That fact provides a means of using the
more efficient function reference arguments with
setTimeout/Interval
and still supporting browsers that can
only handle string arguments, by assigning a custom
toString
method to the function that is referred to in the
argument to setTimeout/Interval
. That custom method would
return the string that would have been used in the
setTimeout/Interval
function call. If the function
reference is type-converted to a string that string represents the
javascript source code that would be needed to invoke the function. If
it is not type-converted then setTimeout/Interval
can
invoke the function directly (and without the overhead involved in
interpreting and executing the string of source code).
function invokedWithSetTimeout(){ ... // do something. } /* Assign a function expression to a toString property of the function object referred to by the identifier - invokeWithSetTimeout -, overriding - Funciton.prototype.toString -. The new toString function returns the javascript source code needed to invoke a call to the function. */ invokedWithSetTimeout.toString = function(){ return "invokedWithSetTimeout();"; } ... var timer = setTimeout(invokedWithSetTimeout, 100); /* If the browser will only recognise string values as the first argument to - setTimeout - then the function referred to by the identifier - invokeWithSetTimeout - will be type-converted to a string by calling its - toString - method, which will return the string " invokedWithSetTimeout();" and that string will be employed by the setTimeout function. Efectivly the same as calling the setTimeout function as:- var timer = setTimeout("invokedWithSetTimeout()", 100); */