簡體   English   中英

如何使用 Javascript 通過單擊向嵌套在 UL 中的多個子元素添加和刪除類?

[英]How do I add and remove classes to multiple child elements nested in an UL with a single click using Javascript?

我想首先為這么長的問題道歉,我只是希望我不會因此而難以理解。

我在 UL 中創建了一個帶有三個菜單元素的側欄,它們展開以顯示子元素、更改背景顏色並在單擊時刪除 hover 效果。 我通過定義一個 function 來做到這一點,它在單擊菜單元素時添加和刪除包含相關屬性的類。

我希望側邊欄執行但似乎無法執行的四件事如下;

  1. 一次只展開一個選擇/單擊的項目,而未選擇的菜單元素的所有 rest 都被折疊。 這意味着如果我單擊第一個項目,它會展開,當我單擊第二個項目時,第一個項目會折疊,而我單擊的項目會展開等等。
  2. 被選中/點擊的元素會改變其背景顏色以表明它被選中。
  3. 選中/點擊的元素對文本沒有 hover 效果,而未選中的元素對它們有文本顏色變化的 hover 效果。
  4. 我還希望選定的菜單元素能夠打開和關閉擴展而不影響 UL 中的其他元素。

我認為我的代碼最麻煩的地方是添加和刪除類,特別是考慮到嵌套在<li>標簽內的<a>標簽(點擊的元素)是“懸停” class 需要的地方被添加/刪除,以及展開的<ul>標簽也嵌套在點擊的元素內。

 function toggleMenu(e) { var kids = document.querySelector("#menuList").children; var unselectedLink = document.querySelectorAll(".unselected a"); var unselectedDropdown = document.querySelectorAll(".unselected ul"); //adds "unselected" class to all elements exept the selected one for (var i = 0; i < kids.length; i++) { kids[i].className = "unselected"; } //adds "menuHover" class to all elements exept the selected element for (var i = 0; i < unselectedLink.length; i++) { unselectedLink[i].className = "menuHover"; } for (var i = 0; i < unselectedDropdown.length; i++) { unselectedDropdown[i].classList.remove("show") } //adds "selected" class, removes "menuHover" class and adds "toggle" to the selected element e.className = "selected"; document.querySelector(".selected a").classList.remove("menuHover"); document.querySelector(".selected ul").classList.toggle("show"); }
 .sidebar { position: fixed; width: 250px; height: 100%; left: 0px; top: 0; background: #1b1b1b; font-family: sans-serif; }.menu-bar { background: #1b1b1b; height: 60px; display: flex; align-items: center; padding-left: 42px; }.side-text { color: #C5C5C5; font-weight: bold; font-size: 20px; } nav ul { background: #1b1b1b; height: 100%; width: 100%; list-style: none; margin-left: 0; padding-left: 0; } nav ul li { line-height: 40px; } nav ul li a { position: relative; color: #C5C5C5; text-decoration: none; font-size: 14px; padding-left: 43px; font-weight: normal; display: block; width: 100%; } nav ul ul { position: static; display: none; } nav ul ul li a { font-family: sans-serif; font-size: 13px; color: #e6e6e6; padding-left: 80px; font-weight: lighter; }.submenu-item:hover { background: #1e1e1e;important. } /*...........selected and show..................*/:selected { background-color; #255DAA. }:show { display; block. } /*...........unselected and hover..................*/:unselected { color; #1e1e1e. }:menuHover:hover { color; #255DAA; }
 <nav class="sidebar"> <div class="menu-bar"> <label class="side-text">MENU</label> </div> <ul id="menuList"> <li class="selected" onclick="toggleMenu(this)"> <a href="#" class="" id="staff-btn">Staff</a> <ul> <li><a href="#">New Staff</a></li> <li><a href="#">View Staff</a></li> </ul> </li> <li class="unselected" onclick="toggleMenu(this)"> <a href="#" id="notes-btn" class="menuHover">Notes</a> <ul> <li><a href="#">New Note</a></li> <li><a href="#">Edit Notes</a></li> </ul> </li> <li class="unselected" onclick="toggleMenu(this)"> <a href="#" class="menuHover" id="tasks-btn">Tasks</a> <ul> <li><a href="#">New Tasks</a></li> <li><a href="#">Edit Task</a></li> </ul> </li> </nav>

我非常接近,但不知何故,從邏輯上講,我在 JavaScript 上做錯了事情,因此對代碼進行任何調整以使其達到上述所有四個目標都將不勝感激。 謝謝

一個簡單的方法來做到這一點:

如果元素被選中,只需取消選中它

如果不是,請取消選擇所有元素和 select 被點擊的元素

function toggleMenu(el) {
  if (el.classList.contains("selected")) {
    el.classList.remove("selected");
    el.classList.add("unselected");
  }
  else {
    for (const child of document.getElementById("menuList").children) {
      child.classList.remove("selected");
      child.classList.add("unselected");
    }
    el.classList.remove("unselected");
    el.classList.add("selected");
  }
}

編輯 1您可以使用以下 css 取消隱藏所選菜單項的子菜單:

.selected ul {
  display: block;
}

編輯 2我遇到了實際實施它的麻煩。

 function toggleMenu(el) { if (el.classList.contains("selected")) { el.classList.remove("selected"); el.classList.add("unselected"); } else { for (const child of document.getElementById("menuList").children) { child.classList.remove("selected"); child.classList.add("unselected"); } el.classList.remove("unselected"); el.classList.add("selected"); } }
 .sidebar { position: fixed; width: 250px; height: 100%; left: 0px; top: 0; background: #1b1b1b; font-family: sans-serif; }.menu-bar { background: #1b1b1b; height: 60px; display: flex; align-items: center; padding-left: 42px; }.side-text { color: #C5C5C5; font-weight: bold; font-size: 20px; } /* menu */.menu { background: #1b1b1b; height: 100%; width: 100%; list-style: none; margin-left: 0; padding-left: 0; }.menu-item { line-height: 40px; }.menu-item a { position: relative; color: #C5C5C5; text-decoration: none; font-size: 14px; padding-left: 43px; font-weight: normal; display: block; width: 100%; } /* submenu */.submenu { position: static; display: none; list-style: none; }.submenu-item a { font-family: sans-serif; font-size: 13px; color: #e6e6e6; padding-left: 80px; font-weight: lighter; }.submenu-item:hover { background: #1e1e1e; } /* selected and unselected */.selected { background-color: #255DAA; }.selected.submenu { display: block; }.unselected { color: #1e1e1e; }.unselected:hover a { color: #255DAA; }
 <nav class="sidebar"> <div class="menu-bar"> <label class="side-text">MENU</label> </div> <ul class="menu" id="menuList"> <li class="menu-item selected" onclick="toggleMenu(this)"> <a href="#" id="staff-btn">Staff</a> <ul class="submenu"> <li class="submenu-item"><a href="#">New Staff</a></li> <li class="submenu-item"><a href="#">View Staff</a></li> </ul> </li> <li class="menu-item unselected" onclick="toggleMenu(this)"> <a href="#" id="notes-btn">Notes</a> <ul class="submenu"> <li class="submenu-item"><a href="#">New Note</a></li> <li class="submenu-item"><a href="#">Edit Notes</a></li> </ul> </li> <li class="menu-item unselected" onclick="toggleMenu(this)"> <a href="#" id="tasks-btn">Tasks</a> <ul class="submenu"> <li class="submenu-item"><a href="#">New Tasks</a></li> <li class="submenu-item"><a href="#">Edit Task</a></li> </ul> </li> </ul> </nav>

作為公認答案的替代方案,我正在研究兩種不同的方法......

#menuList li<a>菜單項列表的容器,這些菜單項具有相鄰的<ul>子菜單項。 為了使用 CSS 輕松選擇,我將 class .menu-item分配給那些<a>

兩個版本的CSS 邏輯基本相同:

  • 設置.menu-item相鄰的ul子菜單項默認隱藏
  • 定義:hover colors
  • 為選定的.menu-item和相鄰的ul定義 colors ( :focus.selected為真)
  • .menu-item被選中時,使.menu-item相鄰的ul子菜單項可見(同上)

區別:對於 CSS,我們只使用:focus選擇器,對於 CSS 和 Javascript,我們使用.selected

僅限 CSS (自動對焦模糊

單擊時<a>獲得焦點(如按鈕、輸入等:focustrue )。 當用戶在焦點元素之外單擊/點擊時,它會自動再次失去焦點(變得模糊並且:focusfalse ,如:not(:focus) = 'blur' )。 我們可以使用 CSS :focus選擇器來處理用戶點擊和修改元素, MDN: ':focus'

CSS 和 Javascript (根據要求進行聚焦模糊

OP 希望選定的.menu-item及其相鄰的ul子菜單項保持可見,直到用戶再次明確取消選擇它。 這不能使用:focus選擇器完成,因此我們忽略該選擇器並使用 class .selected代替自己處理焦點模糊要求, MDN:HTMLElement.blur()

Javascript 邏輯相當簡單:

將“click”- eventListener ( MDN: Element: click event ) 附加到主容器#menuList處理:

  • 當一個.menu-item被選中並且它是當前的.selected然后模糊它( menuItemBlur()
  • 否則
    • 當我們打開之前選擇.menu-item時,首先模糊它( menuItemBlur()
    • 然后聚焦新選擇.menu-item ( menuItemFocus() )

操作代碼的更改

  • 刪除了不需要的 CSS
  • 從 HTML 中刪除了不需要class屬性
  • <#menuList li a>中的href="#"更改為href="javascript:void(0)"以防止它在瀏覽器歷史記錄中創建條目(子菜單項仍將創建條目)。

下面的代碼片段被大量評論,應該是不言自明的。

 'use-strict'; var activeItem; // Holds the currently '.selected''.submenu' (null/undefined if none) // Attach 'click' event listener to the #menuList document.getElementById('menuList').addEventListener('click', function(e) { menuItemToggle(e.target) }); function menuItemToggle(el) { if (el.classList.contains('menu-item2')) { // When a '.menu-item' gets clicked (not its kids) if (el.classList.contains('selected')) { // and it is the '.selected''.menu-item' menuItemBlur(el); // then close it and remove focus() } else { if (activeItem) // When there is a currently selected '.menu-item' menuItemBlur(activeItem); // then deactivate it menuItemFocus(el); // Now activate the clicked `.menu-item` }; }; function menuItemBlur(el) { el.classList.remove("selected"); // Set the '.menu-item' to not '.selected' activeItem = null; // and remove the reference to it el.blur(); // Remove focus from element for CSS ':focus' //...extend with other 'Blur' stuff... }; function menuItemFocus(el) { el.classList.add("selected"); // Set the '.menu-item' to '.selected' activeItem = el; // and save a reference to it //...extend with other 'Focus' stuff... }; };
 .sidebar { position: fixed; width: 250px; height: 100%; left: 0px; top: 0; background: #1b1b1b; font-family: sans-serif; }.menu-bar { background: #1b1b1b; height: 60px; display: flex; align-items: center; padding-left: 42px; }.side-text { color: #c5c5c5; font-weight: bold; font-size: 20px; } nav ul { background: #1b1b1b; height: 100%; width: 100%; list-style: none; margin-left: 0; padding-left: 0; } nav ul li { line-height: 40px; } nav ul li a { position: relative; color: #c5c5c5; text-decoration: none; font-size: 14px; padding-left: 43px; font-weight: normal; display: block; width: 100%; } nav ul ul { position: static; display: none; } nav ul ul li a { font-family: sans-serif; font-size: 13px; color: #e6e6e6; padding-left: 80px; font-weight: lighter; } /*************/ /* ADDED CSS */ /*************/ /* All classes starting with "menu-item" */ [class^="menu-item"] + ul { display: none } /* hide adjacent UL */ [class^="menu-item"]:hover { color: #255daa } /* hover color */ a + ul li a:hover { color: #c5c5c5; background-color: #1b1b1b } /* menu-item adjacent sub-menu-items hover colors Here the generic form is used, but it would probably be more clear to be specific and use: - either.menu-item1:focus + ul li a:hover - or.menu-item2.selected + ul li a:hover */ /* ':focus' version This version uses the CSS ':focus' without any Javascript. Main difference with the '.selected' version below is that when the user clicks outside the '.menu-item', the '.menu-item' looses focus and therefore gets hidden again (as:focus is no longer true). */.menu-item1:focus, .menu-item1:focus + ul { color: #e6e6e6; background-color: #255DAA } /* focus colors */.menu-item1:focus + ul { display: block } /* show adjacent UL */ /* '.selected' version, with Javascript. Basically the same CSS, but now using class '.selected' instead of ':focus'. Closing occurs only on user specific 'click'. */.menu-item2.selected, .menu-item2.selected + ul { color: #e6e6e6; background-color: #255DAA } /* focus colors */.menu-item2.selected + ul { display: block } /* show adjacent UL */ /*********************/ /* for demo use only */ /*********************/ nav h3 { color: rgba(100, 149, 237,.9); /* CornflowerBlue */ font-style: italic; padding-left: 43px; }.anchor { color: white; padding-left: 43px; }.content { font-size: 1.5rem; margin: 5rem 300px; } /* preferred globals */ html,body { box-sizing: border-box; width: 100%; max-width: 100% } *::before,*::after, * { box-sizing: inherit } body { margin: 0 }
 <nav class="sidebar"> <div class="menu-bar"> <label class="side-text">MENU</label> </div> <h3>test</h3> <a class="anchor" href="javascript:void(0)">some '.sidebar' &lt;a&gt;</a> <ul id="menuList"> <h3>:focus version</h3> <li> <a class="menu-item1" href="javascript:void(0)">Staff</a> <ul> <li><a href="#">New Staff</a></li> <li><a href="#">View Staff</a></li> </ul> </li> <li> <a class="menu-item1" href="javascript:void(0)">Notes</a> <ul> <li><a href="#">New Note</a></li> <li><a href="#">Edit Notes</a></li> </ul> </li> <li> <a class="menu-item1" href="javascript:void(0)">Tasks</a> <ul> <li><a href="#">New Tasks</a></li> <li><a href="#">Edit Task</a></li> </ul> </li> <h3>.selected version</h3> <li> <a class="menu-item2" href="javascript:void(0)">Staff</a> <ul> <li><a href="#">New Staff</a></li> <li><a href="#">View Staff</a></li> </ul> </li> <li> <a class="menu-item2" href="javascript:void(0)">Notes</a> <ul> <li><a href="#">New Note</a></li> <li><a href="#">Edit Notes</a></li> </ul> </li> <li> <a class="menu-item2" href="javascript:void(0)">Tasks</a> <ul> <li><a href="#">New Tasks</a></li> <li><a href="#">Edit Task</a></li> </ul> </li> </ul> </nav> <div class="content"> <h3><b>Note</b></h3> <p> This demo uses two different approaches interchangeably creating a quirky behaviour, which under normal circumstances would not exist. </p> <p>To reproduce:</p> <ul> <li>select a <i>':focus version'</i> menu item first <li>then select a <i>'.selected version'</i> menu item </ul> <p> As you can see, the selected <i>':focus version'</i> loses focus and a second 'click' is needed to activate the <i>'.selected version'</i> menu item. This is because the first click event of the <i>'.selected version'</i> gets consumed by the blur event of the <i>':focus version'</i>. </p> <p>Just so you know...</p> </div>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM