Milonic provide full featured pull down web menus for some of the worlds largest companies
click here to see what it can do for you

Download Milonic DHTML Menu
Buy Milonic DHTML Menu

Back To Start Of Archive
Taken From The Forum: Archived Topics for the old Version 3.0 JavaScript Menu
Forum Topic: Click to view post
Last Updated: Saturday July 14 2012 - 06:07:31

Relative positioning with dynamic (ASP) menu


Poster: xMANIGHTx
Dated: Saturday May 10 2003 - 20:33:15 BST

Dear forum user,
I have a dynamic implementation of this superb menu. It means that the menu is generated from ASP, based upon users permissions.
So I could have a main menu bar with 2 elements as well as a menu bar with 5 elements and lots of nested sub menus.
I have the layout fixed to 768px wide, so I need relative positiong.
I used the screen_position property and set it to "center".
Then I used the menu_left property and set it to "offset=-x".
Now the problem is that the offset is good just for a menu configuration (say with 4 elements) but when there are less elements, for example, the menu moves away from the layout left border.
I'm using xhtml and css for all the gfx stuff and layout.
The menu could fit inside a <div> box (there's no table to place it in).
I'm looking for some kind of property, wich would set the menu bar width to a fixed value (say 700 px) so the left offset would remain the same for 2 menu elements as well for 4 or more.
I tryed to give the main menu a width of 400 (menu_width) but this not set the averall bar width but the width of each element (menu) in the menu bar.
Any suggestion is really appreciated!


Poster: xMANIGHTx
Dated: Sunday May 11 2003 - 10:07:36 BST

Since I've seen I can put functions in the left/top_menu properties, I was wondering if I could use DOM to get my box absolute position and put this one in the menu property.... going to try right now.. ;)


Poster: xMANIGHTx
Dated: Sunday May 11 2003 - 11:13:44 BST

I found a nice function on MSDN:

<script>
function getAbsoluteLeft(aoNode){
var aoCurrentNode=aoNode;
var iLeft=0;
while(aoCurrentNode.tagName!="BODY"){
iLeft+=aoCurrentNode.offsetLeft;
aoCurrentNode=aoCurrentNode.offsetParent;
}
return iLeft
}
</script>

This should return the absoulute positioning of an element.
So I put this code in the page and put the call to the function in the array taht way:

addmenu(menu=["menu4",115,"getAbsoluteLeft(RHBBottom)",120,0,,MainSt,1,"center",MainFx,0,1,0,"","","","","","","","","","Home Page","default.asp",,,1,"Anagrafiche","show-menu=menu20","",,1,"Utilità","show-menu=menu6","",,1,"Smart Menu","show-menu=menu18","# offfontcolor=000033;offbackcolor=ffcc66;onfontcolor=ffffff;onbackcolor=cc9933",,1])

Where RHBBottom is the <div> relative to wich I want to place my menu.
I have a really odd result... it seems that it get exactly where it should, but if I add in the function: return iLeft+20 to give an offset, nothing changes... I'm really stuck with this.


Poster: xMANIGHTx
Dated: Sunday May 11 2003 - 11:15:44 BST

I just tryed writing an unpredictable value in the menu_left property, instead of the call to function. I wrote: "sdjfhsdj" and the menu still is in position. So I guess that the function isn't evalutated at all... why?


Poster: xMANIGHTx
Dated: Sunday May 11 2003 - 11:23:41 BST

Well.. that's really off indeed!
If I use no values for screen_position, left and top, the menu gets exactly in the left up corner.
I have a mani <div> box wich is 768px wide and centered and I'm on a 1024 res.
If I put any non-consistent value (say: "asdjfhsdkj") in the left property, the menu gets left aligned with the main box! (<div>)
But I can't change the offset anyway, so even if this could be a working hack, it's of no use...


Poster: xMANIGHTx
Dated: Tuesday May 13 2003 - 20:23:49 BST

1 Problem is solved. The odd beahviour of the left property was due to the presence of the "" in the width property.
By the way, can anyone tell me how to use a function in the left property? I saw it somewhere but it seems I can't find back the post.
If I could get my function to work, the problme would be solved:
<script>
function getAbsoluteLeft(oNode){
var oCurrentNode=oNode;
var iLeft=0;
while(oCurrentNode.tagName!="BODY"){
iLeft+=oCurrentNode.offsetLeft;
oCurrentNode=oCurrentNode.offsetParent;
}
return iLeft;
}
</script>
This is the function that returns ABSOLUTE position of an element.
I use this with the css box to wich my menu bar should be relative.
If I put an alert(iLeft) instead of return(iLeft) and call the function on body onload event that way: onload="getAbsoluteLeft(myBoxIDWithoutQuotes)", it works correctly, sending back the right value.
If I use it in the menu array, I gat no value... maybe because the box is rendered after the menu?
Any other solution to get the reltive positioning of a dynamic menu (main bar elements number may vary)
I'm really desperate. I need to move the menu to the left and can't let it centered. All the issue is due to a fixed centered layout of 768px, elsewhere there would have been no probz. It's just a left-relative positioning problme. Thanks again.


Poster: xMANIGHTx
Dated: Tuesday May 13 2003 - 20:35:24 BST

YEEEEEEEEEEEEEEEEEEEEEEEEEEEEEP!!!!!!!!
Finally it worked! So if you need relative positiong of your menu to any element in your page, even with a dynamically changing menu, here's the solutin :D
Use the function I wrote above and in your main menu array just put:
addmenu(menu=["menuname",,getAbsoluteLeft(YourElementIDWithoutQutes),120,0,"",...
You can easily mod the function to have both left and top absolute position of your element.
It should have worked from the start, dunno what happened... the latest "cheat" was that I hade to remove the SCREEN_POSITION from tha menu array.
Infact for relative positioning, you would usually set a screen position and the use the offset+/-n value. In this case, relative positioning is simply claculated "on the fly".
Thanks and have a good time ;)


Poster: kevin3442
Dated: Tuesday May 13 2003 - 22:44:40 BST

Hi,

You present an innovative solution to an interesting problem. One minor point: it probably only seemed like your function wasn't returning a value early on in your testing because you still had "center" for the Screen Position property. I have found that the Screen Position takes precedence over the Menu Left property (the system ignores a numeric, absolute Menu Left setting if the Screen Position is specified; it will only accept an "offset" in that case). So, it was crucial that you removed your Screen Position setting, so that the menu system would revert to the numeric value returned by your function in the Menu Left element.

As I said, I think your solution is clever indeed. The only reservation I would mention is that I have personally found that getting the position of an element is extremely browser dependent, and sometimes even element dependent, and also dependent on whether your element is nested within another element and, if so, what type of element it is nested in. So, the results you get in positioning your menu may not be consistent across browsers (not a big deal if you're developing mainly fo one browser). For example, I'm fairly certain that your function will not work as it currently stands in NS4x because, if I recall correctly, NS4x does not support the offsetLeft property. Also, your function stops looping at the body tag, but the offsetParent of your div might be the HTML element in NS6, or something else in Opera... browsers that support offsetLeft and offsetTop can't seem to agree on what the offsetParent is. Fortunately, they all have an offsetParent, so it might be better to keep looping up the tree until there is no more offsetParent (i.e., until you're at the very top of the tree). If you don't keep incrementing the position (iLeft in your case) all the way to the top of the offsetParent tree, then you might not get the true absolute left position of the element on the page. Instead, you could get the position offset within some containing element; the result of which would will likely be that your menu would appear to shift too far to the left.

This is just something I suspect could be a problem, based on my own experience (and I definitely don't have a complete understanding of how to get element positions; much of that still mystifies me). You can always test it on a variety of browsers to see what happens.

I'm not trying to rain on your parade... I really do think what you've done is pretty cool. Just wanted to draw your attention to some potential issues. If cross-browser compatibility does turn out to be an issue, you might still be able to tweak your position sniffing function to make it work with more browsers.

Last point: One of your initial ideas was to make the overall width of the menu static, but then you found that the menu's width setting is for the width of each menu item. I ran into a similar problem a while ago. I came up with a function that you can use to set the overall width of the menu, essentially adding more space to the right side. If you're interested in trying it, let me know.

Cheers,

Kevin


Poster: xMANIGHTx
Dated: Wednesday May 14 2003 - 10:42:51 BST

Hallo Kevin! Well, I'm interested in your function of course. What makes me wonder is that if you "add space" to the right to obtain always the same menu bar width, I would face the original problem of adding differente values for each different menu config.
I'm sorry that still I can't show you my work in action, but it needs SQL Server (and SP) and I still have to buy hosting to put it up.
By the way...
Regarding browser compatibility, since all my work is based upon XHTML, JS and CSS, I retained full compatibility with NN 6.1+ (6.0 has big problems handling background images wich I use intensively to have "skin-costumizable" CSS layouts and gfx), Opera 6+, Mozilla 0.9+ and Explorer 5+
I discarded compatibility with NN4 even befor trying this solution, because it's a world apart and it tied too much XHTML/CSS conversion.
I can get along with some of the IE5 or Opera5 "quirks"... but NN4 it's really too much for my sanity ;)
On all of this browser I have almost pixel-precise positioning. Stille have to try the function on all of those, but since it's dynamic and relative positioning, I guess that it should work.
Infact if the box element I need to positione relative to, it's 5 more pixel left in Opera, my function would return a value of 5 more pixels.
I can assure you that I have a very very complex box layout for my site, to let it be fully costumizable from css.
So I have 34 boxes (most of wich are nested) and a couple of tables nested inside boxes and the layout it's identical in all browser I mentioned.
By the way, I'm going to try it in a few mins.
My only concern is that since the main layer is centerd and it's 768px wide, maybe the menu offsett gets wrong if you are on 1024 res and shrink the window... going to test this too.
Here is the function with the possibility to retrieve Top absolute positioning too and pass an offset:
<script>
function getAbsolutePos(oNode,iCoord,iOffset){
var oCurrentNode=oNode;
var iLeft=0;
while(oCurrentNode.tagName!="BODY"){
if (iCoord==0) {iLeft+=oCurrentNode.offsetLeft} else {iLeft+=oCurrentNode.offsetTop};
oCurrentNode=oCurrentNode.offsetParent;
}
return iLeft+iOffset;
}
</script>

iCoord set to 0 retrieves Left pos, else retrieves Top pos
iOffset is integer +/- added to the absoulute value retrieved

Keep in touch :P


Poster: xMANIGHTx
Dated: Wednesday May 14 2003 - 12:14:50 BST

Well... as Kevin advised my testing gave unpredictable results even with the browser I retained compatibility for.
Even more, as I supposed, there is a big issue when you resize the window with IE since you set an absolute positioning for the menu bar, but the rest of the content is relative (since it's centered in the page).
I only have 2 solutions:
1) Making the overall menu bar width fixed. I wonder if I can use your function for that Kevin, because it seems you have to "add" width based on the menu elements, but my elements vary based on user permissions.
2) Centering the menu bar (so it gets along with the window resize) and changing the left offset dynamically, based on the menu elements, at each login. Since all my arrays are ASP generated, this is quite easy to do (I only have to add the menu width * elements number to the offset like this: "offset=costantoffset+(numberofelement*width)"). The thing that it's not really easy is making the whole thing part of a modular skin system, where even the layout changes.. but that's another story :P


Poster: xMANIGHTx
Dated: Wednesday May 14 2003 - 12:51:46 BST

I went on the second solutin path. And.. it worked pretty fine on all browsers.
I post here a bunch of the ASP code, if you need to implement this solution.
The problem you want to solve is this:
I have a 768px wide layout centered on the page. This means that on 1024 res you'll have left and right "body" bands.
I want my dynamic menu to be put exactly some pixel left of my layout at a certain height.
I regualrly set the top property (since this is absolute even if you resize the win) but have to set the left property according to window resize and menu elements.
Solution with ASP generated arrays:
Set the left property ON THE DB you read the arrays from to: "offset=-offsetconstant" (note: you have to put the " in the DB too!)
Then in the ASP recursive function that generates your menu you have to:
1) Keep track of the main menu bar elements
2) Modify the final offset to: "offset=-offsetconstant+(nelements*menuwidth)"
I suggest you to generate first links for each menu, so you can remove the menu completly if it has no links (links navigation is based on user permissions)
When checking for links ONLY FOR THE MAIN MENU BAR, kepp a var counter and increment it for each element of the menubar:

If intMenuID = intMainMenuID Then intMLeftOffset = intMLeftOffset + 1

Then when you're building the MAIN MENU array, check for this. If the Left property in the DB contains the word "offset" then you have to change it according to the n of elements in intMLeftOffset and the Width property. This could be accomplished like that:

First put the menu width retrieved from DB into strMenuWidth var
If themenuI'm building is the main menu Then
If InStr(rsMenu("MENU_LEFT"),"offset") > 0 Then
strMenuLeft = """offset=" & Cint(Mid(rsMenu("MENU_LEFT"),9,Len(rsMenu("MENU_LEFT"))-9)) + (intMLeftOffset * Cint(strMenuWidth)) & """"
Else
If rsMenu("MENU_LEFT") = "-1" Then strMenuLeft = Null Else strMenuLeft = rsMenu("MENU_LEFT")
End If
End If

Hope this helps. :!: :!:

End If


Poster: kevin3442
Dated: Wednesday May 14 2003 - 18:46:20 BST

Hi xMANIGHTx,

Wow, you're doing a lot. I have several comments to make to catch up:

(1) As a general rule, I agree that NS4 sucks and trying to support it along with everything else sucks even more. I like your approach!

(2) Regarding changing the menu width programatically, I guess I shouldn't have said that the function adds extra space to the right. It has that effect visually, but it doesn't actually work that way. In the function call, you simply specify the width that you want. Here's the function:
Code:
function mm_setWidth(menuName, width)
{
  spos(gmobj("menu" + getMenuByName(menuName)),null,null,null,width);
}

I have to admit, I haven't tested this beyond IE6 and NS7; it works well on both of those browsers, but I don't know about others.

(3) Your latest approach is, once again, interesting. I have a couple of points that might help: (a) I think it's important to note that the way you're calculating the menu width will only work if the Menu Width element (4th eleent in the array) is set, so that each menu item has the same width. Many people leave this element unset, allowing the menu system to come up with a width for each menu item at runtime, based on the text & images contained within the item. (b) Cross-browser issues may come into play again. I have occasionally noticed small but definite differences in menu placement and sizing across browsers, with the main problems being NS4 and Opera (which I also despise). (c) The following function may be a little easier to use in determining the menu width, and its results might be a little more accurate.
Code:
function mm_getWidth(menuName)
{
  menuObj = gmobj("menu" + getMenuByName(menuName));
  menuPosArr = gpos(menuObj);
  return menuPosArr[3];
}

The function will derive the actual width from the menu itself. Advantages are: (i) it gives the actual width rather than the result of a calculation that could be different across browsers; (ii) it will work even if the menu's Width element is not set. The disadvantage for you would be that it'll only work after the menu has been rendered... a definite problem for you since you need the width to generate your menu code. But this might not be as bad as it seems. You could initially render the menu invisible to the user, get the menu's width, then reposition it to the appropriate spot on the page (where it will be visible). It sounds like a lot, but it's really not as hard as it sounds. Of course, you'd only need to worry about it if your current approach has cross-browser issues. Just for kicks, I'll tell you how. You'll need another function to reposition the menu once you calculate where it should be:

The following function will reposition the menu to absolute Top and Left coordinates, wich you pass in the function call.
Code:
function mm_setPosition(top, left)
{
  spos(gmobj("menu" + getMenuByName(menuName)),top,left,null,null);
}


To make sure that you don't get the appearance of the menu "jumping" from its initial location to the new location, you initially set your Menu Top (in the menu array) to a negative value (like -100), so that the menu's visibility is on, but the user can't see the menu because it is rendered off of the page (sneaky). While the menu is off the page, get its width using mm_getWidth(), then calculate where you want the menu to be. Now reset the menu's position using mm_setPosition(). You would wrap mm_getWidth(), your positioning calculations, and mm_setPosition() inside of another function of your own that you would call from the body's onload event.

This may all be purely academic, but the issues you bring up are a fun puzzle. And if you don't end up using these functions here, you may find uses for them at some other point.

Cheers,

Kevin


Poster: xMANIGHTx
Dated: Thursday May 15 2003 - 10:55:51 BST

Well Kevin... LOL... there's a lot of stuff here to play with! :D
I guess using your last 2 functs it's impossible for the reason I explained above. Infact if you set an absolute positioning, the menu will stay in place when you resize the window. My case is a common one, where you have a fixed layout, that fits 800res, ceneterd in the page. In this case, the layout it's cenetered and thus relative. If you set the menu to an absolute position, it would "slip out" of place when a user with 1024res resizes the win.
I must say that using the ASP method gives a perfect result on all the browser I mentioned (soon I 'll put that xxxx site up!LOL). The big problem (as you advised) arises when you don't specify the elements width. But I guess that in that case if you have a fixed layout and no menu-widht you could simply center the menu bar and change the gfx to contain it accordingly.
Note that all the problems arise when you have fixed layout and centered and you don't want an horizontal menu to be centered too.
Infact in all other situations (liquid layout, fixed but to the left, ecc.) there would be non problems.
As always this combination of events (wich is the most difficult to manage) is the most common situation! :D
Your first function is what I was searching for initially, so I guess I'm going to test it asap! If it works as it should evene on the other browsers, the asp trick would become useless, and this would increase usability for the "skin developement" too.
When and where should I call this function? Before or after the dumpmenu() funct? :)