DHTML-ish function question...
April 13, 2005 12:01 PM   Subscribe

I keep trying to come up with a generalized way to scale a webpage image in a gradual fashion using JavaScript. I can do it in a specific way (with one hard-coded named image); it's the generalized form (that would work with any rolled-over image) that's giving me fits...

Here's what I've come up with:

function growImage(){
// this is triggered by a mouseOver event...
// increase image's height
this.height++;
// check and see if it's < 200 pixels in size if (this.height 200) { // set self-recursive timer my_timeout=setTimeout(" growImage;" , 1000); } } /code>

What seems to happen is it increments the size on the first pass, and then loses the global "this" value when it iterates (I think). I want to be able to call this function from any image on the page, and have that image grow and shrink in a smooth fashion (like the animated Google toolbar that surfaced lately).

Perhaps in a more generalized question (there I go, searching for a generalized solution...), where's a good place to ask a question like this? There are so many sites, but I don't know what's a "good" one to quiz...

Thanks in advance.
posted by jpburns to Computers & Internet (10 answers total)
 
This has an example of a growing image. I'm not sure what sections of javascript you'd be able to hack out. It appears to use something called prototype which doesn't have documentation.
posted by seanyboy at 12:20 PM on April 13, 2005


On investigation, the prototype library and ...
<head>	<title>Effects demo page</title>	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />	<script src="prototype.js"></script></head><img src="/images/upload.jpg" style="width:130px;" onmouseover="new Effect.Scale(this,150)" onmouseout="new Effect.Scale(this,100)"/>
would do it.
posted by seanyboy at 12:29 PM on April 13, 2005


Use getElementById and pass the element's id in the setTimeout.



<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title> Untitled
<script language="javascript">

function scaleTo(elementId, maxY){

var element = document.getElementById(elementId);
var height;
if(element.style.height == null){
height = asNum(element.style.height);
} else{
height = element.height;
}
if(height < maxy){br> element.style.height = (height + 10) + "px";
element.timeout = setTimeout("scaleTo('"+elementId+"',"+maxY+");", 50);
}
else{
// all done.
}
}

function asNum(size){
alert(size);

if(size.indexOf("px") != -1){
return parseInt(size.substring(0, size.indexOf("px")));
} else {
return parseInt(size);
}
}

</script>
</head>
<body>
<img id="fig1" src="news.jpg" width="79" height="57" border="0" onmouseover="scaleTo(this.id, 300);" />

</body>
</html>
</code>

posted by Loser at 12:31 PM on April 13, 2005


Response by poster:
Thanks, seanyboy, but I wanted to avoid including a huge library I didn't fully understand just in order to achieve the effect (I saw that page, which is great... but I'm looking for a "simple" way to do it...).

Loser:
Thanks for the example. I hope I can adapt it to my usage. I'm using a "generalized" (there's that word, again) handler on the onLoad event to assign mouseOver, mouseOut, and mouseDown listeners to every image.

// called on load of document

function init(){
// grab div named "content"
var content=document.getElementById("pictures");
// search for image tags
var thumbs = content.getElementsByTagName("IMG");
// search for anchor tags
var pics = content.getElementsByTagName("A");
// go through image tags and assign function to mouseover and mouseout
for (i=0; i< thumbs.length; i++){br> thumbs[i].onmouseover = makeBig;
if (pics[i].captureEvents) pics[i].captureEvents(Event.MOUSEOVER);
thumbs[i].onmouseout = makeSmall ;
if (pics[i].captureEvents) pics[i].captureEvents(Event.MOUSEOUT);
}
// go through anchor tags and assign function to click
for (i=0; i< pics.length; i++){br> pics[i].onclick= showImage;
if (pics[i].captureEvents) pics[i].captureEvents(Event.CLICK);
}

}


But if I add parameters (like in your example) to the function call, it'll fire the function (once for every image on the page), rather than just assign it.

I still have to plow through your code; maybe I can still glean what I need from it. Damn! I wish I was more of a propeller-head and less of an artsy-guy.
posted by jpburns at 1:16 PM on April 13, 2005


Best answer: in more general terms, you want to stick your "this" in a closure. i can't remember javascript, but i would guess that you want something like:
function growImage(){
var myfunc = function self() {
if (this.height < 200) {
this.height++;
setTimeout(self, 1000);
}}
myfunc();
}
note how the function self is defined inside a context where "this" has a value. that means that every time that function is called, "this" has the value you expect.

for more info, google for "javascript closures"; the above also uses a recursive function (self), but that's only a bfd if you're used to languages that don't support such things.
posted by andrew cooke at 1:19 PM on April 13, 2005


Best answer: (it might not be obvious, but function "growImage" is defined when the page is read first time, and "this" only has a value when it is called because of the magic of events. in contrast, "self" is defined when "growImage" is executed, so the definition of the function freezes the value of "this". not sure if this helps any. basically, you were right in saying that "this" is undefined on the second call, hence the need for the closure.)
posted by andrew cooke at 1:26 PM on April 13, 2005


Response by poster: andrewcooke:

Thanks for the great pointers. I obviously have a lot of reading and learning to do. Here's what I got to work, by the way:

function growImage(){
var myfunc = function blah() {
if (wha.height < 60) {br> wha.height++;
setTimeout(blah, 1);
}
}
var wha = this;
myfunc();
}


I had to assign the object reference to another var in the outer function for it to make it to the inner function ("this" was "undefined" in the inner one...).

Now if it only worked in Safari...

Thanks, again...
posted by jpburns at 2:22 PM on April 13, 2005


ok - looks like "this" doesn't work because it's "magic" in some way. but if you managed to fix it it means you get the idea.

incidentally, this kind of thing is pretty heavy programming from some points of view, so i wouldn't feel bad about not knowing it (you said you had a lot to learn). on the other hand, it's basic functional programming (and javascript is a way-cool language, much more impressive than many people realise, that combines functional and oo programming ideas). if you're interested in learning more about functional programming, try this book.
posted by andrew cooke at 2:34 PM on April 13, 2005


some languages like variables to declared before they are referenced. it's vaguely possible that if you move var wha = this to before the definition of the function self it might be more portable. as i said, i don't remember enough of javascript to know if this is critical for that language.
posted by andrew cooke at 6:06 AM on April 14, 2005


Response by poster: Here's a version that works in Safari (please ignore the extraneous "br>" crap... something is busted using <code> tags):


function makeBig(){
function blah() {
if (wha.height < 60)br>
{
wha.height++;
setTimeout(blah,10);
}
}
var wha = this;
blah();
}


... something about just declaring the inner function without assigning it to a variable seems to fix it...

Thanks, again...
posted by jpburns at 7:41 AM on April 14, 2005


« Older Who is the brunette with pigtails in the Sonic...   |   Why the 225 lb bench? Newer »
This thread is closed to new comments.