Javascript & CSS - открывающееся меню имеет сбойный переход - PullRequest
6 голосов
/ 09 апреля 2020

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

Если вы выберете второй вариант в первом меню, появится второй вариант. Если вы , а затем go откроете первое меню, вы увидите, что оно глючит при его открытии - почти как задержка затвора. Может быть, это как-то связано с настройкой индекса z, я не уверен?

В firefox и chrome нет (явного) сбоя. В сафари, фрагмент ниже, есть глюк.

Почему глючит?

const selected = document.querySelectorAll(".selected");
const optionsContainer = document.querySelectorAll(".options-container");

for (let i = 0; i < selected.length; i++) {
  selected[i].addEventListener("click", () => {
    optionsContainer[i].classList.toggle("open");
    selected[i].classList.toggle("open");

    for (let j = 0; j < selected.length; j++) {

      if (i != j && selected[j].classList.contains("open")) {
        optionsContainer[j].classList.toggle("open");
        selected[j].classList.toggle("open");
      }
    }
  });
}

for (let i = 0; i < optionsContainer.length; i++) {
  let optionsList = optionsContainer[i].querySelectorAll(".options");

  for (let j = 0; j < optionsList.length; j++) {
    optionsList.forEach(o => {
      o.addEventListener("click", () => {
        selected[i].innerHTML = o.querySelector("label").innerHTML;
        optionsContainer[i].classList.remove("open");
        selected[i].classList.remove("open");
        if (document.getElementById("level").innerText.indexOf('one') === -1) {
          document.getElementById("tier").style.display = "grid";
        } else {
          document.getElementById("tier").style.display = "none";
        }
      });
    });
  }
}
.filter-filterbox-row {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-areas: "question select-box";
  padding: 5px 5px;
  margin-top: -2px;
}

.filter-filterbox-row .question {
  grid-template-areas: "question";
  width: 100px;
  padding-top: 5px;
  padding-bottom: 10px;
  padding-left: 15px;
  text-align: right;
}

.filter-filterbox-row .select-box {
  margin-left: -100px;
  grid-template-areas: "select-box";
}

.select-box {
  width: 200px;
}

.select-box .options-container {
  background: #fff;
  width: 200px;
  display: none;
  transition: all 0.4s;
  position: absolute;
  max-height: 240px;
  border: 1px solid #253e5c;
  border-radius: 0 0 4.5px 4.5px;
}

.selected {
  position: relative;
  border: 1px solid #cdcccc;
  border-radius: 4.5px;
  padding: 4px 15px;
  cursor: pointer;
  transition: all 0.4s;
}

.selected.open {
  border-bottom: none;
  border-radius: 4.5px 4.5px 0 0;
  transition: all 0.4s;
  border-color: #253e5c
}

.selected::after {
  content: "";
  background: url(media/dropdown-black.png);
  background-size: contain;
  background-repeat: no-repeat;
  position: absolute;
  height: 100%;
  width: 12px;
  right: 13px;
  top: 10px;
  transition: all 0.4s;
}

.selected.open::after {
  transform: rotateX(180deg);
  top: -11px;
}

.select-box .options-container.open {
  border-color: #253e5c;
  display: block;
  z-index: 99;
}

.select-box .options-container.open .options:nth-child(n+2) {
  border-top: 1px solid #253e5c;
}

.select-box .options-container .options {
  padding-top: 5px;
  padding-bottom: 5px;
  padding-left: 15px;
}

.select-box .options:hover {
  background: #76bc6b;
  cursor: pointer;
}

.select-box label {
  cursor: pointer;
}

.select-box .options .radio {
  display: none;
}

#tier {
  display: none;
}
<div class="filter-filterbox-row" id="level">
  <div class="question"> Level </div>
  <div class="select-box">
    <div class="selected">
      Select level
    </div>

    <div class="options-container">

      <div class="options">
        <input type="radio" class="radio" id="1" name="level">
        <label for="1">one</label>
      </div>

      <div class="options">
        <input type="radio" class="radio" id="2" name="level">
        <label for="2">two</label>
      </div>
    </div>
  </div>
</div>

<div class="filter-filterbox-row" id="tier">
  <div class="question"> Select tier </div>
  <div class="select-box">
    <div class="selected">
      Select tier
    </div>

    <div class="options-container">

      <div class="options">
        <input type="radio" class="radio" id="bronze" name="tier">
        <label for="bronze">Bronze</label>
      </div>

      <div class="options">
        <input type="radio" class="radio" id="silver" name="tier">
        <label for="silver">silver</label>
      </div>
    </div>
  </div>
</div>

Ответы [ 4 ]

1 голос
/ 17 апреля 2020

Исходя из вашего фрагмента, я думаю, что-то связано с transform внутри псевдоэлемента и transition: all 0.4s; на большинстве элементов:

.selected.open::after {
  transform: rotateX(180deg);
  top: -11px;
}

У меня есть два варианта, чтобы это исправить:

Опция # 1: Добавить z-индекс к каждому из .filter-filterbox-row элементов.

.filter-filterbox-row {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-areas: "question select-box";
  padding: 5px 5px;
  margin-top: -2px;
  position: relative;  // NEW: Relative position.
  z-index: 0;  // NEW: Default z-index value.
}
// NEW: Set higher z-index for parent dropdown.
.filter-filterbox-row#level {
  z-index: 1;
}

Опция # 2: изменить значение преобразования в .selected::after.

.selected::after {
  content: "";
  background: url(media/dropdown-black.png);
  background-size: contain;
  background-repeat: no-repeat;
  position: absolute;
  height: 100%;
  width: 12px;
  right: 13px;
  top: 10px;
  transition: transform 0.4s;  // UPDATE: Change 'all' to 'transform'.
}
1 голос
/ 17 апреля 2020

