事件处理程序执行但不显示效果

时间:2015-01-29 21:20:14

标签: javascript drop-down-menu javascript-events

我正在开发一个基于Javascript的绘画应用程序,我现在正在创建一个菜单,好像客户端区域是桌面窗口一样。

The menu with a dropdown active

在这个菜单当然,我有一些主要标签,他们的下拉列表包含所有项目,有时还有子标签导致孩子下拉菜单。对于我的问题,让我们关注主要标签(例如'文件'或编辑')。简而言之,问题在于他们不会通过显示下拉来做出反应。

在初始化页面的过程中,我按类抓取所有元素:

MTabs = menubar.getElementsByClassName('mainTab');

然后我添加事件处理程序。我传递给addEventListener()的函数由中间函数返回,该函数使用对处理程序所针对的节点的节点引用来组装特定函数。我找到了一个example("臭名昭着的循环问题" - >"工作参考"),它显示了这种类型的处理从循环内添加事件处理程序。这些是对addEventListener()的调用:

for (var i=0, n=MTabs.length; i<n; i++){ // MTab
    node = MTabs[i];
    node.addEventListener('mouseover', onMouseOver_MTabs(node));
    node.addEventListener('click', onClick_MTabs(node));
}

这些是返回处理函数的函数:

var onMouseOver_MTabs = function(node){
    var dropdown = getChildDropdown(node);
    return function(){
        if (menuClicked){
            deactivateMenu(); // Hide all dropdowns
            setActive(node); // add active class to this tab
            setActive(dropdown); // show this tabs dropdown
        }
    }
}

var onClick_MTabs = function(node){
    var dropdown = getChildDropdown(node);
    return function(){
        if (menuClicked){
            quitMenu(); // Hide all menu elements, set menuClicked false
        }
        else {
            setActive(node);
            setActive(dropdown);
            openMenu(); // set menuClicked true
        }
    }
}

全局布尔变量menuClicked表示用户是否单击了菜单。如果为true,则菜单元素应对鼠标悬停事件做出反应,并显示正确的下拉列表。单击选项卡时(在工具&#39;选项卡上方的图像中),此变量在openMenu()内设置为true。奇怪的是,函数返回后,变量再次为false。我通过console.log()跟踪注意到了这一点。此外,下拉列表未显示。我希望这个工作(之前有),因为代码非常简单:

var setActive = function(node){
// Adding css class that sets visibility:visible for dropdowns
    node.classList.add('menuActive');
}

对我而言,处理程序似乎没有设置实际的全局menuClicked变量,但为什么不呢?或者这些函数是否将menuClicked硬编码为false的处理程序返回到if语句中?

我尝试了很多东西,但我仍然想知道为什么这样做不成功。我想我已经在JavaScript的自由度上迷失了一点...... :)。

我做了JSFiddle

2 个答案:

答案 0 :(得分:1)

https://jsfiddle.net/ssantee/9gaj49be/1/

在添加事件处理程序时,您正在进行的工厂操作并不像我认为的那样。在分配处理程序时,这些函数正在执行,在与页面进行任何交互之前会导致多次执行。我会重构那个在事件处理期间找到事件目标。更直截了当。

所以,而不是

node.addEventListener('mouseover', onMouseOver_STabs(node));

执行此操作以分配处理程序。

node.addEventListener('mouseover', onMouseOver_STabs);    

然后,处理程序应该采用单个参数&#39; event&#39;,并使用它来查找事件的目标

var node = event.target;

至于menuClicked var,我认为这是正常的,但是另一个因素是让它变得不可预测;点击事件正在从标签页面传播到“菜单_背景”,导致“退出菜单”&#39; quitMenu&#39;在“openMenu&#39;。

之后立即被召唤

添加

event.stopPropagation();

在你的事件中,处理程序将阻止这一点,正如我在你的小提琴中所做的那样。请参阅 https://developer.mozilla.org/en-US/docs/DOM/DOM_Reference/Examples#Example_5:_Event_Propagation 为了更深入的解释。

另外,重要的是,我不认为你可以使用addEventListener将处理程序附加到窗口加载。您可以采取一些方法来初始化全局变量但我会删除

//doesn't work
window.addEventListener('load',initMain); 

并将初始化代码移至另一个脚本标记,就在html

之前
</body>

标签。或者,将整个脚本移动到那里并将初始化代码移动到它的底部。这在我的小提琴中没有得到很好的反映,因为它只在一个盒子里,我认为jsfiddle包装了一个窗口。载入所有它。

答案 1 :(得分:0)

只有一个jsfiddle CSS菜单和一种方法来检测点击的动作。也许这个例子有助于在这种方法的基础上进行构建。

https://jsfiddle.net/tanertopal/pnamnmc3/1/

HTML

<ul class="menu">
    <li>
        <span>File</span>
        <ul>
            <li action="open">Open</li>
            <li action="save">Save</li>
        </ul>
    </li>
    <li>
        <span>Edit</span>
        <ul>
            <li action="copy">Copy</li>
            <li action="paste">Paste</li>
        </ul>
    </li>
</ul>

CSS

* {
    font-family:"Helvetica Neue";
    font-size: 14px;
}
body {
    background: grey;
}

ul {
    height: 16px;
    list-style-type: none;
    margin: 0;
    padding: 5px 20px;
    background: rgba(0, 0, 0, .25);
    color: white;
}

ul.menu li {
    float: left;
    display: inline;
    margin: 0 30px 0 0;
}

ul.menu li:hover ul, ul.menu li ul:hover {
    display: block;
}

ul.menu li > ul {
    display: none;
    height: auto;
    position: absolute;
    top: 34px;
    background: rgba(0, 0, 0, .25);
    list-style-type: none;
    margin: 0;
    padding: 5px;
}

ul.menu li > ul li {
    display: block;
    float: none;
    padding: 5px;
}

ul.menu li > span {
    padding: 5px 0;
}

ul.menu li.show > ul {
    display: block;
}

JS

var menu = document.querySelectorAll('ul.menu')[0];

menu.addEventListener('click', function (event) {
    var target = event.target;
    var action = target.getAttribute("action");

    // now do something with action
    alert(action);
});