Ok, it's VERY hard to figure out what you are trying to do with your existing code, as I don't think you understand what/how values are passed to a function, much less an event handler... though I would ASSUME your markup goes something like this?
Code:
<ul>
<li id="menu2">
<a href="#">Menu item</a>
<ul id="submenu2" style="display:none;">
<li><a href="#">Submenu Item 1</a></li>
<li><a href="#">Submenu Item 2</a></li>
<li><a href="#">Submenu Item 3</a></li>
<li><a href="#">Submenu Item 4</a></li>
</ul>
</li>
</ul>
If so... well, your code still doesn't make sense. As I mentioned what should be sent to the function when it's assigned as an event handler is the EVENT object, containing the complete information about the triggered event including the object passed to it. As such this:
Code:
function showmenu(menu, subMenu)
Is assigning the EVENT to menu (making the whole thing ignore your global menu variable) and probably setting submenu to "null" and/or undefined since event callbacks don't have two parameters.
You also assign the event to "window" -- that means the ENTIRE window triggers when you mouse-over it, not just the elements you want to target. Instead of window.addEventListener -- assuming I'm following your intent -- you should be targeting menu.addEventListener.
Likewise you should be listening for both mouseover and mouseout... that "menu" variable would NOT be telling you that.
Oh, and if you're declaring multiple var in a row, you don't need to say var on each one, you can make a comma delimited list after a single var statement instead.
Code:
var
menu = document.getElementsByClassName("menu2"),
subMenu = document.getElementsByClassName("submenu2");
function showMenu(e) {
subMenu.style.display='block';
}
function hideMenu(e) {
subMenu.style.display='none';
}
menu.addEventListener('mouseover', showMenu, false);
menu.addEventListener('mouseout', hideMenu, false);
Is probably what you are trying to do. Now, that said... you have to take legacy IE compatibility into account as it doesn't handle events the same way as "proper" JavaScript. Thanks to fear of being sued by Netscape (or even Sun) back when IE went to war with Netscape 4, all versions of IE prior to 9 run JScript, not JavaScript, a similar and mostly compatible equivalent that has a few minor differences just to set it apart.
One of the biggest differences is that the event is not passed as a parameter to the function and instead is stored in "window.event", and that there is no addEventListener method on anything... Thankfully some simple polyfills can fix that; I use two functions to be certain of compatibility on event handling... a lot of people will use massive frameworks that try to change how JS itself works (like jQuery) but really you should learn how to do it without the framework first; and frankly if you do so you may quickly find that these "frameworks" are a waste of time, effort, bandwidth, and on the whole end up a steaming pile of epic fail.
The two function wrappers I use are as follows:
Code:
function eventAdd(e, eventName, callback) {
if (e.addEventListener) e.addEventListener(eventName, callback, false);
else e.attachEvent('on' + eventName, callback);
}
function eventProcess(e, prevent) {
e = e || window.event;
if (!e.target) e.target = e.srcElement;
if (prevent) {
// this MAY be overkill... Oh, who are we kidding, it is!
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
}
return e;
}
In the first function, "e" is the element you are adding the handler to, "eventName" is the name of the event you want to handle like "mouseover" or "mouseout", and "callback" is the function you want run when the event occurs.
The second function (which you don't need for the above examples) properly makes sure you have the current "event" and that the source of the event, aka "target" is set to the proper value. "e" is the event object (if any) and the "prevent" value if true will stop any other handlers in the event chain from trying to do anything. We'll get to why we use that function shortly -- but for now using the above the code would change thusly:
Code:
// handy library functions
function eventAdd(e, eventName, callback) {
if (e.addEventListener) e.addEventListener(eventName, callback, false);
else e.attachEvent('on' + eventName, callback);
}
function eventProcess(e, prevent) {
e = e || window.event;
if (!e.target) e.target = e.srcElement;
if (prevent) {
// this MAY be overkill... Oh, who are we kidding, it is!
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
}
return e;
}
// our actual functionality
var
menu = document.getElementsByClassName("menu2"),
subMenu = document.getElementsByClassName("submenu2");
function showMenu(e) {
subMenu.style.display='block';
}
function hideMenu(e) {
subMenu.style.display='none';
}
eventAdd(menu, 'mouseover', showMenu);
eventAdd(menu, 'mouseout', hideMenu);
That would now work all the way back to IE 5.0
Now, that's cute and all, but really directly manipulating style properties is sloppy coding, even when using JavaScript to handle these types of events. Style.display can lead to all sorts of woes and headaches like what if certain layouts you don't want that behavior? What if you want different behaviors for different layouts and media targets. Swapping classes instead of display behavior would be a far more versatile approach. Whilst the newest JavaScript has direct support for doing this through the new "classList" method on DOM elements, those methods are sadly really slow and you cannot rely on them existing in older browsers, so I use some special functions for handling that on elements as well. You could go directly to "className" to add/remove classes, but that doesn't preserve any existing ones so it's best to simply use a few more simple library functions.
Code:
function classExists(e, className) {
return RegExp('(\\s|^)' + className + '(\\s|$)').test(e.className);
}
function classAdd(e, className) {
if (!classExists(e, className)) e.className += (e.className ? ' ' : '') + n;
}
function classRemove(e, className) {
e.className = e.className.replace(
new RegExp('(\\s|^)' + className + '(\\s|$)'), ' '
) . replace(/^\s+|\s+$/g,'');
}
function classSwap(e, oldClass, newClass) {
classRemove(e, oldClass);
classAdd(e, newClass);
}
Likewise right now, that code would only work on one unique target since it needs to hit elements by ID... meaning you'd need to write unique handlers for each and every one of them... See how my eventProcess function goes after the "target" property of the event and/or window.event? That's the element that actually set off the event... so instead of targeting elements off a global value, we swap a class on the originator.
So that would go something like this:
Code:
function showMenu(e) {
e = eventProcess(e, true);
classSwap(menu, 'hide', 'show');
}
function hideMenu(e) {
e = eventProcess(e, true);
classSwap(menu, 'show', 'hide');
}
That will add or remove the class from the LI, so for using display state (which I'd advise against for accessibility reasons) you'd need this CSS:
Code:
.hide ul { display:none; }
.show ul { display:block; }
Though I'd suggest this instead:
Code:
.hide { overflow:hidden; }
.show { overflow:visible; }
Since I'd assume your dropdown menu is absolute positioned. The Overflow:hidden will actually hide it without things like braille or screen readers accidentally failing to be able to even find your dropdown menu content.
Now a more robust approach would be to make it so these simple handlers can apply quickly to all the LI inside something like your main menu. That would go something like this:
Code:
<ul id="mainMenu">
<li>
<a href="#">Menu item 1</a>
<ul>
<li><a href="#">Submenu Item 1-1</a></li>
<li><a href="#">Submenu Item 1-2</a></li>
<li><a href="#">Submenu Item 1-3</a></li>
<li><a href="#">Submenu Item 1-4</a></li>
</ul>
</li>
<li>
<a href="#">Menu item 2</a>
<ul>
<li><a href="#">Submenu Item 2-1</a></li>
<li><a href="#">Submenu Item 2-2</a></li>
<li><a href="#">Submenu Item 2-3</a></li>
<li><a href="#">Submenu Item 2-4</a></li>
</ul>
</li>
</ul>
To nab all those LI, I'd use a fairly advanced technique called "DOM walking" to get them. The complete code would then go something like this:
Code:
// handy library functions
function eventAdd(e, eventName, callback) {
if (e.addEventListener) e.addEventListener(eventName, callback, false);
else e.attachEvent('on' + eventName, callback);
}
function eventProcess(e, prevent) {
e = e || window.event;
if (!e.target) e.target = e.srcElement;
if (prevent) {
// this MAY be overkill... Oh, who are we kidding, it is!
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
}
return e;
}
function classExists(e, className) {
return RegExp('(\\s|^)' + className + '(\\s|$)').test(e.className);
}
function classAdd(e, className) {
if (!classExists(e, className)) e.className += (e.className ? ' ' : '') + n;
}
function classRemove(e, className) {
e.className = e.className.replace(
new RegExp('(\\s|^)' + className + '(\\s|$)'), ' '
) . replace(/^\s+|\s+$/g,'');
}
function classSwap(e, oldClass, newClass) {
classRemove(e, oldClass);
classAdd(e, newClass);
}
// our actual functionality
function showMenu(e) {
e = eventProcess(e, true);
classSwap(e.target, 'hide', 'show');
}
function hideMenu(e) {
e = eventProcess(e, true);
classSwap(e.target, 'show', 'hide');
}
var
mainMenu = document.getElementById('mainMenu'),
menuChild = mainMenu.firstChild;
while (menuChild) {
if (menuChild.nodeType == 1) {
// is a DOM element, only ones should be LI here
eventAdd(menuChild, 'mouseover', showMenu);
eventAdd(menuChild, 'mouseout', hideMenu);
}
menuChild = menuChild.nextSibling;
}
That might seem a bit complex, but basically I set the menuChild to the first child element of #mainMenu. This could be any type of node since some browsers store whitespace and comments as node types... but nodeType 1 is an actual DOM element, and in the case of a UL all the LI should be the only actual DOM nodes... so if it's nodetype 1 we add our events to it. We then look for the next sibling and keep looping if we find one processing them all.
As such the only ID we needed to target was #mainMenu and it nabs all first level children of that UL. Now, if you wanted to target ALL the LI for this class swap -- which is viable as well, you'd be best off using getElementsByTagName.
Code:
var
mainMenu = document.getElementById('mainMenu'),
menuLI = mainMenu.getElementsByTagName('li');
for (var i = 0, iLen = menuLI.length; i < iLen; i++) {
eventAdd(menuLI[i], 'mouseover', showMenu);
eventAdd(menuLI[i], 'mouseout', hideMenu);
}
Which would add that class swap to EVERY LI inside #mainMenu whether it needs it or not -- but since we're targeting UL inside the LI, that would still function whether or not that UL exists or no... at least down to two depths, then specificity starts to be a pig. Mind you fancy CSS selectors like the ">" child selector could fix that, but those only work in modern browsers and the only reason to resort to scripting for this specific task is legacy browser support. (and then I'd still use CSS and then a .htc polyfill for ancient IE versions)
But as you said, this is more a learning exercise than a proper way of doing things... I'd never do any of this on a modern site.
Either way it's good to know how to do it manually first, so it's refreshing to see someone trying to do this stuff in Vanilla JavaScript without the frameworks first, since until you know how to do it without the framework you can't possibly be qualified to form a rational opinion on if said frameworks -- like jQuery -- are even worth using.
SADLY a lot of people get frustrated before they have that comprehension which is why so many people sing jQuery's praises before they are actually competant enough to know just how badly it's shtupping them.
In any case, you're kind of diving into the deep end of the pool and I know most of what I just presented is pretty advanced, but really that's the fast way to learn to swim. Read it through, try the code, take your time to digest it, and come back with questions.
... and remember, there are no stupid questions, only stupid answers.
Examples of stupid answers:
"Use jQuery"
"Use Bootstrap"
"Use Dreamweaver"
"Use React"