Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

JavaScript Newbie (moveMe)

Status
Not open for further replies.

riluve

Technical User
Mar 11, 2005
78
US
Hey folks!

This is my first adventure into the world of JavaScript and I have run into some difficulty.

I am wanting to manipulate some objects on in my document in rudamentary fashion, e.g. move them left and right.

I found out the hard way that a simple for loop with a time delay is not sufficient to do this (appearently the screen is not updated until a function is finished).

Rather, I suppose I am going to have to follow an example I found which makes re-iterative & timed calls to a simple function which does the moving.

I can get all of this to work with a very basic layout (an init routine to set up the action and an actual move routine that moves the object and then calls itself).

However, I would like to make these two routines - well I guess methods on the object in question (to prevent data collision).

So my first question: is this a reasonable decision or is there something I am missing to make this easier?

If I do take this course of action how do I proceed? It looks like I need to extend the document.Element class with my new methods and data or maybe I need to add a new class which is a subclass of the document.element . . .

Well i'm not sure. Here is my sample - two functions which I basically need to make methods of a document element:

//------------------------------------------------------------

var foo = null; // object

function moveMe()
{
foo.style.left = parseInt(foo.style.left)+2+'px'; //increase left position by 2 pixela
if (parseInt(foo.style.left) <= foo.maxright )setTimeout(foo.moveMe(),10); //pause & call again until maxright is reached.
}

function init(arg0)
{
foo = document.getElementById(arg0); // find Element
foo.style.left = (LeftX(foo) - parseInt(foo.style.marginLeft))+'px'; // find initial position and set as an absolute pixel reference
foo.maxright = 500; // set position to stop
moveMe(); //begin loop
}

window.onload = init('LoadPanel01');

//---------------------------------------------------

Thnx for any help

.
 
you will need to assign the init function to the window's onload event in something like the following manner:

Code:
window.onload = function() { init('LoadPanel01'); };



*cLFlaVA
----------------------------
[tt]( <P> <B>)[sup]13[/sup] * (<P> <.</B>)[/tt]

[URL unfurl="true"]http://www.coryarthus.com/[/url]
 
Here's a working example of how to move something with javascript (copy/paste into a new html file). I had a little fun with it as well, so it cluttered the code a bit, but it's still relatively easy to figure out:
Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "[URL unfurl="true"]http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">[/URL]
<html xmlns="[URL unfurl="true"]http://www.w3.org/1999/xhtml">[/URL]
<head>
   <script type="text/javascript">
   
   var blueDiv;
   var xpos = 1;
   var direction = 1;
   
   function init() {
      blueDiv = document.getElementById("blah");
      moveMe();
   }
   
   function moveMe() {
      xpos = xpos + (2 * direction);
      if (xpos >= 300) {
         if (document.getElementById("chk").checked) {
            direction = direction * -1;
            xpos = 300;
         }
         else {
            xpos = 1;
         }
      }
      else if (xpos <= 1) {
         if (document.getElementById("chk").checked) {
            direction = direction * -1;
            xpos = 1;
         }
         else {
            xpos = 300;
         }
      }
      blueDiv.style.left = xpos;
      window.setTimeout("moveMe()", 10);
   }
   
   window.onload = init;
   
   </script>

   <style type="text/css">

   * {
      border:0px;
      padding:0px;
      margin:0px;
   }

   div#container {
      border:1px solid black;
      width:350px;
      height:50px;
   }

   div#blah {
      width:50px;
      height:50px;
      background-color:blue;
      position:absolute;
      top:1px;
      left:1px;
   }

   </style>
</head>
<body>
   <div id="container"></div>
   <div id="blah"></div>
   <input type="checkbox" id="chk" /> Make me bounce
</body>
</html>

-kaht

Looking for a puppy?

[small]Silky Terriers are small, relatively odorless dogs that shed no fur and make great indoor pets.[/small]
 
yeah these things are great thanx, but the code I showed already works.

What I need to do is make it object oriented.
 
