|
home / documentation / reference / mini-tutorial: menu positioning
Menu Positioning
In HierMenus version 6, all menus support direct positioning via the
TopMenuX,
TopMenuY,
ChildMenuX, and
ChildMenuY configuration parameters. Further, each of these
parameters support the use of String JavaScript expressions--normal, quoted JavaScript code
that returns an integer to be used by HierMenus as the menu's left or top position. For those
parameters that support String JavaScript expressions, HierMenus will reevaluate the JavaScript
code provided each time it is referred to (in this case, each time the menus are repositioned
or displayed) making them a powerful tool for your menu positioning needs.
Positioning Basics
Of course, there's no rule that says you must use expressions in the above parameters.
If you know exactly where your top level menus are to be positioned (or HierMenus' default
positions are adequate), then you can simply specify those positions as the values of your
parameters. For example, this configuration:
HM_f_SetMenus({
MenuID:"MyMenu",
IsPermanent:true,
TopMenuX:10,
TopMenuY:130,
ChildOffset:10,
ChildOverlap:20
});
creates a permanently displayed menu called MyMenu and places it at pixel location
(10,130). By default, all child menus displayed from this menu will overlap the right edge of
the menu by 20 pixels, and will be offset 10 pixels downward from the menu item that spawns
them. This simple configuration setting is plenty adequate for a great number of menu
positioning needs.
String JavaScript Expressions
Using String JavaScript expressions in the TopMenuX and
TopMenuY configuration parameter settings provides a tremendous
amount of flexibility in the positioning of your menus, since it allows you to specify
virtually any JavaScript code you can conceive of to dynamically provide to
HierMenus the exact (x,y) location that you wish to place your menus in. For example,
consider this configuration, similar to the above but modified slightly to use
String JavaScript expressions for positioning:
HM_f_SetMenus({
MenuID:"MyMenu",
IsPermanent:true,
TopMenuX:"HM_f_GetElementXY('image1','x')",
TopMenuY:"HM_f_GetElementXY('image1','y')",
ChildOffset:10,
ChildOverlap:20
});
Now we've placed our top level menu in a significantly different place. By calling
the HM_f_GetElementXY method (included in the HM_Loader.js file, more
below) we're now placing our menu at the same (x,y) position as the element in our HTML
page that we've identified as image1. And since we specified a String JavaScript
expression (we enclosed the expression in quotes), it will be reinterpreted every time
HM attempts to reposition the menus; therefore, when the user resizes the browser and
the position of image1 changes as a result, then the menu will automatically
be adjusted to image1's new position.
Before we continue, note here a key--and very important--difference between String
JavaScript expressions, and normal JavaScript expressions (these are not standard
JavaScript terms; we simply use them here to differentiate the two usages). With
String JavaScript expressions, HM will reinterpret the expression every time
it is required. With normal JavaScript expressions, HM will interpret the
expression only once, immediately when the menu is registered. From then on, it will simply
use the result of that initial expression each time the menu is repositioned.
In HierMenus speak, a String JavaScript expression is one that is enclosed in
quotes; a normal JavaScript expression is not enclosed in quotes.
Let's have a look at our example again, only this time with normal JavaScript
expressions:
HM_f_SetMenus({
MenuID:"MyMenu",
IsPermanent:true,
TopMenuX:HM_f_GetElementXY('image1','x'),
TopMenuY:HM_f_GetElementXY('image1','y'),
ChildOffset:10,
ChildOverlap:20
});
If you were to load this menu, you would probably find that initially the
menu position is correct; since the expression does, after all, retrieve the same value
that the String JavaScript expression returns above. However, if you were to resize the
browser window, you would immediately see the significant difference: even though your
image1 element position changed on the page, HM would not move the
menu to match it. This is because HM stored the results of the expression the first time
it saw it (when the page loaded) and then used this stored result every time it needed
it after that.
While all HM configuration parameters support the use of normal JavaScript
expressions, only a select few support the use of String JavaScript expressions.
These include TopMenuX,
TopMenuY,
ChildMenuX,
ChildMenuY,
TopUponDisplay,
TopUponHide,
ChildUponDisplay, and
ChildUponHide.
Keywords
When using String JavaScript expressions in the TopMenu/ChildMenu positioning
parameters mentioned above, an additional feature is available to you that will allow
you to fine tune the placement of your menus in relation to HierMenus' own default
menu positioning scheme and/or the dimensions of the browser's viewport (the browser
window). This feature consists of a set of specialized terms--called Keywords--that
you can use within String JavaScript expressions in the menu position parameters
(i.e., the TopMenu/ChildMenu parameters listed at the top of this tutorial). Each
Keyword represents a specific location on the page and can be utilized in your
JavaScript expression as such. The available Keywords include
HM_default_x_position,
HM_default_y_position,
HM_window_top_edge,
HM_window_bottom_edge,
HM_window_right_edge, and
HM_window_left_edge.
Let's have a look at a two menu example configuration that includes the use of
Keywords in JavaScript expressions:
HM_f_SetMenus({
MenuID:"MyMenu",
IsPermanent:true,
IsHorizontal:true,
RepositionOnScroll:true,
TopMenuX:"HM_window_left_edge+10",
TopMenuY:"HM_window_top_edge+10",
PositionChild:"below",
});
HM_f_SetItems(
{MenuID:"MyMenu",DisplayText:"Menu Item 1",ChildID:"MyChild1"},
{MenuID:"MyMenu",DisplayText:"Menu Item 2",LinkURL:"item2.html"},
{MenuID:"MyMenu",DisplayText:"Menu Item 3",LinkURL:"item3.html"},
{MenuID:"MyMenu",DisplayText:"Menu Item 4",LinkURL:"item4.html"}
);
HM_f_SetMenus({
MenuID:"MyChild1",
ChildMenuX:"HM_default_x_position-3",
ChildMenuY:"HM_default_y_position-4"
});
We've omitted the menu items for MyChild1 (they're irrelevant for this example).
Reviewing the above configuration, we see that our top level menu has been set based on the
dimensions of the browser window; specifically, the menu has been set so that it will appear
10 pixels from the top and 10 pixels from the left of the browser window's top left corner.
Further, note that the RepositionOnScroll
parameter has been set to true; so that this menu will automatically slide into this top
left corner position each time the user scrolls the page. (Without RepositionOnScroll,
the menu would initially be placed at the top left corner of the browser window,
but would not be moved from there unless the user resized the browser window.) When using
the window_xxx_edge Keywords, you will nearly always want to set
RepositionOnScroll (and/or IsFixed) to
true.
Continuing with the example, notice that the child menus of MyMenu will
automatically be positioned immediately beneath their parent menu items
(PositionChild:"below"). However, looking at the definition for MyChild1,
we see that this position will be slightly overridden via the use of the HM_default_x_position
and HM_default_y_position keywords. In other words, the default position for
this menu has been specified as directly beneath its parent item, and we are overriding
that default via the use of the ChildMenuX and ChildMenuY parameters. Using
these String JavaScript expressions, we are able to adjust that default setting:
ChildMenuX:"HM_default_x_position-3",
ChildMenuY:"HM_default_y_position-4"
Note that we use the ChildMenu parameters here, since the menu will be
interpreted as a child menu when displayed beneath its parent menu item. The above
expressions, in combination with the earlier PositionChild
setting, instruct HierMenus to first position the menu directly beneath its parent item,
and then raise it by 4 pixels ("HM_default_y_position-4") and then shift it to
the left by 3 pixels ("HM_default_x_position-3"). The menu is then displayed in its
new, adjusted location.
Of course, in order to make full use of the HM_default_x_position and
HM_default_y_position keywords, you must know what HM's default positions are
for each type of menu. That information can be gleaned from the following list:
For Top Level PopUp Menus the default position is the
mouse position; that is, the position of the mouse when the menu was initially
popped up. Users of HM 4 or 5 may be familiar with the mouse_x_position
and mouse_y_position keywords. Since the default position of popup menus
is the mouse position, using the new default keywords accomplishes the
same thing as the old mouse position keywords did.
For Permanently Displayed Top Level Menus the default
positions are always (0,0) (and are therefore not particularly useful).
For all Child Menus the default menu position
depends on the configuration settings for ChildOffset,
ChildOverlap, ChildPerCentOver,
and PositionChild, with PositionChild taking
precedence over all others, and ChildPerCentOver taking precedence over
ChildOverlap. The individual defaults for each of these parameters can be
found in their reference entries.
In all of the above cases, note that HM's KeepInWindow behavior
will override the final position of the menu--i.e., even if you do provide a precise
position with the TopMenu/ChildMenu parameters, if the menu's KeepInWindow
parameters are set to true (as they are by default), then the menu will be
moved so that it is contained--as best as possible--within the user's browser window.
Or in other words, the KeepInWindow behavior is always applied last, after
all other menu positioning logic is taken into account. You can override HM's
KeepInWindow behavior on a menu by menu basis by adjusting the
TopKeepInWindowX,
TopKeepInWindowY,
ChildKeepInWindowX, and/or
ChildKeepInWindowY parameters.
Custom Functions
To specifically assist you in your menu positioning endeavors, HM includes three
custom functions in the HM_Loader.js file that will allow you to position
menus in the center of a document, based on their dimensions (width and height), or relative to the position of a known, separate,
non-HM element elsewhere on your HTML page.
HM_f_CenterMenu
HM_f_CenterMenu returns the centered left position of a menu within
the user's current browser document. It is typically called from within the
TopMenuX/ChildMenuX parameter settings. To use it, you pass to it the
MenuID of the menu you are positioning. HM_f_CenterMenu will
retrieve that menu, examine its width, compare that width to the current width
of the browser document, and return the appropriate integer for use in the
left (X) setting of the menu.
Example settings:
TopMenuX:"HM_f_CenterMenu('hm_m_main')",
// center hm_m_main when displayed as a top level menu
ChildMenuX:"HM_f_CenterMenu('hm_m_products')",
// center hm_m_products when displayed as a child menu
HM_f_GetMenuDimension
Using HM_f_GetMenuDimension, you can position the top/left corner of your
menu based on the current width or height of the menu itself, and thus achieve bottom
or right-edge based menu positioning. HM_f_GetMenuDimension was covered in
detail in Bulletin 11, when it was introduced back in
the HierMenus version 5 release cycle. We refer you to that discussion for further
information. Note that when we intially released the code it was called HM_fc_GetMenuDimension,
but when you refer to the function in HierMenus version 6, you must
do so via HM_f_GetMenuDimension. Other than that, the new version 6 function
works in the same manner as described in Bulletin 11.
HM_f_GetElementXY
A new entry for HierMenus version 6 is the inclusion of the HM_f_GetElementXY
function in the HM_Loader.js file. As its name implies, HM_f_GetElementXY
allows you to retrieve the (x,y) position of a non-HierMenus element within your HTML page.
You can then use that position to help you decide where to place a menu. In the following
example (duplicated from above):
HM_f_SetMenus({
MenuID:"MyMenu",
IsPermanent:true,
TopMenuX:"HM_f_GetElementXY('image1','x')",
TopMenuY:"HM_f_GetElementXY('image1','y')",
ChildOffset:10,
ChildOverlap:20
});
We place our menu at the exact position of an image in our page that we have identified
as image1 (i.e., we assigned a name and id to the image of
image1). You may also feel free to offset the menus from the position you retrieve
by simply extending your String JavaScript expressions:
HM_f_SetMenus({
MenuID:"MyMenu",
IsPermanent:true,
TopMenuX:"HM_f_GetElementXY('image1','x')-10",
TopMenuY:"HM_f_GetElementXY('image1','y')+20",
ChildOffset:10,
ChildOverlap:20
});
While HM_f_GetElementXY works perfectly in many situations, there are,
unfortunately, several browser specific scenarios where the (x,y) of the target
element is not properly found. We present a list of those problems we know of below.
To avoid these problem scenarios in general, we recommend that you target page
links or images, both of which should have unique name and
id attributes (necessary for Netscape 4). Further we recommend that your
target link or image does not itself have a border, and that it is not itself
enclosed within a positioned element (relative or absolute).
We conclude this mini tutorial with a look at those browser specific problems
when using HM_f_GetElementXY that we are aware of. The list is grouped
by browser. Note that in some cases the problems can be worked around by slightly
altering your HTML.
- Netscape 4.x
Only named images, named links, and layers are supported. When using images or
links, note that a name attribute must be provided. For maximum compatibility with
all browsers, we recommend including both the name and the id:
<img name='myImage' id='myImage' width='10' ....
Of course, pursuant to HTML standards, the identifier you choose for your image or
link should be unique within the page.
Netscape 4.x will also not properly locate images or links that are themselves
nested within another layer (HM_f_GetElementXY does not recursively
search Netscape 4.x layers).
- Netscape 6.x
In Netscape 6.0x, td's with single pixel rules
(i.e., the default if the table has a border of any size) will not be properly
located. Specifically, they will be one pixel off. A possible work around is to place
something inside the td, locate that something, and then substract to get to
the rule.
Netscape 6.x will not properly locate a non-table item that itself has an
inherited border width (from a style sheet rule) or an inline border width that is
set using something other than pixels. In both cases, the position returned will be
off by the width of the border.
In Netscape 6.1-6.x the above problem extends to any object that qualifies under
the inherited border width rule, as well as any element that is itself nested within an
element that qualifies under the rule above.
- Mozilla/Netscape 7.x
- Internet Explorer 4 (Windows)
Items that are within margins that are not set using
inline styles, or that are set using lengths other than pixels or percentages, will
not be properly found. They'll be off by the width of the margins.
- Internet Explorer 5 (Windows)
IE 5.0 (Windows) incorrectly calculates bordered container
items (P, DIV, etc) other than tables, rows, and cells; but only if the item itself
is the target. Workaround: Insert something into the container such as an image, find
it, and then offset your result by the width of the container's border.
Items nested within a relatively positioned object without height/width settings
are not properly found. Adding a specific height or width to the relatively positioned
object before it is located corrects the problem.
- Internet Explorer 5.5/6
- Safari 1.x
Safari will not properly locate any absolutely positioned
element (or any element within an absolutely positioned object) unless the absolute
positioning was applied directly to the position property via script, or via an inline
style setting on the element itself. The position reported will be off by the width
of the page margins.
- Konqueror 3.1
If the document's default page margins are changed, and
this change is accomplished using style sheet rules, or an inline style on the body
tag that specified length units other than pixels or percentages, then all non-positioned
objects on the page will not be found properly.
Related to the above, items that are themselves, or are within, absolutely positioned
objects, where the absolute positioning is set via a style sheet rule and not
via an inline style or directly by script, will not be reported properly. The same
rule as above applies; except in this case we do not know that the item we're
looking for is within an absolutely positioned object, and we therefore (incorrectly)
apply the margin correction as above.
Absolutely positioned objects where no top or left property
is set will not be properly located.
Any element that is not in a proper HTML container will not be properly located.
(A common example is an image directly within the body of the document but itself
without any valid parent container, which is invalid HTML). In most cases, Konquerer 3.1
will simply report the element's position as (0,0) no matter where it is on the page.
|