Кнопка раскрывающегося списка в HTML/CSS/JS

У меня есть код:

<link rel = "preconnect" href = "https://fonts.googleapis.com">
<link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin>
<link href = "https://fonts.googleapis.com/css2?family=PT+Sans+Narrow:wght@400;700&display=swap" rel = "stylesheet">

<style>
  .wrapper {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-gap: 10px;
  }
  
  .tilecontainer {
    padding: 5px;
    font-size: 25px;
    width: 100%;
  }
  
  @media screen and (max-width: 768px) {
    .tilecontainer {
      font-size: 16px;
    }
  }
  
  .box {
    background-color: #0051a5;
    padding-right: 1em;
    padding-top: 1em;
    border-radius: 1em;
    display: grid;
    grid-template-rows: 1fr auto;
    grid-template-columns: auto 1em;
    grid-template-areas: "sample plus" "extratext extratext";
  }
  
  .plus {
    grid-area: plus;
    background: linear-gradient(#0051a5 0 0), linear-gradient(#0051a5 0 0), #fff;
    background-position: center;
    background-size: 60% 2.5px, 2.5px 60%;
    background-repeat: no-repeat;
    transition: transform 0.3s ease;
  }
  
  .sign {
    border-radius: 50%;
    width: 1em;
    height: 1em;
    margin-right: 1em;
  }
  
  .tilelabel {
    grid-area: sample;
    font-family: 'PT Sans Narrow', sans-serif;
    font-size: 1em;
    text-transform: uppercase;
    cursor: pointer;
    font-weight: 600;
    color: #fff;
    padding-left: 1em;
    height: 2em;
  }
  
  .tileaccent {
    color: #FFC72C;
  }
  
  .hidden-text {
    grid-area: extratext;
    display: none;
    font-family: 'PT Sans Narrow', sans-serif;
    font-size: 0.75em;
    background-color: #F5F5F4;
    color: #000;
    margin: 1em;
    padding-top: 0.5em;
    padding-left: 1em;
    border-radius: 1em;
  }
  
  .expanded>.hidden-text {
    display: block;
    animation: fade-in 1s;
  }
  
  @keyframes fade-in {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
  
  @keyframes fade-out {
    from {
      opacity: 1;
    }
    to {
      opacity: 0;
    }
  }
  
  .hidden-text ul li ul {
    display: none;
  }
  
  .hidden-text ul li.active ul {
    display: block;
  }
  
  li .plus {
    transform: rotate(0);
  }
  
  li.active .plus {
    transform: rotate(45deg);
  }
  /* Changes the cursor to a pointer for the list items that have a child ul */
  
  .hidden-text ul li:has(ul) .plus {
    float: right;
  }
  
  .hidden-text ul li:has(ul) {
    list-style: none;
    /* Remove the default bullet point */
  }
  
  .hidden-text ul li:has(ul) {
    cursor: pointer;
  }
  
  .hidden-text ul {
    list-style: none;
    /* Remove the default bullet point for ul elements */
  }
  
  .hidden-text ul li {
    list-style: none;
  }
</style>

<div class = "wrapper">
  <div class = "tilecontainer">
    <div class = "box">
      <div class = "plus sign"></div>
      <div class = "tilelabel">Sample<span class = "tileaccent"> Text</span></div>
      <div class = "hidden-text">
        <ul>
          <li>Sample
            <div class = "plus sign"></div>
            <ul>
              <li>Sample
                <div class = "plus sign"></div>
                <ul>
                  <a href = "">Text</a>
                </ul>
                <ul>
                  <a href = "">Text</a>
                </ul>
                <ul>
                  <a href = "">Text</a>
                </ul>
                <ul>
                  <a href = "">Text</a>
                </ul>
                <ul>
                  <a href = "">Text</a>
                </ul>
              </li>
            </ul>
        </ul>
        </li>
      </div>
    </div>
  </div>

  <script>
    const tileLabels = document.querySelectorAll('.box .tilelabel');

    tileLabels.forEach((label) => {
      label.addEventListener('click', function() {
        const parent = this.closest('.box'); // Find the closest parent with class 'box'
        parent.classList.toggle('expanded');

        const plus = parent.querySelector('.plus');
        plus.style.transform = parent.classList.contains('expanded') ? 'rotate(45deg)' : 'rotate(0)';
      });
    });

    const toggleChild = (e) => {
      if (e.currentTarget == e.target) {
        const el = e.target;
        el.classList.toggle('active');
      }
    };

    const level2 = document.querySelectorAll('.hidden-text ul li:has(ul)');

    level2.forEach((li) => li.addEventListener('click', toggleChild));
  </script>

в этом коде, когда я нажимаю «Образец», внутренний раскрывающийся список автоматически расширяется, но как я могу сделать так, чтобы внутренние раскрывающиеся списки не раскрывались автоматически? Есть ли способ изменить код JS, чтобы справиться с этим?

Внутренняя кнопка раскрывающегося списка также, похоже, не работает. Внутренний раскрывающийся список также контролируется основным раскрывающимся меню? Как я могу разделить их обоих? Одним из вариантов может быть дублирование класса, но это будет слишком утомительно и неэффективно, если я хочу иметь несколько внутренних раскрывающихся меню.

небольшое предупреждение, :has() поддерживается не во всех браузерах (пока)

Jaromanda X 29.09.2023 03:53
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
150
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

Причина дочернего ul открывается, потому что вы выбрали все ul внутри .active класса, которые нам не нужны.

Вместо этого выберите только дочерний элемент .active, используя >.

 .hidden-text ul li.active > ul { /* magic is here */
     display: block;
 }
 li.active > .plus { /* here also add > */
    transform: rotate(45deg);
  }

Узнать больше о >

const tileLabels = document.querySelectorAll('.box .tilelabel');

    tileLabels.forEach((label) => {
      label.addEventListener('click', function() {
        const parent = this.closest('.box'); // Find the closest parent with class 'box'
        parent.classList.toggle('expanded');

        const plus = parent.querySelector('.plus');
        plus.style.transform = parent.classList.contains('expanded') ? 'rotate(45deg)' : 'rotate(0)';
      });
    });

    const toggleChild = (e) => {
      if (e.currentTarget == e.target) {
        const el = e.target;
        el.classList.toggle('active');
      }
    };

    const level2 = document.querySelectorAll('.hidden-text ul li:has(ul)');

    level2.forEach((li) => li.addEventListener('click', toggleChild));
.wrapper {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-gap: 10px;
  }
  
  .tilecontainer {
    padding: 5px;
    font-size: 25px;
    width: 100%;
  }
  
  @media screen and (max-width: 768px) {
    .tilecontainer {
      font-size: 16px;
    }
  }
  
  .box {
    background-color: #0051a5;
    padding-right: 1em;
    padding-top: 1em;
    border-radius: 1em;
    display: grid;
    grid-template-rows: 1fr auto;
    grid-template-columns: auto 1em;
    grid-template-areas: "sample plus" "extratext extratext";
  }
  
  .plus {
    grid-area: plus;
    background: linear-gradient(#0051a5 0 0), linear-gradient(#0051a5 0 0), #fff;
    background-position: center;
    background-size: 60% 2.5px, 2.5px 60%;
    background-repeat: no-repeat;
    transition: transform 0.3s ease;
  }
  
  .sign {
    border-radius: 50%;
    width: 1em;
    height: 1em;
    margin-right: 1em;
  }
  
  .tilelabel {
    grid-area: sample;
    font-family: 'PT Sans Narrow', sans-serif;
    font-size: 1em;
    text-transform: uppercase;
    cursor: pointer;
    font-weight: 600;
    color: #fff;
    padding-left: 1em;
    height: 2em;
  }
  
  .tileaccent {
    color: #FFC72C;
  }
  
  .hidden-text {
    grid-area: extratext;
    display: none;
    font-family: 'PT Sans Narrow', sans-serif;
    font-size: 0.75em;
    background-color: #F5F5F4;
    color: #000;
    margin: 1em;
    padding-top: 0.5em;
    padding-left: 1em;
    border-radius: 1em;
  }
  
  .expanded>.hidden-text {
    display: block;
    animation: fade-in 1s;
  }
  
  @keyframes fade-in {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
  
  @keyframes fade-out {
    from {
      opacity: 1;
    }
    to {
      opacity: 0;
    }
  }
  
  .hidden-text ul li ul {
    display: none;
  }
  
  .hidden-text ul li.active > ul { /* magic is here */
    display: block;
  }
  
  li .plus {
    transform: rotate(0);
  }
  
  li.active > .plus { /* here also add > */
    transform: rotate(45deg);
  }
  /* Changes the cursor to a pointer for the list items that have a child ul */
  
  .hidden-text ul li:has(ul) .plus {
    float: right;
  }
  
  /* .hidden-text ul li:has(ul) {
    list-style: none;
  }  does not required for li , ul style is enough */  
  
  .hidden-text ul li:has(ul) {
    cursor: pointer;
  }
  
  .hidden-text ul {
    list-style: none;
    /* Remove the default bullet point for ul elements does not required for li */
  }
  
 /* .hidden-text ul li {
    list-style: none;
  } */
<link rel = "preconnect" href = "https://fonts.googleapis.com">
<link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin>
<link href = "https://fonts.googleapis.com/css2?family=PT+Sans+Narrow:wght@400;700&display=swap" rel = "stylesheet">


<div class = "wrapper">
  <div class = "tilecontainer">
    <div class = "box">
      <div class = "plus sign"></div>
      <div class = "tilelabel">Sample<span class = "tileaccent"> Text</span></div>
      <div class = "hidden-text">
        <ul>
          <li>Sample
            <div class = "plus sign"></div>
            <ul>
              <li>Sample
                <div class = "plus sign"></div>
                <ul>
                  <a href = "">Text</a>
                </ul>
                <ul>
                  <a href = "">Text</a>
                </ul>
                <ul>
                  <a href = "">Text</a>
                </ul>
                <ul>
                  <a href = "">Text</a>
                </ul>
                <ul>
                  <a href = "">Text</a>
                </ul>
              </li>
            </ul>
           </li>
        </ul>
      </div>
    </div>
  </div>

он не выполняет анимацию поворота на кнопке раскрывающегося списка для внутреннего раскрывающегося списка

s.ha11 29.09.2023 14:09

Есть ли способ заставить внутреннюю кнопку раскрывающегося списка вращаться так же, как и внешние?

s.ha11 29.09.2023 18:02

Я обновил код, теперь он вращается

Yuvaraj M 30.09.2023 05:52

Другие вопросы по теме