事件处理程序正在执行但未显示效果

Event handler executing but not showing effect

我正在开发一个基于 Javascript 的绘画应用程序,我现在正在其中制作一个菜单,就好像客户区是一个桌面 window。

当然,在这个菜单中,我有一些主选项卡,它们的下拉菜单包含所有项目,有时还有子选项卡导致子下拉菜单。对于我的问题,让我们关注主要选项卡(如 'File' 或 'Edit')。简而言之,问题是他们不会通过显示下拉菜单来做出反应。

在初始化页面的过程中,我通过class:

抓取了所有的元素
MTabs = menubar.getElementsByClassName('mainTab');

然后我添加事件处理程序。我传递给 addEventListener() 的函数由一个中间函数 return 编辑,该函数将特定函数与对处理程序所针对的节点的节点引用组合在一起。我发现了一个 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));
}

这些是 return 处理函数的功能:

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,则菜单元素应对鼠标悬停事件做出反应,并显示正确的下拉菜单。单击选项卡时(在 'tools' 选项卡上方的图像中),此变量在 openMenu() 内设置为 true。奇怪的是,在函数执行 returned 之后,变量再次为 false。我通过 console.log() 跟踪注意到了这一点。此外,未显示下拉列表。我希望它能工作(以前有过),因为代码非常简单:

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

对我来说,这看起来像是处理程序没有设置实际的全局 menuClicked 变量,但为什么不设置呢?或者函数 return 带有 menuClicked 的处理程序是否被硬编码为 false 到它们的 if 语句中?

我已经尝试了很多东西,但我仍然想知道为什么这不起作用。我想我在 JavaScript 的苛刻自由中有点迷失了自己 ... :)

我做了一个JSFiddle

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

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

所以,而不是

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

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

node.addEventListener('mouseover', onMouseOver_STabs);    

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

var node = event.target;

至于 menuClicked 变量,我认为它可以正常工作,但另一个因素使其不可预测;单击事件从选项卡传播到 'menu_background',导致 'quitMenu' 在 'openMenu' 之后立即被调用。

添加

event.stopPropagation();

在你的事件处理程序中会阻止这种情况,就像我在你的 fiddle 的分支中所做的那样。请参见 https://developer.mozilla.org/en-US/docs/DOM/DOM_Reference/Examples#Example_5:_Event_Propagation 以获得更深入的解释。

此外,重要的是,我认为您无法使用 addEventListener 将处理程序附加到 window 加载。您可以采用几种方法来初始化全局变量,但我会删除

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

并将您的初始化代码移动到另一个脚本标记,就在 html

之前
</body>

标签。或者,将整个脚本移到那里并将初始化代码移到所有脚本的底部。这在我的 fiddle 中没有得到很好的反映,因为它都在一个盒子里,我认为 jsfiddle 将所有东西包裹在 window.onload 中。

做了一个 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);
});