I\'m in the process of learning Javascript and I\'m trying to create a simple dropdown menu. An example of my desired functionality can be seen on the google homepage in the top
It looks like you have a pretty decent setup as-is. You'll likely run into some event bubbling problems (for more info, take a look at PPK's Event Order Article). That seems to be outside of the scope of your current question, so I'll just give you what you asked for:
hideMenus()
{
var uls = document.getElementsByTagName('ul'), i;
for (i = 0; i < uls.length; i++)
{
if (uls[i].className === 'submenu' && uls[i].style.display !== 'none')
{
uls[i].style.display = 'none';
}
}
}
First, we get all the <ul>'s on the page. Then, we loop through all of them, check to see if it's a submenu, and if it's currently displayed. If both are true, then we hide it.
There are a couple faults with this code:
class="animal submenu"
), then it will not hide the menugetElementsByClass
.These aren't huge faults, especially if you're only using this to learn about javascript, and if you closely control your code (i.e. no other developers are working on it). All in all, it's a good stepping stone.
In the future, I'd suggest using addEvent
- a fairly common function that allows you to add event handlers to elements without using onclick="..."
. There are a couple different implementations of it, but they (almost) all work the same from your perspective. Here are links to Dean Edwards's Version and John Resig's Version
Good luck!
You can capture a click anywhere if you put onclick on the body. Because of the javascript event propagation model, if you click anywhere on any element and you don't stop the event from propagating, it will reach body and hide the menus.
So basically this means that you want to capture body onclick and make it to hide the menus so when you click on any area of the page, it will close the menus.
But this hides a bit of unwanted behaviour - when you click on the button to show the menu, the menu will display and quickly after that hide (when the event reaches body). To prevent this, you will want to stop the event from propagating when you click on the button which shows the menu (you can see how this works in the code I posted below). The code shows where you need to touch to make it work nicely.
// this function stops event e or window.event if e is not present from
// propagating to other elements.
function stop_event(e) {
if(!e) {
e = window.event;
}
if (e.stopPropagation) e.stopPropagation();
e.cancelBubble = true;
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
return false;
}
// now you just hide all the menus in your hideMenus
function hideMenus()
{
//pseudocode!
for all visible menus - hide // or if you want you can hide all menus,
// the hidden will remain hidden
}
Now the important part.
function menu(id) {
// your stuff here
stop_event(); // this will stop the event going down to the body
// and hiding it after showing it
// this means it will not flicker like: show-hide
}
And finally on your whole UL element:
//partly pesudocode
ul.onclick = function() { stop_event(); }
To explain again what this does:
1st. You hook your hideMenu function to body.onclick. This means that it will always hide the menus if we don't stop the event.
2nd. When you click the menu button, you show the menu and then we stop the event from going to the body. This way, the body.onclick will not fire and it will not hide the menu right after we opened it.
3rd. The ul.onclick means that the menu will not hide itself when we click on it (though if you want the menu to hide when you click the menu itself, you can remove this part).
Here is more or less the logic we use in our web app for drop down menus:
<html>
<head>
<title></title>
</head>
<body>
<div style="position:relative;width:250px">
<a id="link" href="javascript:" onclick="showDiv(this)">Show menu</a>
<ul id="entries" style="display:none;background:#DEF;padding:0;margin:0">
<li>item 1</li>
<li>item 2</li>
</ul>
<input id="inp" style="position:absolute;left:-30px;width:0" />
</div>
<script>
function showDiv(lnk){
var entries = document.getElementById('entries'),
inp = document.getElementById('inp'),
nh = 'data-nohide';
//show the entries
entries.style.display = 'block';
entries.removeAttribute(nh);
inp.focus();
//if mouse over, can't close it
entries.onmouseover = function(){
this.setAttribute(nh, true);
inp.focus();
};
//if mouse out, can close it
entries.onmouseout = function(){
this.removeAttribute(nh);
};
entries.onclick = function(e){
//code when the user clicks on the menu...
alert((e.target||e.sourceElement).innerHTML);
this.style.display = 'none';
};
//if the user press ESC
inp.onkeyup = function(e){
if(e.keyCode === 27){
this.style.display = 'none';
this.removeAttribute(nh);
}else{
//do something else with other keys(ie:down, up, enter)...
inp.focus();
}
};
//click somewhere else input onblur
inp.onblur = function(){
if(!entries.getAttribute(nh)){
entries.style.display = 'none';
entries = inp = null;
}
};
}
</script>
</body>
</html>
The trick is to use an input
field that has the focus
, and when it looses it an onblur
is triggered and close the menu.
The mouseover
, mouseout
are there to prevent the onblur
to fire when the user click an item in the menu.
To have a toggle effect like an open/close on the link, I guess 2 links that hide each other are needed.