Robert said:
Take a look at this new JS function I made.
A task that would have been less onerous if you had formatted with
proper regard for the behaviour of your Usenet posting software,
particularly with regard to automatic line wrapping.
It is really simple but very powerful.
Simple and powerful are both rather subjective assessments.
You can animate any stylesheet numeric value (top left
width height have been tested), and works for both %
and px values.
But not work in, for example em units?
Works in both ie and firefox.
With 5 distinct classes of modern, more or less DOM standard, dynamic,
visual web browsers, and 40 odd scriptable browsers, being able to label
something as "works on both ... " is not that much of an achievement.
And quite an important consideration would be how it fails where it
doesn't 'work'.
Parameters
styleType = top | left | width | height
toNumber = the new value of the style
then you pass in as many ids as you would like.
There is also the significant caveat that the elements identified by
those IDs will need to have their initial pertinent CSS properties set
as inline style attributes or previously assigned with scripts.
var oldMoveTimeOut = new Array();
One of the considerations of simplicity would be the number of global
variables/Identifiers used by any code. The one associated with the
global function name is almost unavoidable but a second to hold a
storage array doubles the complexity of at least that aspect of the
script. It means that in deployment it is necessary to keep the two
global items together (in the sense of both being defined in their
context of use) and it doubles the potential for naming collisions.
function resizeObects(styleType, toNumber){
for (var loopCount = 2; loopCount <=
(resizeObects.arguments.length - 1); loopCount++)
Accessing the - arguments - as a property of the function object is
deprecated, and unnecessary as it is always possible to treat the
Identifier - arguments - as a function local variable, and of course -
arguments.length - is simpler than - resizeObects.arguments.length -.
It is also theoretically more efficient to use pre-increment on the loop
counter instead of post-increment. As it makes no practical difference
to the - for - loop, even if it is not a more efficient operation on any
given javascript implementation the fact that it is extremely unlikely
to be less efficient makes - ++loopCount - probably the preferable
choice.
window.clearTimeout(oldMoveTimeOut[resizeObects.arguments[loopCount]]);
On the first call to this function, at the very least, the value of -
oldMoveTimeOut[resizeObects.arguments[loopCount]] - is going to be
undefined. The - clearTimeout - method is described as cancelling a
timer created with - setTimeout - using the value returned from the -
setTimeout - call as its argument. The undefined argument to -
clearTimeout - must have potentially unpredictable results. Even if
shown to be safe on the couple of browers used for testing it cannot be
considered safe in cross-browser code.
It would be better to test the value of -
oldMoveTimeOut[arguments[loopCount]] - and not attempt to cancel the
timer when it is undefined (or null) and explicitly set that value to
null after the timer is cancelled (to avoid cancelling the same timer
twice).
var objectInQuestion =
document.getElementById(resizeObects.arguments[loopCount]);
This is an interesting line of code as it retrieves a reference to the
DOM Element and assigns it to a local variable, which is probably a good
idea, and then the rest of the code never uses this reference, making it
a pointless action.
var currentStyleValue =
eval("new Number(new String(document.getElementById('"
+ resizeObects.arguments[loopCount] +
"').style." + styleType +
".replace('px','').replace('%', '')));");
You have just used the - eval - function so any claims for simplicity
and power are now void.
As appears to be true of all the common cases of - eval - abuse the
superior alternative is no more than using bracket notation property
accessors in place of some dot-notation property accessors, and the just
not using - eval -. The above can be entirely replaced with:-
var currentStyleValue =
new Number(
new String(
document.getElementById(
resizeObects.arguments[loopCount]
).style[styleType].replace('px','').replace('%', '')
)
);
But given that the reference to the element has already been retrieved
and there is absolute no point in using the string primitive value that
is returned from the final - replace - call to create a String object,
as using the String object as an argument to the Number constructor will
type-convert the String object back into a string primitive. So hat
whole thing could be shorted to:-
var currentStyleValue =
new Number(
objectInQuestion.style[styleType].replace('px','').replace('%', '')
);
However, there is no good reason for using a Number object here either,
so type-converting the string primitive into a number primitive would be
sufficient, and as animation functions should be as quick as possible if
they are to work well the type-converting method should probably be the
fastest available, which is the unary plus operator:-
var currentStyleValue =
+objectInQuestion.style[styleType].replace('px','').replace('%', '');
Which will all be considerably quicker (more then 20 times faster) than
the original - eval - abuse, but could be quicker still as instead of
re-using the - objectInQuestion - reference it would be possible to
assign a reference the element's - style - object to a variable and
avoid re-resolving the reference to that object. I.E:-
var styleObj = objectInQuestion.style;
- earlier in the code and then:-
var currentStyleValue =
+styleObj[styleType].replace('px','').replace('%', '');
var d = currentStyleValue - toNumber;
This is where using a Number object to represent the current style's
value starts to look like a bad idea as it now needs to be
type-converted into a number primitive in order to evaluate this
expression and assign the result to - d -.
if (currentStyleValue < toNumber)
d = toNumber - currentStyleValue;
The comparison and subtraction operations also requi8re the Number
object to be type-converted into a number primitive.
d = Math.round(d / 5);
if (d < 1)
d = 1;
if (currentStyleValue < toNumber)
newValue = new String(currentStyleValue + d);
else
newValue = new String(currentStyleValue - d);
Again, there is absolutely no reason for creating a String object here,
as it will implicitly involve the numeric value being type-converted to
a string primitive during the creation of the String object, and then
the String object will be type-converted back into that string primitive
when it is used late in the code.
eval("document.getElementById('" +
resizeObects.arguments[loopCount] + "').style." +
styleType + " =
new String(document.getElementById('" +
resizeObects.arguments[loopCount] + "').style." +
styleType + ").replace('" + currentStyleValue +
"','" + newValue + "')");
This second - eval - abuse is as worthless as the first. The direct
replacement is:-
document.getElementById(
resizeObects.arguments[loopCount]
).style[styleType] =
new String(
document.getElementById(
resizeObects.arguments[loopCount]
).style[styleType].replace(currentStyleValue, newValue)
);
- and the optimum replacement would be more like:-
styleObj[styleType] =
styleObj[styleType].replace(currentStyleValue, newValue);
if (newValue != toNumber)
This not-equals test is going to have some potentially problematic
consequences given that CSS length values may be non-integers and the
values by which the current value is adjusted is rounded to an integer.
It means that when the CSS value is stet to a non-integer value the
function will overshoot - toNumber -, keep executing and try to
compensate in the opposite direction, undershoot, execute again,
overshoot, and so on. Putting the code into an indefinite loop.
oldMoveTimeOut[resizeObects.arguments[loopCount]] =
As the - oldMoveTimeOut - object is being assigned new members using
property names that cannot qualify as array indexes, and never used as
an assay as such, it would be better if it was created as an Object
instead of an Array.
window.setTimeout("resizeObects('" +
styleType + "', " + toNumber + ", '" +
resizeObects.arguments[loopCount] + "');",1)
}
}
It is also the case that this function will animate one CSS value but
cannot animate two values on the same element at the same time. So a
positioned element may be moved vertically or horizontally but not
diagonally.
Richard.