使用html,css和javascript在简单的菜单中冒泡 [英] bubbling in a simple menu using html, css and javascript

查看:82
本文介绍了使用html,css和javascript在简单的菜单中冒泡的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,对于一个简单的菜单,如果子菜单被展开/显示,那么我需要子菜单在几个条件下折叠:


  1. 点击主菜单或子菜单LI时

  2. 当视口大于定义的最小宽度时

  3. 当点击发生在屏幕上的任何其他位置时

只要我没有为addEventListener声明addEventListener,则数字2起作用且数字1起作用mainMenuID元素。出于某种原因,当我为任何mainMenuID元素添加该事件侦听器时,它优先于其他任何内容,因此单击Portfolio或其子菜单项以外的主菜单项,展开和折叠菜单,但单击子菜单或ulID什么都不做。一旦我删除了mainMenuID的eventlistener,点击子菜单或ulID展开/折叠子菜单。



第二个问题是,如何添加点击事件监听页面的其余部分,以便在显示子菜单时,点击会折叠子菜单。



谢谢!

 <!DOCTYPE html> 
< html lang =en>

< head>
< meta charset =UTF-8>
< meta name =viewportcontent =width = device-width,initial-scale = 1.0>
< meta http-equiv =X-UA-Compatiblecontent =ie = edge>
< / head>

< style media =screen>
body {
background-color:black;
}

nav ul {
position:relative;
text-align:left;
}