but the code I showed already works
Umm..... the exact code you showed works? From a quick scan I can tell you that this line window.onload = init('LoadPanel01'); should not work - when you pass a function reference to an event handler via javascript, you're doing exactly that - passing a reference. For that reason you cannot provide arguments for the function call, at least not without a little trickery.

Anyway, I'll go ahead and provide an object oriented example of my code above. It's important to note that the blueDiv object must have global scope, otherwise the window.setInterval call will not recognize the variable. That's one of the major pains of setTimeout and setInterval - you cannot easily pass a "this" reference to the function since the function is passed as a string. The function call string is evaluated much the same way an eval command would.

Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "[URL unfurl="true"]http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">[/URL]
<html xmlns="[URL unfurl="true"]http://www.w3.org/1999/xhtml">[/URL]
<head>
   <script type="text/javascript">
   
   var blueDiv;
   
   function init() {
      blueDiv = new bigBlueBox("blah");
      window.setInterval("blueDiv.moveMe()", 10);
   }
   
   function bigBlueBox(objRef) {
      this.obj = document.getElementById(objRef);
      this.xpos = 1
      this.direction = 1;
   }
   
   bigBlueBox.prototype.moveMe = function () {
      this.xpos = this.xpos + (2 * this.direction);
      if (this.xpos >= 300) {
         if (document.getElementById("chk").checked) {
            this.direction = this.direction * -1;
            this.xpos = 300;
         }
         else {
            this.xpos = 1;
         }
      }
      else if (this.xpos <= 1) {
         if (document.getElementById("chk").checked) {
            this.direction = this.direction * -1;
            this.xpos = 1;
         }
         else {
            this.xpos = 300;
         }
      }
      this.obj.style.left = this.xpos;
   }
   
   window.onload = init;
   
   </script>

   <style type="text/css">

   * {
      border:0px;
      padding:0px;
      margin:0px;
   }

   div#container {
      border:1px solid black;
      width:350px;
      height:50px;
   }

   div#blah {
      width:50px;
      height:50px;
      background-color:blue;
      position:absolute;
      top:1px;
      left:1px;
   }

   </style>
</head>
<body>
   <div id="container"></div>
   <div id="blah"></div>
   <input type="checkbox" id="chk" /> Make me bounce
</body>
</html>

-kaht

Looking for a puppy?

[small]Silky Terriers are small, relatively odorless dogs that shed no fur and make great indoor pets.[/small]
 
LOL - well I don't know what to tell you,

window.onload = init('LoadPanel01');

works for me in FF, IE, and Opera. (see it for yourself
Unfortunately, the entire reason to make the code object oriented is to eliminate the global variable - so, uh well . . . sadness.

There must be a way to do this without having such a fatal flaw.
 
oh, I should say, its only moving the document element to the right as far as the 500px point, but the original position is 50% of the page width, therefore to see it work you may have to decress your window width to less than 1,000px.
 
it's working only by coincidence. you are including your JS files at the bottom of your page. doing the onload=init('blah') just calls the function at that exact point. it doesn't attach the function call to the onload event, you just can't tell because you're still calling the function.

kaht is correct.



*cLFlaVA
----------------------------
[tt]( <P> <B>)[sup]13[/sup] * (<P> <.</B>)[/tt]

[URL unfurl="true"]http://www.coryarthus.com/[/url]
 
Ah, I see. I guess I will look into this then.

OK, so any ideas about making an object move through an attached, encapsulated (not needing global variables) method?
 
Read here, scroll down to the section labeled "no brackets". As you'll see, what gets set to the event handler is the result of the function that is called - which means that your function is fired immediately (as cory pointed out above). What you want to do is pass a reference to the function (like I stated above). Here's an example of how this can explode:
Code:
<script type="text/javascript">

function init(str) {
   alert(document.getElementById(str).value);
}

window.onload = init("blah");

</script>

<input type="text" value="bet I don't get alerted" id="blah" />
In this example you are attempting to set init to run once the page loads, but what you're actually doing is running init immediately. You'll get an error because the object being referenced (blah) does not exist yet (because the page hasn't loaded) Now, what i said above about the trickery - you'll have to create a new function if you want it to work how you're expecting (being able to pass parameters) - and this new function has to invoke the function you actually want to run once the page loads. Here's the same example (only working this time):
Code:
<script type="text/javascript">

function init(str) {
   alert(document.getElementById(str).value);
}

window.onload = function () {init("blah");};

</script>

<input type="text" value="bet I don't get alerted" id="blah" />

As far as keeping the object out of globalscope - I don't see a way that you'll be able to do this and still use setInterval or setTimeout. Maybe someone else will have an idea.

-kaht

Looking for a puppy?

[small]Silky Terriers are small, relatively odorless dogs that shed no fur and make great indoor pets.[/small]
 
Kool - thnx

ok, so what other way might I have to make this work without either setInterval or setTimeout?
 
OK, someone please correct me if I am wrong, but doesn't this implementation have a fatal flaw that renders it useless?

It seems viable for only a single instance - such that it can only be used to move a single thing at a time or else the global variable collides with the new instance.

If this is the best JavaScript has to offer . . . . well it just seems like an insane limitation/implementation at both the implementation and structual levels.

The only logical reason I can see to not be screaming bad names about this is that it may have been done on purpose to hinder malicious/viral type coding. But really it doesn't seem like even like a fool-proof anti-spam device either. So what's the deal?

OK, so I am thinking the only 'decent' way to implement this in a real-world useful fashion (from the library sense) is to set up the global variable as an array such that each time a new instance was started, the array could be used as an index to refer to each instance. However, this starts the problem of having a needlessly bloated array which should be managed in a way to remove instances once they have been serviced.

yuk

is there any hope or good news to this situation?


.
 
It seems viable for only a single instance - such that it can only be used to move a single thing at a time or else the global variable collides with the new instance.

false to the max. just change your onload stanza to this:

Code:
window.onload = function () {
    init("blah1");
    init("blah2");
    init("blah3");
    init("blah4");
};



*cLFlaVA
----------------------------
[tt]( <P> <B>)[sup]13[/sup] * (<P> <.</B>)[/tt]

[URL unfurl="true"]http://www.coryarthus.com/[/url]
 
You'd need a slight change to my code to do that cory:
Code:
function init() {
   blueDiv = new bigBlueBox("blah");
   window.setInterval("blueDiv.moveMe()", 10);
}
The setInterval call would have to build the string off of the parameter passed - which means that it would have to create a global variable equivalent of blueDiv for each div that is to be moved on the screen - which isn't hard to do at all.

Anyway, I think I'm done with the suggestions. It's a lot of work with no [purple]pay[/purple] - and what I've posted should be enough to get the OP on the right track.

-kaht

Looking for a puppy?

[small]Silky Terriers are small, relatively odorless dogs that shed no fur and make great indoor pets.[/small]
 
I really don't see how it's possible to dynamically create a new blueDiv equivalent for each invocation without using an array.
 
@riluve
I've always thought you could create global variables inside a function in js? just omit the "var" - doesn't that get you 90% of the way?

and doesn't the post above give you what you are looking for? you need to tweak the init function a bit and use the stanza that Cory provides.

(tweaked code)
Code:
function init(itm) {
   global_itm = new bigBlueBox("itm");
   window.setInterval(global_itm.moveMe()", 10);
}
window.onload = function() {
  init("blah1"); 
  init("blah2"); 
  ...etc...
}

and (at least in firefox) you have to tweak Kaht's object so that this line
Code:
this.obj.style.left = this.xpos;
becomes
Code:
this.obj.style.left = this.xpos + "px";
 
Yeah I have done all of that and found a really ugly bug in IE as well, however none of this solves the ORIGINAL problem that I have been re-iterating from the begining . . .

A global variable is a terrible solution. OK, sure, its "good enough" for say a half dozen little things rolling around on the screen, but say you wanted to individualy animate a lite snow storm - that could be displayed eternally (as long as a browser window is open).

If you just re-cycle you individual snowflakes, this still may be sufficient, but this still seems to be an unacceptible limitation.

Rather, you should be able to draw each snowflake individualy, let it drop in an organic/random fashion, and all data associated with the snowflake should be deleted when it falls off the screen. See what I mean? If you can't do this then what is the freaking point of even using object oriented design?

If you can encapsulate the entire object and its data, you only need system resources for the objects currently active/alive.

If you stoop to using ANY form of a global variable, you either have to let it grow indefinately until it overruns system resources and crashes the system, or you have to stop periodically and clean up after the objects that have passed into oblivian. OK, I suppose the other thing you could do is set an upper limit to the number of objects and then just wrap around when you reach the limit . . . but again, this is the exact problem best solved with OOP and can not other wise be both "generic" and "optimal" at the same time.

OK, let me say, I am not trying to actually individualy animate a snow storm, I am just using this example as i wrap my head around what seems like an unreasonable limitation.

if you care to see an implementation of cLFlaVA's & kaht's code with bug fixes and and the kludge that I can't seem to overcome (global variable).

//-------------------------------------------------

<head>
<style type="text/css">
*
{
border:0px;
padding:0px;
margin:0px;
}

div#container
{
border:1px solid black;
width:350px;
height:50px;
position: absolute;
top: 50%;
left: 50%;
margin-left:-175px;
margin-top:-25px;
z-index: 3;
}

.slice
{
height: 50px;
width: 10px;
position:absolute;
}
</style>

<script type="text/javascript">
//this must grow every time you start a new animation!<yuk>
var slider = new Array();
newIndex.foo = 0;
function newIndex() { return newIndex.foo++; }

function initSlider(arg0)
{
lIndex = newIndex();
//fix for crazy IE(WXP) interval bug
if ((lIndex%3)==2) lIndex = newIndex();
//create decent difference for each instance's interval
lInterval = (lIndex*9)+(10+lIndex);
slider[lIndex] = new protoSlider(arg0, lIndex);
window.setInterval("slider["+lIndex+"].moveMe()", lInterval );
//append test data to document ---------
var newP = document.createElement("P");
var pText = document.createTextNode(arg0+" Index: "+lIndex+" Interval: "+lInterval+" Modulo: "+lIndex%3);
newP.appendChild(pText);
document.body.appendChild(newP);
//-------------------------------

function protoSlider(arg0, arg1)//(Element.ID,Element.leftX)
{
this.obj = document.getElementById(arg0);
this.xpos = ((arg1*15)+1)
this.direction = 1;
}

protoSlider.prototype.moveMe = function ()
{
this.xpos = this.xpos + (this.direction);
if (this.xpos >= 340)
{
this.direction = this.direction * -1;
this.xpos = 340;
}
else if (this.xpos <= 1)
{
this.direction = this.direction * -1;
this.xpos = 1;
}
this.obj.style.left = this.xpos+'px';
}

window.onload = function ()
{
initSlider("Black");
initSlider("Red");
initSlider("Orange");
initSlider("Blue");
initSlider("Purple");
initSlider("Green");
};
</script>
</head>


<body>
<div id="container">
<div id="Red" class="slice"
style="background-color:red; left:0px; z-index: 5;"></div>
<div id="Blue" class="slice"
style="background-color:blue; left:20px; z-index: 10;"></div>
<div id="Black" class="slice"
style="background-color:black; left:40px; z-index: 15;"></div>
<div id="Purple" class="slice"
style="background-color:purple; left:60px; z-index: 20;"></div>
<div id="Orange" class="slice"
style="background-color:eek:range; left:80px; z-index: 25;"></div>
<div id="Green" class="slice"
style="background-color:green; left:100px; z-index: 30;"></div>
</div>
</body>
//-------------------------------------------------


 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top