如何使用Javascript将所有选项卡折叠在一个垂直手风琴中



我制作了以下手风琴,如w3school所示。

它工作正常,但希望通过单击同一父选项卡和/或手风琴元素(即DOM中除元素.tab.tabcontent外的所有位置(来折叠所有面板?

更新(参见SNIPET(我发现了如何在点击菜单外部时关闭菜单。

当我点击它的父选项卡时,我仍然需要关闭(切换?(面板(例如,打开"伦敦",如果我点击"巴黎"one_answers"伦敦"也可以关闭它(

//function to open accordion
function openCity(evt, cityName) {
// Declare all variables
var i, tabcontent, tablinks;
// Get all elements with class="tabcontent" and hide them
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// Get all elements with class="tablinks" and remove the class "active"
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the link that opened the tab
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " active";
}
//function to open sidebar
function openSidebar(x) {
document.getElementById("sidenav").classList.toggle("sidenav-visible");
x.classList.toggle("change");
//closes all the tabs
tabcontent = document.getElementsByClassName("tabcontent");
for (itab = 0; itab < tabcontent.length; itab++) {
tabcontent[itab].style.display = "none";
}
}
//closes the menu when clicking outside 
window.addEventListener('click', function(e){
if (!document.getElementById('sidenav').contains(e.target) && (!document.getElementById('burger').contains(e.target))){
document.getElementById('sidenav').classList.remove("sidenav-visible"); 
document.getElementById('burger').classList.remove("change");
} 
})
.container {
width: 100%;
height: 500px;
background-color: grey;
}
/*Togge burger button to open sidebar menu*/
.container-burger {
position:absolute;
top:0.5em;
left: 0.5em;
z-index:450;
}
.bar1, .bar2, .bar3 {
width: 35px;
height: 5px;
background-color: #333;
margin: 6px 0;
transition: 0.4s;
}

.change .bar1 {
transform: translate(0, 11px) rotate(-45deg);
}

.change .bar2 {opacity: 0;}

.change .bar3 {
transform: translate(0, -11px) rotate(45deg);
}
.sidenav {
position: absolute;
width: 0;
height: 100%;
top:70px;
background-color: #feffff00;
z-index:40;
opacity: 0;
-moz-transition: 0.3s;
-o-transition: 0.3s;
-webkit-transition: 0.3s;
transition: 0.3s;
}
.sidenav-visible {
width: 60%;
opacity: 1;
visibility: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* Style the tab */
.tab {
float: left;
border: 1px solid #ccc;
background-color: #f1f1f1;
width: 60px;
height: 300px;
}
/* Style the buttons inside the tab */
.tab button {
display: block;
background-color: inherit;
color: black;
padding: 22px 16px;
width: 100%;
border: none;
outline: none;
text-align: left;
cursor: pointer;
transition: 0.3s;
font-size: 17px;
}
/* Change background color of buttons on hover */
.tab button:hover {
background-color: #ddd;
}
/* Create an active/current "tab button" class */
.tab button.active {
background-color: #ccc;
}
/* Style the tab content */
.tabcontent {
float: left;
background-color:#fff;
padding: 0px 12px;
border: 1px solid #ccc;
width: 200px;
border-left: none;
height: 300px;
display:none;
overflow: hidden;
overflow-y:scroll;
}
</style>
</head>
<body>

<div class="container">
<!--burger button to open sidebar menu-->
<div id="burger" class="container-burger" onclick="openSidebar(this)" title="Filters and Info">
<div class="bar1"></div>
<div class="bar2"></div>
<div class="bar3"></div>
</div><!--ends burger button-->

<div class="sidenav" id="sidenav">
<!--starts the accordion-->
<div class="tab">
<button class="tablinks" onclick="openCity(event, 'London')" id="defaultOpen">London</button>
<button class="tablinks" onclick="openCity(event, 'Paris')">Paris</button>
<button class="tablinks" onclick="openCity(event, 'Tokyo')">Tokyo</button>
</div>
<div id="London" class="tabcontent">
<h3>London</h3>
<p>London is the capital city of England.</p>
</div>
<div id="Paris" class="tabcontent">
<h3>Paris</h3>
<p>Paris is the capital of France.</p> 
</div>
<div id="Tokyo" class="tabcontent">
<h3>Tokyo</h3>
<p>Tokyo is the capital of Japan.</p>
</div>
</div>
</div>

下面的例子有一个侧边栏导航,当"汉堡包";按钮。汉堡包按钮是一个切换,这意味着它有两种状态(显示侧边栏/隐藏侧边栏(。我添加了";关闭点击";功能,当然还添加了一个功能,使打开子菜单(section.content.visible(的按钮(button.link(切换。

这个答案将解决问题中解决的问题,其中button.link需要切换。

// Collect all button.link into a NodeList and convert it into an array
const links = [...document.querySelectorAll(".link")];
// Collect all section.content into a NodeList and convert it into an array as well
const content = [...document.querySelectorAll(".content")];

/**
* For each button.link bind it to the "click" event. When triggered, 
* the event handler openSub(e) will be invoked.
*/
links.forEach(btn => btn.onclick = openSub);

默认情况下,事件处理程序始终传递事件对象(evt(引用具有与"id"相同的#id的元素;这个";[姓名]"这个";指的是绑定到";点击";事件接下来,遍历链接数组。如果当前CCD_ 6是注册到该"0"的CCD_;点击";事件,在与当前button.link关联的section.content上切换.visible类,然后在当前button.link上切换.active类。否则,从当前section.content中删除.visible类,从button.link中删除.active类。

function openSub(evt) {
let synced = document.getElementById(this.name);
links.forEach((btn, idx) => {
if (btn === evt.currentTarget) {
synced.classList.toggle("visible");
btn.classList.toggle("active");
} else {
content[idx].classList.remove("visible");
btn.classList.remove("active");
}
});
}

由于大多数元素都是绝对或相对定位的,因此所有section.content都是绝对定位的,偏移量为-15vw,翻译为:

将元素定位到相对定位的父元素的左侧。负值将在元素原始位置的父视口15%之外结束。元素的初始宽度和不透明度将为0(使其几乎不存在(。当section.content被分配一个.visible类时,它的宽度扩展到15vw,不透明度增加到最大值(1.0(。然后它从左到右移动视口的30%。

注意:设置转换值,以便在section.content.visible返回时突然剪切translateX()的动画(transition: 0s(。与之前的目标相反,转换为0.8秒,这将在更长的时间内显示动画。

.content {
position: absolute;
left: -15vw;
width: 0;
/*...*/   
opacity: 0;
transition: 0s;
}
.content.visible {
width: 15vw;
opacity: 1;
transform: translateX(30vw);
transition: 0.8s;
}

注意:示例没有响应,因此iframe中答案中呈现的代码失真。以整页模式查看此答案。

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
:root {
--shadow_out: 0px 2px 4px rgba(0, 0, 0, 0.15), 0px 4px 8px rgba(0, 0, 0, 0.15),
0px 8px 16px rgba(0, 0, 0, 0.15), 0px 16px 32px rgba(0, 0, 0, 0.15);
--shadow_btn: 0px 0px 0px 1px rgba(0, 0, 0, 0.15), 0px 4px 8px rgba(0, 0, 0, 0.15),
0px 8px 16px rgba(0, 0, 0, 0.15), 0px 16px 32px rgba(0, 0, 0, 0.15);
}
html {
font: 300 2ch/1 "Segoe UI";
scroll-behavior: smooth;
}
main {
display: flex;
flex-flow: column nowrap;
justify-content: space-around;
position: relative;
width: 100%;
min-height: 500px;
background-color: grey;
}
.burger {
position: absolute;
top: 0.5em;
left: 0.5em;
z-index: 45;
cursor: pointer;
}
.bar {
display: block;
width: 35px;
height: 5px;
margin: 6px 0;
background: #fff;
transition: 0.4s;
}
.change .b1 {
transform: translate(0, 11px) rotate(-45deg);
}
.change .b2 {
opacity: 0;
}
.change .b3 {
transform: translate(0, -11px) rotate(45deg);
}
.slide {
display: flex;
flex-flow: row nowrap;
align-items: center;
position: absolute;
top: 15%;
left: -30vw;
z-index: 46;
width: 0;
opacity: 0;
transition: 0.3s;
}
.tab {
width: 15vw;
min-height: 300px;
}
.tab button {
display: block;
padding: 1.25rem 1rem;
width: 100%;
border: none;
outline: none;
text-align: left;
font: inherit;
font-size: 1.1rem;
cursor: pointer;
box-shadow: var(--shadow_out);
}
.tab button:hover {
background-color: #ddd;
}
.tab button:active,
.tab button.active {
font-weight: 700;
background-color: #ccc;
transform: scale(1.1);
}
.content {
position: absolute;
left: -15vw;
width: 0;
min-height: 300px;
padding: 0px 0.65rem;
border: 1px solid #ccc;
border-right: none;
background: #def;
opacity: 0;
transition: 0s;
box-shadow: var(--shadow_btn);
}
.slide.visible {
width: 30vw;
opacity: 1;
transform: translateX(30vw);
}
.content.visible {
width: 15vw;
opacity: 1;
transform: translateX(30vw);
transition: 0.8s;
}
</style>
</head>
<body>
<main>
<menu class="burger" title="Capital">
<b class="bar b1"></b>
<b class="bar b2"></b>
<b class="bar b3"></b>
</menu>
<nav class="slide">
<menu class="tab">
<button name="London" class="link">London</button>
<button name="Paris" class="link">Paris</button>
<button name="Tokyo" class="link">Tokyo</button>
</menu>
<section id="London" class="content">
<h3>England</h3>
<p>London is the capital city of England.</p>
</section>
<section id="Paris" class="content">
<h3>France</h3>
<p>Paris is the capital of France.</p>
</section>
<section id="Tokyo" class="content">
<h3>Japan</h3>
<p>Tokyo is the capital of Japan.</p>
</section>
</nav>
</main>
<script>

const burger = document.querySelector('.burger');
const slide = document.querySelector('.slide');

const links = [...document.querySelectorAll(".link")];
const content = [...document.querySelectorAll(".content")];

burger.onclick = openNav;
window.onclick = exitNav;
links.forEach(btn => btn.onclick = openSub);
function openNav(evt) {
content.forEach(sec => sec.classList.remove("visible"));
slide.classList.toggle("visible");
this.classList.toggle("change");
}
function exitNav(evt) {
if (!slide.contains(evt.target) && !burger.contains(evt.target)) {
slide.classList.remove("visible");
burger.classList.remove("change");
}
}

function openSub(evt) {
let synced = document.getElementById(this.name);
links.forEach((btn, idx) => {
if (btn === evt.currentTarget) {
synced.classList.toggle("visible");
btn.classList.toggle("active");
} else {
content[idx].classList.remove("visible");
btn.classList.remove("active");
}
});
}
</script>
</body>
</html>