Forum Moderators: open

Message Too Old, No Replies

Function Arguments Problem

         

Avalon

8:52 pm on Aug 19, 2004 (gmt 0)

10+ Year Member



I'm building a smooth menu dropdown but have run into a brick wall adding a 3rd function argument to the openMenu() function call. The code runs as listed with 2 arguments, but when I insert the "table1" object as an additional parameter (variable), it chokes. I'm at a loss to figure it out. Anybody have a clue what I'm doing wrong?
Here's the menu open portion of the script. Again, as long as the table1 is hardcoded, it's operational.
============================
<html>
<head>

<script language="JavaScript">

function openMenu(xcoord,ycoord){

if (ycoord < 200){
//td1.innerHTML = ycoord;
table1.style.left = xcoord;
table1.style.top = ycoord;
table1.style.position = 'absolute';
ycoord = ycoord + 1;
setTimeout("openMenu(" +xcoord+","+ycoord+")",10);
}

}
</script>

</head>
<body>

<table border="1" style="position:absolute;left:200px;top:40px;">
<tr>
<td onmouseover="openMenu(0,0)">First Item</td>
</tr>
</table>

<table bgcolor="#dedede" border="1" id="table1" style="position:absolute;left:0px;top:0px;border-collapse:collapse;">
<tr>
<td>test 1st link</td>
</tr>
<tr>
<td>test 2nd link</td>
</tr>
<tr>
<td>test 2nd link</td>
</tr>
</table>

</body>
</html>
=========================================

Birdman

9:16 pm on Aug 19, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Welcome to Webmaster World, Avalon!

It should be cut and dry.

1) Add the variable to the function call
<td onmouseover="openMenu(0,0,'table1')">

2) Add the argument to the function

function openMenu(xcoord,ycoord,table_id){

if (ycoord < 200){
//td1.innerHTML = ycoord;
table_id.style.left = xcoord;
table_id.style.top = ycoord;
table_id.style.position = 'absolute';
ycoord = ycoord + 1;
setTimeout("openMenu(" +xcoord+","+ycoord+",table_id)",10);
}

}

I imagine that would do what you want.

Avalon

9:53 pm on Aug 19, 2004 (gmt 0)

10+ Year Member



Thank you Birdman. Nice to have someone to discuss these things with. No, if you try the substitution you gave me, you'll see it doesn't work.

I've already been down that road. I gave you only the code that WORKS, without the additional variables. Once the table1, table_id argument var is instituted, it crashes. There are some syntactical differences between what you suggested, and my attempts, but I've tried it both ways without success.

I've written entire apps with this style of coding, so I'm baffled.

Birdman

10:28 pm on Aug 19, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Hmmm...let's see. Have you tried it this way? I think it needs the single quotes because it's a string. I had left quotes out altogether in my first example because I'm not very familiar with setTimeout(). Sorry.

setTimeout("openMenu(" +xcoord+","+ycoord+",'"+table_id+"')",10);

Bernard Marx

12:04 am on Aug 20, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Yes, beat me to it. setTimeout's tricky. It's strings all the way.

I'll post mine anyway. There's a couple of changes.
Esp. that you need to use getElementById, otherwise it'll only work in IE (can't reference elements using id alone)

..and stricter browsers will want "px" on the positions.

-- html --
<td onmouseover="openMenu(0,0,'table1')">First Item</td>

-- script --

function openMenu(xcoord,ycoord, elmId)
{
if (ycoord >=200) return

var style = document.getElementById(elmId).style
style.left = xcoord+"px";
style.top = ycoord+"px";
// style.position = 'absolute'; // X not needed. It's already in the css
ycoord += 1;
setTimeout("openMenu(" +xcoord+","+ycoord+",'"+elmId+"')",10);
}


Here's a different take on it, that avoids those strings.

function openMenu(xcoord,ycoord, elmId)
{
var style = document.getElementById(elmId).style
repeat()
function repeat()
{
ycoord += 1
if (ycoord>=200) return
style.top = ycoord+"px"
setTimeout(repeat,10)
}
}

// 1. Inner function has access to outer function's local vars.
// 2. setTimeout can accept a function reference.

Avalon

4:21 pm on Aug 20, 2004 (gmt 0)

10+ Year Member



Thanks to both of you for the insights. Unfortunately, to date, nothing has been successful.

The function call itself:

<td onmouseover="openMenu(0,0,table1)">First Item</td>

...shouldn't need single quotes around the table1 object (it works well with that object alone as the argument, if I take away the movement); nor should I need quotes around the table_id reference in the setTimeout() function; should be like this:

setTimeout("openMenu(" +xcoord+","+ycoord+ "," +table_id+ ")",10);

...but it doesn't work either way.

You guys ARE ON THE RIGHT TRACK though. I'm convinced this is a String issue in the setTimeout() call...but I'm really familiar with this type of string/var manipulation and syntax. I know when I figure this out, I'm gonna be really embarrassed...but until then, if anyone has the answer, please share it with me.

