我正在开发一个基于Javascript的绘画应用程序,我现在正在创建一个菜单,好像客户端区域是桌面窗口一样。
在这个菜单当然,我有一些主要标签,他们的下拉列表包含所有项目,有时还有子标签导致孩子下拉菜单。对于我的问题,让我们关注主要标签(例如'文件'或编辑')。简而言之,问题在于他们不会通过显示下拉来做出反应。
在初始化页面的过程中,我按类抓取所有元素:
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
答案 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);
});