Javascript & CSS - 打开菜单的过渡有问题
Javascript & CSS - opening menu has glitchy transition
我有一些代码在菜单相互展开时最初会出现故障。
如果您 select 在第一个菜单中选择第二个选项,则会出现第二个选项。如果您 然后 去打开第一个菜单,您会看到它在打开时出现故障 - 几乎有类似快门的延迟。也许这与 z 索引设置有关,我不确定?
在 firefox 和 chrome 中,没有(明显的)故障。在 Safari 中,下面的代码片段存在一个小故障。
为什么会出现故障?
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>
使用 CSS 在 Safari 浏览器中设置 z-index 会出现延迟。
用 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>
根据您的代码片段,我认为伪元素中的 transform
和大多数元素中的 transition: all 0.4s;
有一定关系:
.selected.open::after {
transform: rotateX(180deg);
top: -11px;
}
我有两种选择来解决这个问题:
选项 #1:为每个 .filter-filterbox-row
元素添加 z-index。
.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'.
}
将 position: relative;
添加到 class .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;
如果没有明确说明,就是文档默认的正常流向。由于您已经在 child div:
上声明了 z-index: 99
.select-box .options-container.open {
border-color: #253e5c;
display: block;
z-index: 99;
}
...说明 z-index 假设 div 处于绝对位置,因此我们需要声明 parent div 的位置,即 .filter-filterbox-row
。此外,如果您需要声明 top
、bottom
、left
或 [=21],则需要在 child div 上添加 position: absolute;
=]属性。
为 parent div 声明一个明确的位置是防止不同的浏览器行为故障的一种方法,例如这样。当我们在 child div 上声明一个 z-index 并将位置声明为相对位置时,我们总是做同样的做法,也应该声明 parent div如果 child div 的相对或绝对位置相应地表现。
如果您改用嵌套 <details>
和 <summary>
会有什么不同吗?
我有一些代码在菜单相互展开时最初会出现故障。
如果您 select 在第一个菜单中选择第二个选项,则会出现第二个选项。如果您 然后 去打开第一个菜单,您会看到它在打开时出现故障 - 几乎有类似快门的延迟。也许这与 z 索引设置有关,我不确定?
在 firefox 和 chrome 中,没有(明显的)故障。在 Safari 中,下面的代码片段存在一个小故障。
为什么会出现故障?
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>
使用 CSS 在 Safari 浏览器中设置 z-index 会出现延迟。
用 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>
根据您的代码片段,我认为伪元素中的 transform
和大多数元素中的 transition: all 0.4s;
有一定关系:
.selected.open::after {
transform: rotateX(180deg);
top: -11px;
}
我有两种选择来解决这个问题:
选项 #1:为每个 .filter-filterbox-row
元素添加 z-index。
.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'.
}
将 position: relative;
添加到 class .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;
如果没有明确说明,就是文档默认的正常流向。由于您已经在 child div:
z-index: 99
.select-box .options-container.open {
border-color: #253e5c;
display: block;
z-index: 99;
}
...说明 z-index 假设 div 处于绝对位置,因此我们需要声明 parent div 的位置,即 .filter-filterbox-row
。此外,如果您需要声明 top
、bottom
、left
或 [=21],则需要在 child div 上添加 position: absolute;
=]属性。
为 parent div 声明一个明确的位置是防止不同的浏览器行为故障的一种方法,例如这样。当我们在 child div 上声明一个 z-index 并将位置声明为相对位置时,我们总是做同样的做法,也应该声明 parent div如果 child div 的相对或绝对位置相应地表现。
如果您改用嵌套 <details>
和 <summary>
会有什么不同吗?