Thanks again

Bernard Marx

5:22 pm on Aug 20, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Hmm something wrong somewhere.
I've had working versions using both of the functions I posted (Bird's too, I think). I'll post a complete page when I get one back together again.

(as above)
setTimeout("openMenu(" +xcoord+","+ycoord+ "," +table_id+ ")",10);

(with arbitrarily chosen var values) this will produce:

setTimeout("openMenu(100,200,table1)",10)

setTimeout is asked to execute the statement:

openMenu(100,200,table1)// in a global context

In those args there is a variable, table1.
In most browsers, that variable is undefined.
In IE it will hold a reference to the right element, but openMenu doesn't take an object reference parameter, it takes an id string.

What you need (if you don't want the nested-function version)is:

setTimeout("openMenu(" +xcoord+","+ycoord+ ",'" +table_id+ "')",10);

Birdman

5:54 pm on Aug 20, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



No, if you try the substitution you gave me, you'll see it doesn't work.

True, I didn't take the time to test it. If you tried the substitutions Bernard gave, you'll see that they BOTH work.

This works for me:

<script type="text/JavaScript">

function openMenu(xcoord,ycoord,tid){
t = document.getElementById(tid);

if (ycoord < 200){

t.style.left = xcoord;
t.style.top = ycoord;
t.style.position = 'absolute';
ycoord = ycoord + 1;
setTimeout("openMenu("+xcoord+","+ycoord+", '"+tid+"')",10);
}

}
</script>

<td onmouseover="openMenu(0,0,'table1')">First Item</td>

shouldn't need single quotes around the table1 object

You do need the quotes. It is not an object, it is a string. It will not be an object until you use that string in a javascript assignment:

function openMenu(xcoord,ycoord,tid){ // tid is a string
t = document.getElementById(tid); // t is an object

nor should I need quotes around the table_id reference in the setTimeout() function;

Once again, it's just a simple string so it needs quotes

setTimeout("openMenu("+xcoord+","+ycoord+", '"+tid+"')",10);

Notice I put the string variable as the last arg, not the object(t). Why, because the function is expecting a string at first and then creates the object with that string.

Hope that helps straighten things out.

Bernard Marx

8:54 pm on Aug 20, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Here's a working page (tested IE5 & NS6) with 2 implementations of the function.
Just swap the XX between #1 and #2.

I made 2 changes:
1. Netscape didn't want to move a table today, so I put it in a DIV and moved that instead.

2. When running #1, if you mouseover again strange things start to happen, because the function doubles up. #2 has a facility to block further calls until running has stopped. This can be done to #1 as well, but I think it would require a global variable.

<html>
<head>
<script language="JavaScript">

function openMenu(xcoord,ycoord, elmId)
{
if (ycoord >=200) return

var style = document.getElementById(elmId).style
style.top = ycoord+"px";

ycoord += 1;
setTimeout("openMenu(" +xcoord+","+ycoord+",'"+elmId+"')",10);
}

function XXopenMenu(xcoord,ycoord, elmId)
{
var elm = document.getElementById(elmId)
if(elm.openMenuRunning) return
elm.openMenuRunning = true

repeat()
function repeat()
{
ycoord += 1
if (ycoord>=200){
elm.openMenuRunning = false
return
}
elm.style.top = ycoord+"px"
setTimeout(repeat,10)
}
}

</script>

</head>
<body>

<table border="1" style="position:absolute;left:200px;top:40px;">
<tr><td onmouseover="openMenu(0,0,'table1')" >First Item</td></tr>
</table>

<div id="table1" style="position:absolute;left:0px;top:0px;border-collapse:collapse;">
<table bgcolor="#dedede" border="1">
<tr><td>test 1st link</td></tr>
<tr><td>test 2nd link</td></tr>
<tr><td>test 2nd link</td></tr>
</table>
</div>

</body>
</html>

Avalon

2:29 am on Aug 21, 2004 (gmt 0)

10+ Year Member



Well folks, I told you I'd be embarrassed...and I wasn't wrong. It appears that somewhere along the way, I missed some basic principles. Never belonged to a Javascript Forum before, and I've been isolated doing web application development for so long (Cold Fusion and Javascript), that sometimes it's easy to fall into bad habits based on what works.

If it means anything, it's really great knowing there are sharp minds and good people out there willing to help.

Having said that, maybe you can explain the apparently basic principle I'm overlooking (or never learned) here...why can I reference table1 in the function call below without quotes and without getElementByID (I've always used the latter ONLY for Netscape compatibility). This works when I'm simply applying style properties to it. I've removed the extra arguments for demonstration and taken away the movement...the function simply moves the table to the 200px location.

Again...much thanks to you both.

<head>
<script language="JavaScript">
var ycoord=200;
var xcoord=0;
function openMenu(table_id){
table_id.style.left = xcoord;
table_id.style.top = ycoord;
table_id.style.position = 'absolute';
}
</script>
</head>
<body>

<table border="1" style="position:absolute;left:200px;top:40px;">
<tr>
<td onmouseover="openMenu(table1)" >First Item</td><td id="td1">&nbsp;</td>
</tr>
</table>

<table bgcolor="#dedede" border="1" id="table1" style="position:absolute;left:0px;top:0px;border-collapse:collapse;">
<tr>
<td>test 1st link</td>
</tr>
<tr>
<td>test 2nd link</td>
</tr>
<tr>
<td>test 2nd link</td>
</tr>
</table>

</body>

Bernard Marx

1:17 pm on Aug 21, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Don't humble yourself too much, there won't be room for me underneath.

If your application development has been intranet-based, then you will have been in the nice cosy world of internet explorer (I presume).IE (at present) keeps backward compatibility with it's previous proprietary - sometimes just plain easier - element referencing methods. This does lead to some confusion when moving into cross-browser territory.

I'll just cover elements in general, as there are even more ways of referencing images, and forms/form elements.

<div id="table1">

1. Using the element's id alone (your 'usual practice'):[IE]

table1.style.left

This is IE-only ; Still supported.
It can lead to a conflict, when using XB methods.
..........................................................................
Put simply, you might want to put an element into a global variable for easy reference, using the id for a variable name to make things easy ( and emulating your practice):

var table1 = document.getElementById('table1')

This will cause an error in IE (only with a global var). For some reason, if you declare the variable first it's OK

var table1
table1 = document.getElementById('table1')

..........................................................................
2. Ref'ing as a member of the document's all collection:[IE]

document.all.table1
document.all['table1'] // when using variables
document.all('table1') // also accepts ()

Again, IE-only; Still supported..
Every element has an all collection (not just doc), of all the elements it contains. Elements can be referenced by id, name, or source-order index. A collection is returned when elements share the same name.

3. Ref'ing as a member of the document's layers collection:[NS 4]

document.layers.table1
document.layers['table1']

Netscape 4 only; no longer supported

This can only reference
<layer> which is NS-only
<div> only when absolutely positioned

The important thing is that, in the NS4 DOM, each layer/pos:div is a sub-document. So, to ref nested layers, you need to recursively loop through all the layers.

Modern Standard

4. document.getElementById('table1')

IE5+, NS6+, Moz, most other things.

This is the one to use, and we can educate IE4, and NS4 (assuming no nesting) to use it:

if(!document.getElementById)
document.getElementById = document.all ¦¦ function(id){return this.layers[id]}
// ¦¦ to pipes

Birdman

2:56 pm on Aug 21, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I've been using this cross-browser object script for a while now and it seems to work well. It definately makes my scripts shorter because I don't have to use the long references for every var.

var DHTML = (document.getElementById ¦¦ document.all ¦¦ document.layers);

function getObj(nm)
{
if (document.getElementById)
{
this.obj = document.getElementById(nm);
}
else if (document.all)
{
this.obj = document.all[nm];
}
else if (document.layers)
{
this.obj = document.layers[nm];
}
return this.obj;
}

function fObj(nm){
this.obj = document.forms[0].elements[nm];
return this.obj;
}

I saved this script as cross_browser_object.js and then pull it in before running any js scripts.

<script type="text/javascript" src="/includes/cross_browser_object.js"></script>

<script type="text/javascript">
//In this script, I can access any object by it's ID like this:

myVar = getObj("id_of_some_element").innerHTML;
myVar2 = getObj("id_of_some_element").style.position;
etc...

//If it's a form element, I use the fObj() function

myVar3 = fObj("name_of_some_form_element").value;
myVar4 = fObj("name_of_some_form_element").checked;
etc...
</script>

Bernard Marx

5:05 pm on Aug 21, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Birdman, A couple of things:

There's nothing wrong with having a general function for referencing objects, like getObj, but I was trying to up the ante. There are a couple of features in my method that "up it" a bit.

1. It's more efficient, since the code is only executed once. From then on, calls to getElementById involve no code branching.

2. Allows you to (at least attempt to) write modern standard code.

3. It's a tad shorter.

You have a global variable, DHTML, which you haven't used. The funny thing is that, if NS4 allowed access to the document layers collection via round brackets, then you could use DHTML as an accessor:

var myElm = DHTML('elmId')

(I think, since the context,

document.
, is preserved)

I'm not trying to wind you up, but there is an issue with the current form of

getObj
and
fObj
- although you won't notice it when you run them.

Since the functions are called globally, the keyword,

this
, refers to the window, so
obj
is being set as a
window
property (aka global variable). This is a little less efficient, and will conflict you are trying to use a global variable called,
obj
(unlikely, I know).

In both functions, declare

obj
at the top as a local var, then remove this..
It'll be a bit shorter then too.