Question

I have a grid that will be used as a menu, it consist of a top row made from a ul with 3 li's set to display:inline-block within each of these li's there is a ul and under that 4 li's. I've assigned an event listener to respond to click on the li under the inner ul's with class is "inner".
You can see the code here: http://jsfiddle.net/jimeast123/qK2Ju/2/ When I click on the items in the first 2 columns(you need to avoid clicking on the text) I get the expected alert message when I click on the third column there's no response. I've tried with different numbers of columns and the last column never responds.

My code: html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>drop down</title>
    <style>
    body {
        margin: 0;
        padding: 0;
    }
    header {
        height: 120px;
        background-color: #eef;
    }
    .top {
        background-color: #fee;
    }
    .top li {
        display: inline-block;
        width: 5.5em;
        height: 1.5em;
        border: 1px solid black;
        text-align: center;
    }
    li {
        list-style: none;
    }
    .inner {
        margin-left: -2.55em;
        display:block;
    }
    li a {
        text-decoration: none;
    }
    .inner li {
        display: block;
        text-align: center;
        background-color: #efe;
    }
    </style>

</head>
<body>
<!-- heading>h1+nav>ul.top>li*5>(a[href=#]>{menu$})>ul.inner>li*4>a[href=#]>{menu$} -->
<header>
    <nav>
        <header>
    <nav>
        <ul class="top">
            <li><a href="#">menuA</a>
                <ul class="inner" id="one">
                    <li><a href="#">item1</a></li>
                    <li><a href="#">item2</a></li>
                    <li><a href="#">item3</a></li>
                    <li><a href="#">item4</a></li>
                </ul>
            </li>
            <li><a href="#">menuB</a>
                <ul class="inner" id="two">
                    <li><a href="#">item1</a></li>
                    <li><a href="#">item2</a></li>
                    <li><a href="#">item3</a></li>
                    <li><a href="#">item4</a></li>
                </ul>
            </li>
            <li><a href="#">menuE</a>
                <ul class="inner" id="five">
                    <li><a href="#">item1</a></li>
                    <li><a href="#">item2</a></li>
                    <li><a href="#">item3</a></li>
                    <li><a href="#">item4</a></li>
                </ul>
            </li>
        </ul>
    </nav>
</header>
    </nav>
</header>
<script src="dropdown.js"></script>
</body>
</html>

JavaScript:

if (document.body.addEventListener) {
    document.body.addEventListener('click', theHandler, false);
} else {
    document.body.attachEvent('onclick', theHandler); //for IE
}

function theHandler(e){
    e = e || window.event;
    var target = e.target || e.srcElement;
    if (target.className.match(/inner/)) {
        //console.log("inner class was clicked");
        alert("inner class was clicked");
    }
}
Was it helpful?

Solution

The user can't actually click on the .inner elements because when they try do, what they actually end up clicking on is the child objects in the .inner objects. As such, you won't get a target with that class.

You have a couple choices. First off, if your HTML isn't dynamically created with JS code, then you could just apply the event listeners directly to all .inner objects and the normal click event propagation will cause them to trigger when child nodes are clicked on.

Or, if you want to keep the event handler on the body, then the work-around is probably to see if the parent chain contains that class (your target will be several levels below .inner).

function hasClass(elem, cls) {
    var str = " " + elem.className + " ";
    var testCls = " " + cls + " ";
    return(str.indexOf(testCls) != -1) ;
}

function isTargetInClass(target, cls) {
    while (target && target !== document.body) {
        if (hasClass(target, cls)) {
            return true;
        }
        target = target.parentNode;
    }
    return false;
}

function theHandler(e){
    e = e || window.event;
    var target = e.target || e.srcElement;
    if (isTargetInClass(target, "inner")) {
        //console.log("inner class was clicked");
        alert("inner class was clicked");
    }
}

OTHER TIPS

I think it has to do with the fact that you're adding the event listener to the body, and the target may not be what you hope (e.g. that's why clicking on the text doesn't work). You're probably better off replacing your code with something like this (requires jQuery):

$('.inner').click(function() {
    alert('inner was clicked');
});

This will add the click handler to all elements with class inner

Also, you might want to do an "Inspect Element" to see if where things are when you're clicking on them is really what you're expecting.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top