nav ul li {
display:block; /* 什么????删除这个实际上使文本装饰重新获得....为什么??? * /
}

导航{
text-decoration:none;
}

/ *隐藏默认下拉菜单* /
nav ul ul {
position:relative;
display:none;
左:0;
}

/ *显示单击* /
导航的下拉列表ul li.showSubmenu> ul {
display:block;
头寸:相对;
宽度:30%;
}

.menu a {
text-decoration-style:none;
text-decoration:none;
background-color:black;
颜色:白色;
}

.menu a:悬停{
background-color:white;
颜色:黑色;
}
< / style>

< body>
< nav id =mainMenuIDclass =menu>
< ul>
< li class =menuItem>< a href =#>主页< / a>< / li>
< li class =menuItem>< a href =#>关于< / a>< / li>
< li id =submenuItemclass =submenu_class>< a href =#>< span id =submenuID>投资组合▼< / span>< / a> ;
< ul id =ulID>
< li>< a href =#>横向< / a>< / li>
< li>< a href =#>架构< / a>< / li>
< li>< a href =#>动物< / a>< / li>
< li>< a href =#>其他< / a>< / li>
< / ul>
< / li>
< li class =menuItem>< a href =#>信息< / a>< / li>
< li class =menuItem>< a href =#>联系< / a>< / li>
< / ul>
< / nav>


< script>
函数openSubmenu(){
if(document.getElementsByClassName('showSubmenu')。length == 0){
document.getElementById(submenuItem)。classList.toggle(showSubmenu );
document.getElementById(submenuID)。textContent =Portfolio▲;
} else {
document.getElementById(submenuItem)。classList.toggle(showSubmenu);
document.getElementById(submenuID)。textContent =Portfolio▼;
}
}


函数resetSubmenu(){
var submenuElements = document.getElementsByClassName('submenu_class showSubmenu');
for(var i = 0; i< submenuElements.length; i ++){
submenuElements [i] .setAttribute('class','submenu_class');
document.getElementById(submenuID)。textContent =Portfolio►;
}
}

function screenWidthFunction(x){
if(x.matches){// if if a narrow screen
document.getElementById( submenuID)。textContent =Portfolio▼;
document.getElementById(ulID)。addEventListener(click,openSubmenu,true);
document.getElementById(submenuID)。addEventListener(click,openSubmenu,true);
document.getElementById(mainMenuID)。addEventListener(click,openSubmenu,true);
} else {
resetSubmenu();
document.getElementById(submenuID)。textContent =Portfolio►;
document.getElementById(ulID)。removeEventListener(click,openSubmenu);
document.getElementById(submenuID)。removeEventListener(click,openSubmenu);
document.getElementById(mainMenuID)。removeEventListener(click,openSubmenu);
}
}

var x = window.matchMedia((max-width:480px));
screenWidthFunction(x);
x.addListener(screenWidthFunction);
< / script>




< >
您好Robidu,我使用这个菜单只使用CSS,但我被引导使用JS来实现它的一些功能,这阻止了大部分CSS的工作。
所以在冰川速度上,我一直在努力学习JS。信不信由你,从视频,W3Schools等学习....我已经写了,重新工作,并且在5或6周的大部分时间里存在这个菜单。完全荒谬。
看来你理解并且很容易熟悉JS。我需要花费更多的时间来弄清楚如何实施你的所有建议,并且在此过程中,我很可能会破坏已经有效的建议。有什么方法我可以请你通过向我提供他们需要去的地方的代码片段,或者只是更具体的建议来救我痛苦。
我摘录了你的文字部分,我有哪些问题(由于评论的字符限制,我必须将它们分成多个评论):


  1. ......你应该使用CSS隐藏子菜单,因为我认为这个
    是默认值。
    q。是的,这是默认值。我以为我已经在我的CSS中使用nav
    ul li.showSubmenu> ul {...}这样做了。

  2. ...实现一个在DOMContentReady $上触发的初始化阶段b $ b建立一个支持基础设施。
    q。这个,我永远不会知道怎么弄清楚自己。

  3. ...需要一个标志,指示菜单是否打开所以
    处理程序可以做出相应的反应...备用自己问题
    切换...手头有菜单的状态......
    q。我认为showSubmenu这个类的切换就是那种旗帜。如何以及在哪里使用变量来代替切换?

  4. ...自己动手......反复附加和删除事件
    听众。,以及稍后的如果菜单是隐藏的,它根本没有得到任何
    的事件。
    q。您是说要将侦听器分配给JS代码中的任何变量吗?我是否仍然需要在现在以相同方式使用侦听器的相同位置引用这些变量?

  5. ...附加一个事件侦听器,用于检查鼠标事件到
    打开子菜单的项目。
    q。你的意思不是我在id =submenuID上的听众?你的建议会有什么不同?

  6. ...将一个resize事件处理程序附加到执行
    check的窗口...
    q。我从W3的例子中裁剪了屏幕宽度代码。如何/在哪里使用innerWidth / innerHeight会有所不同?

  7. ...将冒泡阶段设置为true以拦截事件。
    q。如果你对菜单系统中的项目应用一种,而对页面上的其他元素应用另一种,等等,我无法找到关于冒泡工作的任何解释。我理解基础,但无法弄清楚为什么当使用所有三个监听器时,它不能在这个简单的菜单上工作。

  8. 单击菜单项时折叠菜单...附加事件
    处理程序到每个菜单项(最佳位置是菜单中的链接。
    q。我是否需要为每个LI分配一个ID?


  9. <总体问题:我使用了具有UL和LI的NAV元素,因为
    我被告知它是屏幕阅读器最容易访问的。是否有一个
    更好的方法来做我正在尝试做的事情菜单还是我在正确的轨道上排序


我真诚地为以下所有内容道歉问题/澄清,但我真诚地转动我的车轮,绕圈,在波涛汹涌的大海中,戴着一个盲人d ...以及你可以投入混合的任何其他类比。



我真的很感激您可以提供的任何特定编码来证明您的建议细节。 (或者甚至指出我的例子,以便我可以尝试解决它们。)

解决方案

有点重组应该做的这里有技巧。



首先,你应该使用CSS来隐藏子菜单,因为我认为这是默认的。此外,您应该实现一个初始化阶段,该阶段在 DOMContentReady 上触发以设置支持基础结构。通过这种方式,您可以省去重复附加和删除事件侦听器的麻烦。您可能希望将检查鼠标事件的事件侦听器附加到打开子菜单的项目。您还需要一个标志,指示菜单是否打开,以便处理程序可以做出相应的反应(这样您就可以省去切换等问题,如果需要,还可以立即获得菜单的状态。做任何额外的检查)。一个简单的变量应该在这里诀窍。
正面:如果菜单被隐藏,它根本不会发生任何事件。



如果你对窗口大小感兴趣,我' d建议您切换到 window.innerWidth / window.innerHeight ,因为这是你得到的数值并且可以轻松地与您需要的最小尺寸进行比较。只需将resize事件处理程序附加到执行此检查的窗口,即可进行设置。如果窗口大小低于最小值,只需强制菜单折叠。



如果用户单击文档中的任何位置,则折叠菜单,附加事件监听器寻找鼠标点击/按键到文档对象这里的技巧(将冒泡阶段设置为 true 以拦截事件,然后才能发生其他任何事情)。



单击菜单项时折叠菜单最好通过将事件处理程序附加到每个单独的菜单项来实现(最佳位置是菜单内的链接 - 将冒泡阶段设置为 false )只关闭菜单。



编辑:



我现在通过使用你的HTML和CSS做了一些修改,这就是我提出的(请注意我已经将文件转换为XHTML - 但这取决于你是否要这样做):

 <?xml version =1.0encoding =UTF-8?> 
<!DOCTYPE html>
< html xmlns =http://www.w3.org/1999/xhtml
xml:lang =en>
< head>
< title>子菜单测试用例< / title>
< meta name =viewportcontent =width = device-width,initial-scale = 1.0/>

< style type =text / css>
body {
background-color:black;
颜色:白色;
}

一边{
宽度:15em;
float:left;
}

nav ul {
position:relative;
text-align:left;
list-style:none; / *杀死任何列表装饰* /
}

nav ul ul {
position:relative;
左:0;
}

导航{
text-decoration:none;
}

/ *这个ARIA属性可以大大提高可访问性,也可以使用
来实际隐藏元素,只需绑定display:none;它... * /
[aria-hidden =true] {
display:none;
}

.menu a {
color:white;
}

.menu a:悬停,.menu span:悬停{
background-color:white;
颜色:黑色;
}

/ *覆盖移动设备设置的样式定义就在这里! * /
@media屏幕和(最小宽度:480px)
{
/ *如果子菜单在足够宽的窗口上,则重新定位子菜单... * /
nav ul li > ul {
position:absolute;
margin-top:-1.2em;
左:7em;
}
}
< / style>
< script type =application / javascript>
/ *<![CDATA [* /
var submenu_open = false; //子菜单是否已打开?
var need_mobile = false; //我们在狭窄的窗户上吗?

//小心隐藏并显示子菜单
函数ToggleMenu(p_event)
{
//不要激活e。 G。右键单击...
if(p_event.button!= 0)
return;

if(submenu_open)
{
//如果子菜单先前已经打开,请将其关闭(并在必要时调整
//控制菜单项)
document.getElementById('sub1')。setAttribute('aria-hidden','true');
if(window.innerWidth< 480)
document.getElementById('sub-item1')。childNodes [0] .childNodes [0] .data ='Portfolio▼';
}
其他
{
//如果子菜单先前已关闭,请将其打开(并根据需要调整
//控制菜单项)
document.getElementById('sub1')。setAttribute('aria-hidden','false');
if(window.innerWidth< 480)
document.getElementById('sub-item1')。childNodes [0] .childNodes [0] .data ='Portfolio▲';
}

//这可以防止文件的根节点(即文档对象)来自
//在点击子菜单的上级项时查看事件...
p_event.stopPropagation();

submenu_open =!submenu_open;
}

//点击文档内的任何位置时触发...
函数CloseMenu(p_event)
{
if(!submenu_open)
返回;

document.getElementById('sub1')。setAttribute('aria-hidden','true');
if(window.innerWidth< 480)
document.getElementById('sub-item1')。childNodes [0] .childNodes [0] .data ='Portfolio▼';
submenu_open = false;
}

函数CheckWindowSize(p_event)
{
if(window.innerWidth< 480)
{
if(need_mobile)
返回;

//在移动设备上,将子菜单插入主菜单...
if(submenu_open)
document.getElementById('sub-item1')。childNodes [ 0] .childNodes [0] .data ='投资组合▲';
else
document.getElementById('sub-item1')。childNodes [0] .childNodes [0] .data ='Portfolio▼';
}
其他
{
如果(!need_mobile)
返回;

//如果窗口足够宽,我们可以显示主
//旁边的子菜单//一个...
document.getElementById('sub-item1') .childNodes [0] .childNodes [0] .data ='Portfolio►';
}

need_mobile =!need_mobile;
}

//初始化序列(添加一些事件处理程序)
document.addEventListener('DOMContentLoaded',function(p_event){
document.getElementById(' sub-item1')。addEventListener('click',ToggleMenu,false);
window.addEventListener('resize',CheckWindowSize,false);
document.addEventListener('click',CloseMenu,false) ;

//如果我们在移动设备上,请调整菜单项的文本。
if(window.innerWidth< 480)
{
need_mobile = true;
document.getElementById('sub-item1')。childNodes [0] .childNodes [0] .data ='Portfolio▼';
}
},false);
/ *]]> * /
< / script>
< / head>
< body>
< header>< h1>子菜单测试用例< / h1>< / header>
< aside>
< nav class =menu>
< ul>
< li class =menuItem>< a href =javascript:alert('\'Home \''触发!');>主页< / a>< / li>
< li class =menuItem>< a href =javascript:alert('\'关于'''触发!');>关于< / a>< / li>
<! - 假设此处正常操作(窗口宽度> = 480像素),因此
文本相应地设置...
请注意我删除了一些无关元素和
属性。 - >
< li class =submenuid =sub-item1>< span>投资组合►< / span>
< ul id =sub1aria-hidden =true>
< li>< a href =javascript:alert('\'Landscape \''触发!');>风景< / a>< / li>
< li>< a href =javascript:alert('\'建筑\''触发!');>架构< / a>< / li>
< li>< a href =javascript:alert('\'动物\\''触发!');>动物< / a>< / li>
< li>< a href =javascript:alert('\'其他\\''触发!');>其他< / a>< / li>
< / ul>
< / li>
< li class =menuItem>< a href =javascript:alert('\'信息\''已启动!');>信息< / a>< / li>
< li class =menuItem>< a href =javascript:alert('\'Contact'\\'触发!');>联系< / a>< / li>
< / ul>
< / nav>
< / aside>
< main />
< / body>
< / html>

请参阅(X)HTML中的注释,了解有关正在进行的操作的详细信息。
通过努力,我发现我可以大大简化我已经提到的方法,所以它只归结为三个事件:




  • 调整大小:当窗口宽度低于某个阈值时切换菜单布局

  • 鼠标单击菜单项:打开或关闭子菜单

  • 鼠标点击其他地方:关闭菜单



至于你的问题......



ad 1。:我已经对您提供的CSS进行了一些重组。我抛出了一些定义,并将任何元素隐藏到 aria-hidden 属性(如果设置为true,则不显示该元素)。所述attrtibute在改善可访问性方面也有很大帮助。在此示例中,如果您无法在屏幕上看到它,则屏幕阅读器也不会显示它。



ad 2:设置相当简单。只需在主执行路径中包含 document.addEventListener('DOMContentLoaded',function(p_event){},false);
并添加任何需要设置的内容进入功能。这对于所谓的不引人注目的JavaScript(即,JavaScript本身在需要时将钩子附加到文档而不是将它们硬编码到(X)HTML中)尤其重要。



ad 3。:你可以用一个简单的变量(一个指示菜单是否打开的布尔值)来完成。然后,您可以快速查看菜单的状态,而无需查询DOM。



ad 4。:这是非常繁琐和昂贵的,因此处理程序只附加一次。其余的应该由控制逻辑决定是否有任何事件被默默忽略(如果不满足某些条件则返回)。



ad 5:需要一个你实现的那种处理程序,但我已经简化了一点,以避免在不必要的地方调用DOM(它转向上述标志),此外它还考虑了窗口调整文本的宽度。



ad 6。: innerWidth / innerHeight 是可以轻松实现的数值由DOM返回。我不知道你的方法,但据我估计它似乎有点贵,加上当你将值存储在变量中时,你可以对它进行多次检查(例如,如果你需要检查不同的宽度/高度) ),这只需要简单的比较。你的方法需要你重置匹配条件。



ad 7:我需要在这里纠正自己,因为通过咀嚼你的问题我发现了一切(即应将 addEventListener 的第三个参数设置为 false 。否则,执行的顺序会搞砸,或者某些链接首先看不到该事件。



ad 8。:原来也是不必要的。我从我在JavaScript中实现的上下文菜单中得到了我的初始答案,但由于其性质,我不得不求助于这些处理程序来关闭它。这里的情况有点不同,所以只需省略这些处理程序就可以简化事情。



ad 9:你实际上已经选择了最好的方法。实现导航时,我也使用这种方法。



我希望我可以将你的一些问号变成感叹号。但是,如果您仍有疑问,请务必询问。没有什么比没有答案的问题更糟糕了。


In the code below for a simple menu, if the sub-menu is expanded/showing, then I need the sub-menu to collapse on several conditions:

  1. When the main menu or sub-menu LI is clicked
  2. When the viewport is larger than the defined minimum width
  3. When a click happens anywhere else on the screen

Number 2 works and number 1 works as long as I don't declare the addEventListener for the mainMenuID element. For some reason, when I add that event listener for any mainMenuID element, it takes precedence over anything else thus clicking on a main menu item other than Portfolio or its sub-menu items, expands and collapses the menu, but clicking on the submenuID or the ulID does nothing. As soon as I remove the eventlistener for mainMenuID, clicking on submenuID or ulID expand/collapse the sub-menu.

The second question is, how do I add a click event listener to the rest of the page so that if the sub-menu is showing, the click collapses the sub-menu.

Thank you!

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>

<style media="screen">
body{
  background-color: black;
}

nav ul {
    position: relative;
    text-align: left;
}

nav ul li {
    display: block;  /* WHAT????  REMOVING THIS ACTUALLY MAKES TEXT-DECORATIONS REAPPEAR.... WHY???*/
}

