A CSS Menu in Farcry That Stays Open???
I truly thought this project would be the end of me. The project was to create a CSS menu that pops up instead of down. Simple enough. However, when the user clicked on a child item, that menu tree had to stay open. If you’re at all familiar with CSS menus you know that they basically revolve around an unordered list with several classes and they turn the display property in CSS on and off. Of course, this wouldn’t work because the use of :hover was out. So, here’s how I accomplished this (2 level menus only)…
1) Create a custom nav class based on the generic. mine neiNav.
2) Keep track of where you are in the navigation tree. In my case I was using 2 levels only. The first level was a child of Home and the 2nd level was a child of any particular item in the first level. Since the point is to keep the child menu open if the user selects something from it, we only need to keep track of the object ID in the 2nd level. I did this with the following:
<cfparam name="parentNavID" default="">
<cfif application.factory.otree.getparentID(request.navid).parentid NEQ application.navid.home>
<cfset parentNavID = application.factory.oTree.getParentID(request.navid).parentId>
</cfif>
This way we know that if the parentNavID has a value, then the value it contains represents a level 1 menu item. Thus, the tree directly following this item needs to stay open
3) Pass the parentNavID to your nav class:
<skin:neiNav
navID="#application.navid.home#"
parentNavId="#parentNavID#"
id="nav"
depth="2"
bActive="true"
bIncludeHome="true">
4) Add the following line to your nav class file (my case was neiNav.cfm)
<cfparam name="attributes.parentNavID" default="">
*** Everything beyond here is not for the faint of heart ***
5) Look for
else if(currentlevel gt previouslevel)
in your nav class file. Add the following code to make it look like this:
else if(currentlevel gt previouslevel){
if(qNav.objectid[i-1] eq attributes.parentNavID)
writeOutput("<ul class=""subNavRemain"" id=""subNavRemain"">");
else
writeOutput("<ul class=""subNav"" id=""subNav"&dropCounter-1&""" onMouseOver=""highlightOpen("&dropCounter-1&")"" onMouseOut=""unHighlightOpen("&dropCounter-1&")"">");
ul=ul+1;
}
6) Next you have to re-create the links which is usually the last part of the navigation class file. Inside the links we need to define a class, id, onMouseOver and onMouseOut event. We also need to track a counter so each level 1 item with a sub-menu behind it will have a class f drop1, drop2, drop3, etc. Here’s the code:
// write the link
if(currentlevel IS NOT 2)
writeOutput("><a href="""&href&""">"&trim(qNav.ObjectName[i])&"</a>");
else {
if (qNav.nLevel[i+1] GT currentlevel){
if(qNav.objectid[i] eq attributes.parentNavID){
writeOutput("><a href="""&href&""" class=""drop"&dropCounter&""" id=""dropRemain"" onMouseOver=""showOpen("&dropCounter&")"" onMouseOut=""hideOpen("&dropCounter&")"">"&trim(qNav.ObjectName[i])&"</a>");
dropCounter = dropCounter + 1;
}
else {
writeOutput("><a href="""&href&""" class=""drop"&dropCounter&""" id=""drop"" onMouseOver=""showOpen("&dropCounter&")"" onMouseOut=""hideOpen("&dropCounter&")"">"&trim(qNav.ObjectName[i])&"</a>");
dropCounter = dropCounter + 1;
}
}
else {
writeOutput("><a href="""&href&""" class=""noDrop"" onMouseOver=""showOpen()"" onMouseOut=""hideOpen()"">"&trim(qNav.ObjectName[i])&"</a>");
}
}
7) Now, you just need CSS classes and a few js functions to control everything.
Here’s the CSS:
/*NAVIGATION*/
#header ul#nav {list-style-type: none; margin: 0; float: left;letter-spacing: .001em; padding: 15px 0 0 10px; display: block; height: 18px; width: 790px; background: #d3d6d9; color: #626469; font-family:Lucida Grande,Arial,Helvetica,sans-serif;font-size: .75em;list-style: none;}
ul#nav li {display:inline; padding:0px; white-space: nowrap; list-style-type: none;}
ul#nav li a {padding: 15px 4px 10px 4px;height: 20px; width: 60px; margin: 0 0px 0 0; color: #626469;}
ul#nav li a:hover, ul#nav li.show a{display: inline; list-style-type: none; color: #ffffff; text-decoration:none; background-color: #004b8d;}
ul#nav li ul.subNav {
font-size: .9em;
background: #004b8d;
list-style-type: none;
padding: 10px;
height: 20px;
top: 0px;
float:left;
left:-10px;
width:516px;
padding-top:15px;
margin-top: -74px;
position: relative;
display: none;
text-align: right;
}
ul#nav li#remain a {
color:#ffffff;
text-decoration:none;
background-color:#044b8d;
}
ul#nav li#remain ul#subNavRemain {
font-size: .9em;
background: #004b8d;
padding: 15px 10px 10px 10px;
height: 20px;
left:-10px;
margin-top: -74px;
float:left;
width:516px;
position:relative;
display: block;
text-align: right;
}
ul#nav li:hover ul.subNavRemain,ul#nav li.show ul.subNavRemain, ul#nav li.open ul.subNavRemain {display: inline;}
ul#nav li#remain ul.subNavRemain li,ul#nav li ul.subNavRemain li a {color: #ffffff; list-style-type: none; display: inline; }
ul#nav li#remain ul.subNavRemain li a{padding: 10px 4px 7px 6px;height: 20px; width: 60px; margin: 0 2px 0 0;}
ul#nav li#remain ul.subNavRemain li a:hover {color:#d3d6d9;}
ul#nav li#remain ul.subNavRemain li :hover {background-image: url('../images/menu_arrow.gif'); background-position: bottom left; background-repeat: no-repeat;}
ul#nav li#remain a.active {color: #333333; font-weight: bold}
ul#nav li#remain ul.subNavRemain li a.active {color: #cccccc; font-weight: bold;}
ul#nav li:hover ul.subNav,ul#nav li.show ul.subNav, ul#nav li.open ul.subNav {display: inline;}
ul#nav li ul.subNav li,ul#nav li ul.subNav li a {color: #ffffff; list-style-type: none; display: inline; }
ul#nav li ul.subNav li a{padding: 10px 4px 7px 6px;height: 20px; width: 60px; margin: 0 2px 0 0;}
ul#nav li ul.subNav li a:hover {color:#d3d6d9;}
ul#nav li ul.subNav li :hover {background-image: url('../images/menu_arrow.gif'); background-position: bottom left; background-repeat: no-repeat;}
ul#nav li a.active {color: #333333; font-weight: bold}
ul#nav li ul.subNav li a.active {color: #cccccc; font-weight: bold;}
ul#nav ul.open{display: inline;}
Here’s the JS:
function hideOpen(x){
unHighlightOpen(x);
}
function showOpen(x){
highlightOpen(x);
}
function highlightOpen(x){
var openNav = document.getElementById("nav").getElementsByTagName('a');
var remain = document.getElementById("remain");
if (remain != null){
var remainNavUL = remain.getElementsByTagName('ul');
var dropRemain = document.getElementById("dropRemain");
dropRemain.style.backgroundColor = "#d3d6d9";
dropRemain.style.color = "#626469";
remainNavUL[0].style.display = "none";
}
for (var i = 0; i < openNav.length; i++){
if (openNav[i].className == "drop"+x){
openNav[i].style.backgroundColor = "#004b8d";
openNav[i].style.color = "#ffffff";
if (openNav[i].id == "dropRemain"){
remainNavUL[0].style.display = "block";
}
}
}
}
function unHighlightOpen(x){
var openNav = document.getElementById("nav").getElementsByTagName('a');
var remain = document.getElementById("remain");
if (remain != null){
var remainNavUL = remain.getElementsByTagName('ul');
var dropRemain = document.getElementById("dropRemain");
}
for (var i = 0; i < openNav.length; i++){
if (openNav[i].className == "drop"+x){
openNav[i].style.backgroundColor = "#d3d6d9";
openNav[i].style.color = "#626469";
}
}
if (remain != null){
dropRemain.style.backgroundColor = "#004b8d";
dropRemain.style.color = "#ffffff";
remainNavUL[0].style.display = "block";
}
}
What the…?
I know there’s a lot going on all at once. Essentially, each link gets a unique class (drop1, drop2, etc). Each mouse over function and mouse out function sends a unique number corresponding to the 1, 2, etc of the drop classes (showOpen(1), showOpen(2), etc). Then the JS just keeps track of this in order to display the correct menu and leave the correct menu open.
When everything is said and done you should end up with HTML that looks like this:
<ul id="nav">
<li>
<a href="/index.cfm" onMouseOver="showOpen()" onMouseOut="hideOpen()">Home</a>
</li>
<li>
<a href="/index.cfm?objectid=AE902317-B0D0-DE64-E7D53D856EF42722" class="drop1" id="drop" onMouseOver="showOpen(1)" onMouseOut="hideOpen(1)">About NEI</a>
<ul class="subNav" id="subNav1" onMouseOver="highlightOpen(1)" onMouseOut="unHighlightOpen(1)">
<li>
<a href="/index.cfm?objectid=AE9372EE-B0D0-DE64-E7C3CF312394A15A">Our Company</a>
</li>
<li>
<a href="/index.cfm?objectid=AE9372FE-B0D0-DE64-E7B2313B49C48D6D">Our Locations</a>
</li>
<li>
<a href="/index.cfm?objectid=AE93730E-B0D0-DE64-E7778C404E12E114">Our Experts</a>
</li>
<li>
<a href="/index.cfm?objectid=AE93731D-B0D0-DE64-E7733CE3844C51C6">Our Affiliations</a>
</li>
</ul>
</li>
<li class="remain" id="remain">
<a href="/index.cfm?objectid=AE902327-B0D0-DE64-E75A3530D46BCAD1" class="drop2" id="dropRemain" onMouseOver="showOpen(2)" onMouseOut="hideOpen(2)">Our Services</a>
<ul class="subNavRemain" id="subNavRemain">
<li>
<a href="/index.cfm?objectid=AE942873-B0D0-DE64-E78757D6FA125CAC">Origination</a>
</li>
<li>
<a href="/index.cfm?objectid=AE942883-B0D0-DE64-E71A8826B4DE3F23">Destination</a>
</li>
<li>
<a href="/index.cfm?objectid=AE942892-B0D0-DE64-E78AA55C1E431566">Program Administration</a>
</li>
<li>
<a href="/index.cfm?objectid=AE9428A2-B0D0-DE64-E7503A7E09D35808">International</a>
</li>
<li>
<a href="/index.cfm?objectid=AE9428B2-B0D0-DE64-E72C69FB52D25B02">Expense Administration</a>
</li>
<li>
<a href="/index.cfm?objectid=AE9428C1-B0D0-DE64-E791C3295E2017F7">Business</a>
</li>
</ul>
</li>
<li>
<a href="/index.cfm?objectid=AE902337-B0D0-DE64-E749A41EE8EC52F1" class="noDrop" onMouseOver="showOpen()" onMouseOut="hideOpen()">New & Updates</a>
</li>
<li>
<a href="/index.cfm?objectid=AE902346-B0D0-DE64-E72AE45AA6F85BDA" class="noDrop" onMouseOver="showOpen()" onMouseOut="hideOpen()">FAQ's</a>
</li>
<li>
<a href="/index.cfm?objectid=AE902356-B0D0-DE64-E7409364BFEB2499" class="drop3" id="drop" onMouseOver="showOpen(3)" onMouseOut="hideOpen(3)">Success Stories</a>
<ul class="subNav" id="subNav3" onMouseOver="highlightOpen(3)" onMouseOut="unHighlightOpen(3)">
<li>
<a href="/index.cfm?objectid=AE947E92-B0D0-DE64-E729070164382F4F">Transferee</a>
</li>
<li>
<a href="/index.cfm?objectid=AE947EA2-B0D0-DE64-E7AD141ECFD93DB2">Client</a>
</li>
</ul>
</li>
<li>
<a href="/index.cfm?objectid=AE902366-B0D0-DE64-E7B138B2B8DAF72C" class="noDrop" onMouseOver="showOpen()" onMouseOut="hideOpen()">Contact Us</a>
</li>
<li>
<a href="/index.cfm?objectid=AE902375-B0D0-DE64-E756FE28651B98BD" class="noDrop" onMouseOver="showOpen()" onMouseOut="hideOpen()">Career Opportunities</a>
</li>
</ul>
You can go to http://neinew2.cfwebtools.com/index.cfm to see the working result.