Как добавить и удалить классы для нескольких дочерних элементов, вложенных в UL, одним щелчком мыши, используя Javascript? - PullRequest
1 голос
/ 02 августа 2020

Я хочу начать с извинений за такой длинный вопрос, я просто надеюсь, что в результате не усложню его понимание.

Я создал боковую панель с тремя элементами меню в UL, которые расширяются чтобы показать дочерние элементы, изменить цвет фона и убрать эффект наведения при нажатии. Я сделал это, определив функцию, которая добавляет и удаляет классы, содержащие соответствующие свойства, при щелчке по элементу меню.

Четыре определенных c вещей, которые я хочу, чтобы боковая панель выполняла, но не могу заставить ее делать следующие:

  1. только один выбранный / нажатый элемент раскрывается за раз, в то время как все остальные невыделенные элементы меню свернуты. Это означает, что если я нажимаю первый элемент, он расширяется, а когда я нажимаю второй, первый сворачивается, а тот, который я щелкнул, расширяется et c.
  2. Выбранный / нажатый элемент меняет свой цвет фона на указывает, что он выбран.
  3. Выбранный / нажатый элемент не имеет эффекта наведения на текст, в то время как невыделенные элементы имеют эффект наведения на них изменения цвета текста.
  4. Я также хочу, чтобы выбранный элемент меню иметь возможность включать и выключать расширение, не затрагивая другие элементы в UL.

Я думаю, что больше всего проблем с моим кодом у меня возникают при добавлении и удалении классов, особенно с учетом того, что Теги <a>, которые вложены в теги <li> (элементы, по которым был выполнен щелчок) - это то место, где необходимо добавить / удалить класс «зависания», а также теги <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, поэтому любые изменения в коде достижение всех четырех из вышеперечисленных целей будет высоко оценено. спасибо

Ответы [ 2 ]

0 голосов
/ 07 августа 2020

В качестве альтернативы принятому ответу, здесь два разных подхода, над которыми я работал ...

#menuList li - это контейнер для списка пунктов меню <a>, которые имеют смежные <ul> суб- Пункты меню. Для облегчения выбора с помощью CSS я назначил класс .menu-item этим <a>.

CSS logi c для обеих версий по существу одинаковы:

  • установить .menu-item смежные ul элементы подменю, скрытые по умолчанию
  • определить :hover цвета
  • определить цвета для выбранного .menu-item и смежных ul (либо :focus, либо .selected истинно)
  • make .menu-item смежные ul элементы подменю, видимые при выборе .menu-item (то же самое)

Разница: для CSS только мы используем селектор :focus, для CSS с Javascript мы используем класс .selected.

CSS только (автоматически c фокус и размытие )

<a> получает фокус при нажатии (например, кнопка, ввод и т. д. c . :focus это истина ). Когда пользователь щелкает / касается вне выделенного элемента, он снова автоматически теряет фокус (получает размытие и :focus равно false , как в : not (: focus) = ' размытие '). Мы можем использовать селектор CSS :focus для обработки пользовательских кликов и изменения элементов, MDN: ': focus' .

CSS с Javascript ( focus и blur по запросу)

OP хочет, чтобы выбранный .menu-item и прилегающие к нему ul подменю оставались видимыми до тех пор, пока пользователь специально отменяет выбор снова. Это невозможно сделать с помощью селектора :focus, поэтому мы игнорируем этот селектор и используем вместо него класс .selected для обработки требований focus и blur , MDN: HTMLElement. blur () .

Javascript logi c довольно прост:

Прикрепите «щелчок» - eventListener ( MDN: Элемент: событие щелчка ) в основной контейнер #menuList обработка:

  • когда выбирается .menu-item, и это в настоящее время .selected, затем размытие it (menuItemBlur())
  • иначе
    • когда у нас есть ранее выбранный .menu-item open, blur тот первый (menuItemBlur())
    • а затем фокус вновь выбранный .menu-item (menuItemFocus())

Изменения в коде OP

  • удалены ненужные CSS
  • удалены ненужные class атрибуты из HTML
  • изменено href="#" в <#menuList li a> на 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>
0 голосов
/ 02 августа 2020

Простой способ сделать это:

Если элемент был выбран, просто отмените его выбор

Если это не так, снимите выделение со всех элементов и выберите элемент, по которому щелкнули

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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...