nav a {
    text-decoration: none;
}

/* Hide dropdowns by default */
nav ul ul {
    position: relative;
    display: none;
    left: 0;
}

/* Display Dropdowns on Click */
    nav ul li.showSubmenu > ul {
    display: block;
    position: relative;
    width: 30%;
}

.menu a {
    text-decoration-style: none;
    text-decoration: none;
    background-color: black;
    color: white;
}

.menu a:hover{
    background-color: white;
    color: black;
}
</style>

<body>
    <nav id="mainMenuID" class="menu">
        <ul>
            <li class="menuItem"><a href="#">Home</a></li>
            <li class="menuItem"><a href="#">About</a></li>
            <li id="submenuItem" class="submenu_class"><a href="#"><span id="submenuID">Portfolio ▼</span></a>
            <ul id="ulID">
                <li><a href="#">Landscape</a></li>
                <li><a href="#">Architecture</a></li>
                <li><a href="#">Animal</a></li>
                <li><a href="#">Other</a></li>
            </ul>
            </li>
            <li class="menuItem"><a href="#">Information</a></li>
            <li class="menuItem"><a href="#">Contact</a></li>
       </ul>
    </nav>


<script>
function openSubmenu() {
    if (document.getElementsByClassName('showSubmenu').length == 0){
      document.getElementById("submenuItem").classList.toggle("showSubmenu");
      document.getElementById("submenuID").textContent = "Portfolio ▲";
    } else {
      document.getElementById("submenuItem").classList.toggle("showSubmenu");
      document.getElementById("submenuID").textContent = "Portfolio ▼";
    }
}


function resetSubmenu() {
    var submenuElements = document.getElementsByClassName('submenu_class showSubmenu');
    for (var i = 0; i < submenuElements.length; i++) {
    submenuElements[i].setAttribute('class', 'submenu_class');
    document.getElementById("submenuID").textContent = "Portfolio ►";
    }
}

function screenWidthFunction(x) {
    if (x.matches) {//if it's a narrow screen
        document.getElementById("submenuID").textContent = "Portfolio ▼";
        document.getElementById("ulID").addEventListener("click", openSubmenu, true);
        document.getElementById("submenuID").addEventListener("click", openSubmenu, true);
        document.getElementById("mainMenuID").addEventListener("click", openSubmenu, true);
    } else {
        resetSubmenu();
        document.getElementById("submenuID").textContent = "Portfolio ►";
        document.getElementById("ulID").removeEventListener("click", openSubmenu);
        document.getElementById("submenuID").removeEventListener("click", openSubmenu);
        document.getElementById("mainMenuID").removeEventListener("click", openSubmenu);
      }
   }

    var x = window.matchMedia("(max-width: 480px)");
    screenWidthFunction(x);
    x.addListener(screenWidthFunction);
</script>

<> Hi Robidu, I had this menu working using just CSS, but I was led to use JS for some of its functionality, which stopped most of the CSS from working. So at glacier speeds, I’ve been trying to learn JS on the fly. Believe it or not, learning from videos, W3Schools, etc…. I’ve been writing, re-working, and kludging this menu into existence for the better part of 5 or 6 weeks. Totally ridiculous. It seems you understand and are readily familiar with JS. It would take me many more weeks of trying to figure out how to implement all of your advice, and along the way, I would very likely destroy what already works. Is there any way I could kindly ask you to please save me that pain by providing me with either the code snippets for where they need to go, or just more specific advice. I’ve excerpted below the parts of your text about which I have questions (because of the character limits on comments, I have to split them across multiple comments):

  1. "…you should use CSS to hide the submenu since I think that this is to be the default." q. Yes, it is the default. I thought I was already doing this in "nav ul li.showSubmenu > ul {…}" in my CSS.
  2. "…implement an initialization stage that fires on DOMContentReady to set up a support infrastructure." q. This, I would never know how to figure out myself.
  3. "…need a flag that indicates whether or not the menu is open so that the handler can react accordingly…spare yourself the problem with "toggle"…have the menu's state at hand…" q. I thought the "toggling" of the class "showSubmenu" was serving as that kind of ‘flag’. How and where would I use the variable in place of the toggle?
  4. "…spare yourself…repeatedly attaching and removing event listeners.", and later on "If the menu is hidden, it doesn't get any events at all." q. Are you saying to assign the listeners to variables anywhere in the JS code? Would I not still need to refer to those variables in the same places where I use the listeners now and in the same manner?
  5. "…attach an event listener that checks for mouse events to the the item that opens the submenu here." q. You mean other than the listener I have on id="submenuID"? What would be different about what you are advising?
  6. "…attach a resize event handler to the window that performs this check..." q. I cropped the screen width code from W3’s examples. How/where would using innerWidth/innerHeight be different?
  7. "…set the bubbling stage to true to intercept the event". q. I haven’t been able to find any explanation about bubbling works if you apply one kind to the items in a menu system, and another kind to other elements on a page, etc. I understand the basics, but couldn’t figure out why it wasn’t working on this simple menu when all three listeners were used.
  8. "Collapsing the menu when a menu item is clicked…attach an event handler to each individual menu item (the best spot would be the links inside the menu". q. Would I have to assign an ID to each LI?

  9. Overall question: I used a NAV element with ULs and LIs because I was told it’s the most accessible for screen readers. Is there a better way to do what I’m trying to do with this menu or am I sort of on the right track?

I sincerely apologize for all the follow-up questions/clarifications, but I’m sincerely spinning my wheels on this, going in circles, in rough seas, wearing a blindfold…. and any other analogy you can throw into the mix.

I would truly appreciate any specific coding you can provide to demonstrate the specifics of your advice. (or even point me to examples so that I can try and figure them out).

解决方案

A bit of retooling should do the trick here.

First of all, you should use CSS to hide the submenu since I think that this is to be the default. Furthermore you should implement an initialization stage that fires on DOMContentReady to set up a support infrastructure. This way you spare yourself the hassle with repeatedly attaching and removing event listeners. You would want to attach an event listener that checks for mouse events to the item that opens the submenu here. You would also need a flag that indicates whether or not the menu is open so that the handler can react accordingly (this way you spare yourself the problem with "toggle" and the likes, plus you immediately have the menu's state at hand if you need to do any additional checks). A simple variable should do the trick here. The plus side: If the menu is hidden, it doesn't get any events at all.

If you are interested in the window size, I'd recommend that you switch to window.innerWidth / window.innerHeight instead, because that is a numeric value that you get and which can easily be compared to the minimum size that you require. Just attach a resize event handler to the window that performs this check, and you are set. If the window size cuts below your minimum, just force the menu to collapse.

As far as collapsing the menu if the user clicks anywhere within the document, attaching an event listener looking for mouse clicks / key presses to the document object does the trick here (set the bubbling stage to true to intercept the event before anything else can happen).

Collapsing the menu when a menu item is clicked can best be achieved by attaching an event handler to each individual menu item (the best spot would be the links inside the menu - set the bubbling stage to false) that merely closes the menu.

EDIT:

I have done some tinkering now by taking your HTML and CSS, and this is what I have come up with (please note that I have also transformed the file to XHTML - but that's up to you whether or not you want to do that):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xml:lang="en">
<head>
<title>Submenu Test Case</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<style type="text/css">
body {
  background-color: black;
  color: white;
  }

aside {
  width: 15em;
  float: left;
  }

nav ul {
  position: relative;
  text-align: left;
  list-style: none;         /* Kills any list decoration */
  }

nav ul ul {
  position: relative;
  left: 0;
  }

nav a {
  text-decoration: none;
  }

/* This ARIA attribute can greatly improve accessibility and can also be used
   to actually hide elements just by tying "display: none;" to it... */
[aria-hidden="true"] {
  display: none;
  }

.menu a {
  color: white;
  }

.menu a:hover, .menu span:hover {
  background-color: white;
  color: black;
  }

/* Style definitions that override settings for mobile devices go here! */
@media screen and (min-width: 480px)
  {
/* Reposition the submenu if it's on a sufficiently wide window... */
  nav ul li > ul {
    position: absolute;
    margin-top: -1.2em;
    left: 7em;
    }
  }
</style>
<script type="application/javascript">
/* <![CDATA[ */
var submenu_open = false;   // Has the submenu been opened?
var need_mobile = false;    // Are we on a narrow window?

// Takes care of hiding and showing the submenu
function ToggleMenu(p_event)
  {
// Do not activate e. g. on a right click...
  if(p_event.button != 0)
    return;

  if(submenu_open)
    {
// If the submenu has previously been open, close it (and adjust the
// controlling menu item if necessary)
    document.getElementById('sub1').setAttribute('aria-hidden', 'true');
    if(window.innerWidth < 480)
      document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▼';
    }
  else
    {
// If the submenu has previously been closed, open it (and adjust the
// controlling menu item if necessary)
    document.getElementById('sub1').setAttribute('aria-hidden', 'false');
    if(window.innerWidth < 480)
      document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▲';
    }

// This prevents the document's root node (i. e. the document object) from
// seeing the event when clicking on the superordinate item for the submenu...
  p_event.stopPropagation();

  submenu_open = !submenu_open;
  }

// Triggered upon clicking anywhere inside of the document...
function CloseMenu(p_event)
  {
  if(!submenu_open)
    return;

  document.getElementById('sub1').setAttribute('aria-hidden', 'true');
  if(window.innerWidth < 480)
    document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▼';
  submenu_open = false;
  }

function CheckWindowSize(p_event)
  {
  if(window.innerWidth < 480)
    {
    if(need_mobile)
      return;

// On a mobile device, insert the submenu into the main one...
    if(submenu_open)
      document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▲';
    else
      document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▼';
    }
  else
    {
    if(!need_mobile)
      return;

// If the window is wide enough, we can display the submenu next to the main
// one...
    document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ►';
    }

  need_mobile = !need_mobile;
  }

// Initialization sequence (adds a few event handlers)
document.addEventListener('DOMContentLoaded', function (p_event) {
  document.getElementById('sub-item1').addEventListener('click', ToggleMenu, false);
  window.addEventListener('resize', CheckWindowSize, false);
  document.addEventListener('click', CloseMenu, false);

// If we are on a mobile device, adjust the text of the menu item.  
  if(window.innerWidth < 480)
    {
    need_mobile = true;
    document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▼';
    }
  }, false);
/* ]]> */
</script>
</head>
<body>
<header><h1>Submenu Test Case</h1></header>
<aside>
<nav class="menu">
  <ul>
    <li class="menuItem"><a href="javascript:alert('\'Home\' triggered!');">Home</a></li>
    <li class="menuItem"><a href="javascript:alert('\'About\' triggered!');">About</a></li>
    <!-- Assume normal operation here (window width >= 480 pixels) so the
         text is set accordingly...
         Please note that I have removed some extraneous elements and
         attributes. -->
    <li class="submenu" id="sub-item1"><span>Portfolio ►</span>
    <ul id="sub1" aria-hidden="true">
      <li><a href="javascript:alert('\'Landscape\' triggered!');">Landscape</a></li>
      <li><a href="javascript:alert('\'Architecture\' triggered!');">Architecture</a></li>
      <li><a href="javascript:alert('\'Animal\' triggered!');">Animal</a></li>
      <li><a href="javascript:alert('\'Other\' triggered!');">Other</a></li>
    </ul>
    </li>
    <li class="menuItem"><a href="javascript:alert('\'Information\' triggered!');">Information</a></li>
    <li class="menuItem"><a href="javascript:alert('\'Contact\' triggered!');">Contact</a></li>
  </ul>
</nav>
</aside>
<main />
</body>
</html>

Please see the annotations in the (X)HTML for details on what's going on there. By working on this I have discovered that I could greatly simplify the method that I have mentioned yet again so it just boils down to three events:

  • Resize: Switches the menu layout when the window width undercuts a certain threshold
  • Mouse click on menu item: Opens or closes the submenu
  • Mouse click anywhere else: Closes the menu

As for your questions...

ad 1.: I have done a bit of retooling of the CSS that you have provided. I have tossed some definitions out and have tied hiding any elements to the aria-hidden attribute (if that is set to true, the element is not displayed). Said attrtibute also helps a lot in improving accessibility. In this example, if you cannot see it on the screen, a screenreader won't display it, either.

ad 2.: It's rather simple to set up. Just include document.addEventListener('DOMContentLoaded', function (p_event) { }, false); in the main execution path and add anything that needs to be set up into the function. This is crucial especially for so-called unobtrusive JavaScript (that is, JavaScript that itself attaches hooks to the document where needed instead of hard-coding them into the (X)HTML).

ad 3.: You can get that done with a simple variable (a boolean that indicates whether the menu is open). You can then quickly check on the menu's state without having to query the DOM.

ad 4.: That is extremely tedious and costly at best so handlers are only attached once. The rest should be up to the control logic whether any events are silently ignored (by just returning if certain conditions aren't met).

ad 5.: It takes a handler of the sort that you have implemented, but I have simplified it a bit to avoid calling the DOM where unnecessary (it resorts to the aforementioned flag), plus it also takes into account the window's width for adjusting the text.

ad 6.: innerWidth / innerHeight are numeric values that can easily be returned by the DOM. I don't know about your method, but by my reckon it seems to be a bit expensive, plus when you store the value in a variable, you can perform multiple checks on it (e. g. if you need to check for different widths/heights), which just requires simple comparisons. Your approach would require you to reset the matching condition.

ad 7.: I need to correct myself here, because by chewing on your problem I found that everything (i. e. the third parameter to addEventListener) should be set to false. Otherwise the order of execution is messed up, or some links don't see the event in the first place.

ad 8.: Turned out to be unnecessary as well. I had derived my initial answer from a context menu that I have implemented in JavaScript, but because of its nature I had to resort to these handlers to close it. Here things are a little different so things can be simplified just by omitting these handlers.

ad 9.: You actually have picked the best way here. When implementing a navigation, I'm also using this approach.

I hope I could turn some of your question marks into exclamation marks. However, if you still have questions, by all means, please ask. There's nothing worse than questions left unanswered.

这篇关于使用html,css和javascript在简单的菜单中冒泡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