Добавление position: relative; к классу .filter-filterbox-row немедленно исправит это для Safari:

.filter-filterbox-row {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-areas: "question select-box";
  padding: 5px 5px;
  margin-top: -2px;
  position: relative;
}

position: static; - это стандартный поток документов по умолчанию, если не указано иное. Поскольку вы объявили z-index: 99 для дочернего элемента div:

.select-box .options-container.open {
  border-color: #253e5c;
  display: block;
  z-index: 99;
}

..., заявив, что z-index предполагает, что div находится в абсолютной позиции, таким образом, мы должны объявить позицию для родительского элемента div который является .filter-filterbox-row. Кроме того, добавление position: absolute; к дочернему элементу div требуется, если вам необходимо указать свойства top, bottom, left или right.

Объявление явной позиции для родительского элемента div это один из способов предотвращения различных сбоев поведения браузера, таких как этот. То же самое мы делаем всегда, когда объявляем z-индекс дочернего элемента div и указываем положение как относительное, родительский элемент div также должен быть объявлен вместе с позицией относительного или абсолютного поведения дочернего элемента div соответственно.

1 голос
/ 17 апреля 2020

Задержка установки z-index в браузере Safari при выполнении с помощью CSS.

Установка с помощью JavaScript устраняет проблему:

document.getElementsByClassName('select-box')[0].style.zIndex = 99;

Фрагмент:

const selected = document.querySelectorAll(".selected");
const optionsContainer = document.querySelectorAll(".options-container");

for (let i = 0; i < selected.length; i++) {
  selected[i].addEventListener("click", () => {
    optionsContainer[i].classList.toggle("open");
    selected[i].classList.toggle("open");
    document.getElementsByClassName('select-box')[0].style.zIndex = 99;
    for (let j = 0; j < selected.length; j++) {
      if (i != j && selected[j].classList.contains("open")) {
        optionsContainer[j].classList.toggle("open");
        selected[j].classList.toggle("open");
      }
    }
  });
}

for (let i = 0; i < optionsContainer.length; i++) {
  let optionsList = optionsContainer[i].querySelectorAll(".options");

  for (let j = 0; j < optionsList.length; j++) {
    optionsList.forEach(o => {
      o.addEventListener("click", () => {
        selected[i].innerHTML = o.querySelector("label").innerHTML;
        optionsContainer[i].classList.remove("open");
        selected[i].classList.remove("open");
        if (document.getElementById("level").innerText.indexOf('one') === -1) {
          document.getElementById("tier").style.display = "grid";
        } else {
          document.getElementById("tier").style.display = "none";
        }
      });
    });
  }
}
.filter-filterbox-row {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-areas: "question select-box";
  padding: 5px 5px;
  margin-top: -2px;
}

.filter-filterbox-row .question {
  grid-template-areas: "question";
  width: 100px;
  padding-top: 5px;
  padding-bottom: 10px;
  padding-left: 15px;
  text-align: right;
}

.filter-filterbox-row .select-box {
  margin-left: -100px;
  grid-template-areas: "select-box";
}

.select-box {
  width: 200px;
}

.select-box .options-container {
  background: #fff;
  width: 200px;
  display: none;
  transition: all 0.4s;
  position: absolute;
  max-height: 240px;
  border: 1px solid #253e5c;
  border-radius: 0 0 4.5px 4.5px;
}

.selected {
  position: relative;
  border: 1px solid #cdcccc;
  border-radius: 4.5px;
  padding: 4px 15px;
  cursor: pointer;
  transition: all 0.4s;
}

.selected.open {
  border-bottom: none;
  border-radius: 4.5px 4.5px 0 0;
  transition: all 0.4s;
  border-color: #253e5c
}

.selected::after {
  content: "";
  background: url(media/dropdown-black.png);
  background-size: contain;
  background-repeat: no-repeat;
  position: absolute;
  height: 100%;
  width: 12px;
  right: 13px;
  top: 10px;
  transition: all 0.4s;
}

.selected.open::after {
  transform: rotateX(180deg);
  top: -11px;
}

.select-box .options-container.open {
  border-color: #253e5c;
  display: block;
  z-index: 99;
}

.select-box .options-container.open .options:nth-child(n+2) {
  border-top: 1px solid #253e5c;
}

.select-box .options-container .options {
  padding-top: 5px;
  padding-bottom: 5px;
  padding-left: 15px;
}

.select-box .options:hover {
  background: #76bc6b;
  cursor: pointer;
}

.select-box label {
  cursor: pointer;
}

.select-box .options .radio {
  display: none;
}

#tier {
  display: none;
}
<div class="filter-filterbox-row" id="level">
  <div class="question"> Level </div>
  <div class="select-box">
    <div class="selected">
      Select level
    </div>

    <div class="options-container">

      <div class="options">
        <input type="radio" class="radio" id="1" name="level">
        <label for="1">one</label>
      </div>

      <div class="options">
        <input type="radio" class="radio" id="2" name="level">
        <label for="2">two</label>
      </div>
    </div>
  </div>
</div>

<div class="filter-filterbox-row" id="tier">
  <div class="question"> Select tier </div>
  <div class="select-box">
    <div class="selected">
      Select tier
    </div>

    <div class="options-container">

      <div class="options">
        <input type="radio" class="radio" id="bronze" name="tier">
        <label for="bronze">Bronze</label>
      </div>

      <div class="options">
        <input type="radio" class="radio" id="silver" name="tier">
        <label for="silver">silver</label>
      </div>
    </div>
  </div>
</div>
0 голосов
/ 18 апреля 2020

Имеет ли значение, если вместо этого вы используете вложенные <details> и <summary>?

https://css-tricks.com/quick-reminder-that-details-summary-is-the-easiest-way-ever-to-make-an-accordion/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...