简体   繁体   English

如何使用 Javascript 折叠垂直手风琴中的所有选项卡

[英]How can I collapse all tabs in a vertical accordion with Javascript

I made the following accordion, as indicated by w3school.如 w3school 所示,我制作了以下手风琴。

It works correctly, but want to collapse all panels by clicking on the same parent tab and/or outside the accordion elements (ie everywhere in the DOM except in elements .tab and .tabcontent )?它工作正常,但想通过单击相同的父选项卡和/或手风琴元素外部(即 DOM 中除元素.tab.tabcontent之外的任何地方)来折叠所有面板?

UPDATE (SEE SNIPPET) I found how to close the menu when clicking outside of it.更新(查看代码片段)我发现了如何在菜单外单击时关闭菜单。

Still I need to close (toggle?) the panel when clicking ALSO on its parent tab (eg open "London" and close it either if I click on "Paris" and "London" as well)在其父选项卡上单击 ALSO 时,我仍然需要关闭(切换?)面板(例如,如果我也单击“巴黎”和“伦敦”,则打开“伦敦”并关闭它)

 //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>

The following example features a sidebar nav that will reveal a hidden menu when the "hamburger" button is clicked.下面的示例具有侧边栏导航功能,当单击“汉堡包”按钮时,它会显示一个隐藏的菜单。 The hamburger button is a toggle which means it has two states (show sidebar/hide sidebar).汉堡包按钮是一个开关,这意味着它有两种状态(显示侧边栏/隐藏侧边栏)。 I added the "off-click" function from OP and of course added a function that makes the buttons ( button.link ) that open the sub-menus ( section.content.visible ) into toggles.我从 OP 添加了“关闭单击”function,当然还添加了一个 function,它使打开子菜单( section.content.visible )的按钮( button.link )进入切换状态。

This answer will address the problem addressed in the question, wherein the button.link s need to be toggles.这个答案将解决问题中解决的问题,其中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);

By default, event handlers always passes the Event Object (evt) Reference the element that has an #id identical to "this" [name] "this" refers to the element that's bound to the "click" event.默认情况下,事件处理程序始终传递事件 Object (evt) 引用具有与“this”[name] 相同的#id 的元素“this”指的是绑定到“click”事件的元素。 Next, iterate through the links array.接下来,遍历链接数组。 If the current button.link is the button.link that is registered to this "click" event, toggle the .visible class on the section.content associated with the current button.link then toggle the .active class on the current button.link Otherwise, remove the .visible class from the current section.content and the.active class from button.link .如果当前button.link是注册到此“单击”事件的button.link ,则在与当前button.link关联的section.content上切换.visible class,然后在当前 button.link 上切换.active button.link否则,从当前section.content中删除.visible class,从 button.link 中删除 .active button.link

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");
    }
  });
}

Since the majority of the elements are either absolutely or relatively positioned, all section.content are absolutely positioned and have an offset of -15vw which translate as:由于大多数元素是绝对定位或相对定位的,因此所有section.content都是绝对定位的,并且偏移量为 -15vw,翻译为:

Position element to the left side of relatively positioned parent element. Position 元素相对定位的父元素的左侧。 The negative value will end up outside of parent 15% of viewport from the element's original position. The initial width and opacity of element will be 0 (making it virtually non-existent).负值将在元素原始 position 的父视口 15% 之外结束。元素的初始宽度和不透明度将为 0(使其几乎不存在)。 When section.content is assigned a .visible class, it's width extends to 15vw, it's opacity increases to max (1.0).section.content被分配一个.visible class 时,它的宽度扩展到 15vw,它的不透明度增加到最大(1.0)。 It's then moved from left to right for 30% of viewport.然后它从左向右移动 30% 的视口。

Note : the transition values are set so that upon section.content.visible being returned on an animation of translateX() is cut abruptly ( transition: 0s ).注意:设置过渡值后,在translateX()的 animation 上返回的section.content.visible会被突然切断( transition: 0s )。 Contrary to the previous objective, the transition is 0.8s which will show the animation within a longer timeframe.与之前的目标相反,过渡时间为 0.8 秒,这将在更长的时间范围内显示 animation。

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

Note: the example is not responsive so the code rendered in the answer within the iframe is distorted.注意:该示例没有响应,因此在 iframe 中的答案中呈现的代码被扭曲了。 Review this answer in full page mode.以整页模式查看此答案。

 <,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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM