Tuesday, February 21, 2012

Multi-level jquery right to left menu

Hey ...
Horizontal dropdown main menu is one the most useful components for every sites or web applications. There are several jquery plug-in menus which build menu from UL/LI tags and work so good with various animations and effects. But always they have a problem for right to left web applications such as Arabic or Persian web applications. Most of the menus don’t work from right to left direction as well as from the left to right direction (the default orientation). So here I’m going to build a simple and complete multi-level right to left dropdown menu.
So let’s do it…
There are some rules which should be followed:
  • The menu should be generated from UL/LI tags
  • The first level should be horizontal and the other sub-level should be vertical
  • The menu items should be free and the user can assign it his/her customize CSS class
  • The content of menu items should be handle by user
  • The sub-menus should appear at the bottom of the item for the first level and for the other levels should appear at right side of the selected parent item
  • The sub-menus should vanish after specific time in milliseconds by leaving the parent item
The scenario is like this:
  • The user should make its own UL/LI tags with content
  • The UL/LI should be in the middle of a div tag with specific id and at least “jquerycssmenu” CSS class(it can have more extra classes)
  • There are some parameters which can be changed before generating menu such as: “jquerycssmenu. arrowimages”, “arrowimages. fadesettings”, “arrowimages. isRTL”. For switches of them refer to “jquerycssmenu.js “ to configure them
  • Finally, call “jquerycssmenu.buildmenu(divId);” on page load
The javascript code is available here:
1) var jquerycssmenu = {
2) arrowimages : {down:['downarrowclass', 'arrow-down.gif', 25], right:['rightarrowclass', 'arrow-left.gif']},
3) //duration of fade in/ out animation and the starting fade out delay, in milliseconds
4) fadesettings: {overduration: 350, outduration: 100, startfadeoutdelay:300},
5) // you can set it false and if you set it to false the sub menu positionning will be ok but you should
6) // change arrow incons position to the left side manually and .jquerycssmenu UL LI { float:left} should be set
7) isRTL : true,
8) buildmenu:function(menuid) {
9) jQuery(document).ready(function($) {
10) var mainmenu = $("#" + menuid + ">ul");
11) var headers = mainmenu.find("ul").parent();
12) headers.each(function(i) {
13) var curobj = $(this);
14) var subul = $(this).find('ul:eq(0)');
15) var inMenu = false;
16) this._dimensions = {w:this.offsetWidth, h:this.offsetHeight, subulw:subul.outerWidth(), subulh:subul.outerHeight()};
17) this.istopheader = curobj.parents("ul").length == 1 ? true : false;
18) subul.css({top:this.istopheader ? this._dimensions.h + "px" : 0});
19) /*for isRTL : false set it to paddingRight*/
20) curobj.children("a:eq(0)").css(this.istopheader ? {paddingLeft: jquerycssmenu.arrowimages.down[2]} : {}).append(
21) '<img src="' + (this.istopheader ? jquerycssmenu.arrowimages.down[1] : jquerycssmenu.arrowimages.right[1]) + '" class="' + (this.istopheader ? jquerycssmenu.arrowimages.down[0] : jquerycssmenu.arrowimages.right[0]) + '" style="border:0;" />');
22) curobj.hover(function(e) {
23) inMenu = true;
24) var targetul = $(this).children("ul:eq(0)");
25) this._offsets = {left:$(this).offset().left, top:$(this).offset().top};
26) var menuleft = this.istopheader ? this._dimensions.subulw - this._dimensions.w - 15 : this._dimensions.w;
27)
38) if (jquerycssmenu.isRTL) {
39) menuleft = (this._offsets.left < this._dimensions.subulw) ? (this.istopheader ? -(this._dimensions.subulw - this._dimensions.w) : this._dimensions.subulw ) : -menuleft;
30) } else {
31) menuleft = (this._offsets.left + menuleft + this._dimensions.subulw > $(window).width()) ? (this.istopheader ? -this._dimensions.subulw + this._dimensions.w : -this._dimensions.w) : menuleft;
32) }
33)
34) targetul.css({left:menuleft + "px"}).fadeIn(jquerycssmenu.fadesettings.overduration);
35) }, function(e) {
36) inMenu = false;
37) var li = $(this);
48) setTimeout(function() {
49) if (!inMenu) {
40) li.children("ul:eq(0)").fadeOut(jquerycssmenu.fadesettings.outduration)
41) }
42) }, jquerycssmenu.fadesettings.startfadeoutdelay);
43) });
44) });
45) mainmenu.find("ul").css({display:'none', visibility:'visible'});
46) });
47) }
58) };
As you can see everything happens in “buildmenu” method. So let’s take a look to it.
First of all we should find all LIs containing sub-menu(UL) in the middle of them from level 2 (lines 10, 11). Then we should iterate all of them to position their sub-menus at the right place relatively their parent item and handle mouseove and mouseout events to show or hide the sub-menu. We should find the sub-menu(line 14).
“inMenu” is a flag which determine if the mouse cursor is in the menu area or not (line 15). We set it to true in moseover and false in mouseout. In mouseout we don’t hide the sub-menu immediately. After a specific delay we do it to have more user-friendly result (line 42). You can comment “setTimeout(function() {” and “}, jquerycssmenu.fadesettings.startfadeoutdelay);” then check the result. You will find the menu without these lines so user-enemy on sub-menus navigation.
“_dimensions” is a map containing the selected element (LI) and it’s submenu width and height (line 16)
“istopheader” is a flag to determine if the selected item is top level or not(line 17). We should have a different strategy in sub-menu positioning if the selected item is top level or not.
Then we should set the top position of the sub-menu and place an arrow image into the parent of it (lines 18 to 21)
Then we should handle the mouseover and mouseout events. We do it by hover jquery API. The first argument is mouseover and the second argument is mouseout (line 22)
In mouseover function we should set the sub-menu left postion and make it to be shown (lines 25 to 32)
In mouseout function we should make the sub menu to be hidden (line 40)
At the last step of “buildmenu” API we should hide all of the ULs (line 45)
To have a more user-friendly user interface result we should prepare a CSS to illustrate ULs/LIs and A tags. I do it on this way:
.jquerycssmenu {
PADDING-LEFT: 15px;
FONT: bold 12px Verdana;
}
.jquerycssmenu UL {
PADDING-BOTTOM: 0px;
LIST-STYLE-TYPE: none;
MARGIN: 0px;
PADDING-LEFT: 0px;
PADDING-RIGHT: 0px;
PADDING-TOP: 0px
}
.jquerycssmenu UL LI {
POSITION: relative;
DISPLAY: inline;
/*for isRTL : false set it to left*/
FLOAT: right
}
.jquerycssmenu UL LI A {
BACKGROUND: #eff0ff;
PADDING-BOTTOM: 4px;
PADDING-LEFT: 7px;
PADDING-RIGHT: 7px;
DISPLAY: inline-block;
/*for isRTL : false set it to ltr*/
DIRECTION: rtl;
COLOR: #2d2b2b;
MARGIN-RIGHT: 3px;
TEXT-DECORATION: none;
PADDING-TOP: 5px
}
.jquerycssmenu UL LI A:hover {
BACKGROUND: #eff9ff;
COLOR: black
}
.jquerycssmenu UL LI UL {
Z-INDEX: 1;
POSITION: absolute;
DISPLAY: block;
VISIBILITY: hidden;
BORDER-BOTTOM: black 1px solid;
LEFT: 0px
}
.jquerycssmenu UL LI UL LI {
DISPLAY: list-item;
FLOAT: none;
}
.jquerycssmenu UL LI UL LI UL {
TOP: 0px
}
.jquerycssmenu UL LI UL LI A {
BORDER-BOTTOM: #778 0px solid;
BORDER-LEFT: #778 1px solid;
BORDER-RIGHT: #778 1px solid;
BORDER-TOP: #778 1px solid;
PADDING-BOTTOM: 4px;
MARGIN: 0px;
PADDING-LEFT: 5px;
WIDTH: 160px;
PADDING-RIGHT: 5px;
FONT: 13px Verdana;
/*for isRTL : false set it to ltr*/
DIRECTION: rtl;
BACKGROUND: white;
COLOR: black;
PADDING-TOP: 4px
}
.jquerycssmenu UL LI UL LI A:hover {
BACKGROUND: #eff9ff;
COLOR: black
}
.downarrowclass {
POSITION: absolute;
TOP: 7px;
/*for isRTL : false set it to right: 5px */
LEFT: 5px
}
.rightarrowclass {
POSITION: absolute;
TOP: 5px;
/*for isRTL : false set it to right: 5px */
LEFT: 5px
}
That’s it. Now we have a multi-level right to left dropdown menu.
You can download the js, css and sample html file from here.

all rights reserved by Mostafa Rastgar and Programmer Assistant weblog

No comments: