简体   繁体   English

如何在多个元素上使用 vanilla JS 切换显示样式

[英]How to toggle display style using vanilla JS on multiple elements

I wasn't sure how to write that question logically, I pretty much figured the togglw out, but I struggle with something else.我不知道如何从逻辑上写出这个问题,我几乎想出了togglw,但我在为别的事情苦苦挣扎。 When I click on one element and open it, I want to click on another one to open and the previously opened to close.当我单击一个元素并打开它时,我想单击另一个打开并关闭之前打开的元素。 It doesn't work, however.但是,它不起作用。 Whenever I click on another element, the opened one just closes without opening the other.每当我单击另一个元素时,打开的元素就会关闭而不打开另一个元素。 Basically, I want to close the element only when I click on that specific element.基本上,我只想在单击该特定元素时关闭该元素。 Or when I click on another one (which kinda works) but in this case also want the other to open, if that makes sense.或者当我点击另一个(有点工作)但在这种情况下也希望另一个打开,如果这有意义的话。 I am stuck at this point.我被困在这一点上。

Thanks for any help.谢谢你的帮助。

Here is my codepen: https://codepen.io/danosvk/pen/JjLEGMK这是我的代码笔: https ://codepen.io/danosvk/pen/JjLEGMK

Here is my code:这是我的代码:

 const questions = document.querySelectorAll("section"); const answers = document.querySelectorAll(".answer"); toggle = false function open() { for (let i = 0; i < answers.length; i++) { answers[i].style.display = "none"; } toggle = !toggle this.lastElementChild.style.display = toggle ? "block" : "none" } questions.forEach((question) => question.addEventListener("click", open));
 .card { width: 20.4375rem; background-color: #ffffff; border-radius: 1.4375rem; margin: auto; padding: 132px 24px 48px; text-align: center; transform: translateY(-125px); } section { display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e8e8ea; flex-wrap: wrap; } p { color: var(--main-text-color); font-size: 0.75rem; } .answer { flex-basis: 100%; text-align: left; display: none; }
 <div class="card"> <h1 class="no">faq</h1> <section> <p class="question">How many team members can i invite?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> You can invite up to 2 additional users on the Free plan. There is no limit on team members for the Premium plan. </p> </section> <section> <p class="question">What is the maximum file upload size?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> No more than 2GB. All files in your account must fit your allotted storage space. </p> </section> <section> <p class="question">How do I reset my password?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Click “Forgot password” from the login page or “Change password” from your profile page. A reset link will be emailed to you. </p> </section> <section> <p class="question">Can I cancel my subscription?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Yes! Send us a message and we'll process your request no questions asked. </p> </section> <section> <p class="question">Do you provide additional support?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Chat and email support is available 24/7. Phone lines are open during normal business hours. </p> </section>

Using toggle won't work as it is always false when an answer is showing.使用toggle不起作用,因为在显示答案时它总是false的。

Instead, save the style of the clicked option before wiping all the answers, and then use it to determine whether or not to show the answer.相反,在擦除所有答案之前保存单击选项的样式,然后使用它来确定是否显示答案。

If this.lastElementChild.style.display is '' (ie on the first time the menu is clicked), it is set to none .如果this.lastElementChild.style.display'' (即第一次单击菜单时),则将其设置为none

 const questions = document.querySelectorAll("section"); const answers = document.querySelectorAll(".answer"); function open() { currentDisplay=this.lastElementChild.style.display||'none'; for (let i = 0; i < answers.length; i++) { answers[i].style.display = "none"; } if (currentDisplay=='none') this.lastElementChild.style.display = "block" } questions.forEach((question) => question.addEventListener("click", open));
 .card { width: 20.4375rem; background-color: #ffffff; border-radius: 1.4375rem; margin: auto; padding: 132px 24px 48px; text-align: center; transform: translateY(-125px); } section { display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e8e8ea; flex-wrap: wrap; } p { color: var(--main-text-color); font-size: 0.75rem; } .answer { flex-basis: 100%; text-align: left; display: none; }
 <div class="card"> <h1 class="no">faq</h1> <section> <p class="question">How many team members can i invite?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> You can invite up to 2 additional users on the Free plan. There is no limit on team members for the Premium plan. </p> </section> <section> <p class="question">What is the maximum file upload size?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> No more than 2GB. All files in your account must fit your allotted storage space. </p> </section> <section> <p class="question">How do I reset my password?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Click “Forgot password” from the login page or “Change password” from your profile page. A reset link will be emailed to you. </p> </section> <section> <p class="question">Can I cancel my subscription?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Yes! Send us a message and we'll process your request no questions asked. </p> </section> <section> <p class="question">Do you provide additional support?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Chat and email support is available 24/7. Phone lines are open during normal business hours. </p> </section>

I would do this by using this CSS rule to show the answers rather than changing their style inline:我会通过使用这个 CSS 规则来显示答案而不是改变他们的内联样式来做到这一点:

.expanded .answer {
    display: block;
}

...and then using event delegation on the .card element that contains the FAQ to catch clicks anywhere within it; ...然后在包含常见问题解答的.card元素上使用事件委托来捕获其中任何位置的点击; if the click passed through a section , do this:如果点击通过一个section ,请执行以下操作:

  • If the section has the expanded class, remove it to un-expand the answer.如果该section具有expanded类,请将其删除以取消扩展答案。
  • If the section doesn't have the expanded class, remove it from any other section that has it, then add it to the section that was clicked.如果该section没有expanded类,请将其从具有它的任何其他部分中删除,然后将其添加到单击的section中。

Here's the JavaScript part:这是 JavaScript 部分:

const card = document.querySelector(".card");
card.addEventListener("click", (event) => {
    const section = event.target.closest("section");
    if (section) {
        if (section.classList.contains("expanded")) {
            // This is the expanded one, un-expand it
            section.classList.remove("expanded");
        } else {
            // This isn't the expanded one, un-expand the expanded
            // one (if any) and expand this one
            document.querySelector("section.expanded")?.classList.remove("expanded");
            section.classList.add("expanded");
        }
    }
});

Notice the optional chaining on the querySelector looking for an expanded section;注意querySelector上的可选链接寻找扩展部分; it's there in case there is no expanded section yet.如果还没有扩展部分,它就在那里。

Live Example:现场示例:

 const card = document.querySelector(".card"); card.addEventListener("click", (event) => { const section = event.target.closest("section"); if (section) { if (section.classList.contains("expanded")) { // This is the expanded one, un-expand it section.classList.remove("expanded"); } else { // This isn't the expanded one, un-expand the expanded // one (if any) and expand this one document.querySelector("section.expanded")?.classList.remove("expanded"); section.classList.add("expanded"); } } });
 .card { width: 20.4375rem; background-color: #ffffff; border-radius: 1.4375rem; margin: auto; padding: 132px 24px 48px; text-align: center; transform: translateY(-125px); } section { display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e8e8ea; flex-wrap: wrap; } p { color: var(--main-text-color); font-size: 0.75rem; } .answer { flex-basis: 100%; text-align: left; display: none; } .expanded .answer { display: block; }
 <div class="card"> <h1 class="no">faq</h1> <section> <p class="question">How many team members can i invite?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> You can invite up to 2 additional users on the Free plan. There is no limit on team members for the Premium plan. </p> </section> <section> <p class="question">What is the maximum file upload size?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> No more than 2GB. All files in your account must fit your allotted storage space. </p> </section> <section> <p class="question">How do I reset my password?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Click “Forgot password” from the login page or “Change password” from your profile page. A reset link will be emailed to you. </p> </section> <section> <p class="question">Can I cancel my subscription?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Yes! Send us a message and we'll process your request no questions asked. </p> </section> <section> <p class="question">Do you provide additional support?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Chat and email support is available 24/7. Phone lines are open during normal business hours. </p> </section> </div>

It might be worth adding a class to the section and using that in the selectors, in case you have other parts of the page using section , like this:可能值得向section添加一个类并在选择器中使用它,以防页面的其他部分使用section ,如下所示:

 const card = document.querySelector(".card"); card.addEventListener("click", (event) => { const section = event.target.closest(".faq"); if (section) { if (section.classList.contains("expanded")) { // This is the expanded one, un-expand it section.classList.remove("expanded"); } else { // This isn't the expanded one, un-expand the expanded // one (if any) and expand this one document.querySelector(".faq.expanded")?.classList.remove("expanded"); section.classList.add("expanded"); } } });
 .card { width: 20.4375rem; background-color: #ffffff; border-radius: 1.4375rem; margin: auto; padding: 132px 24px 48px; text-align: center; transform: translateY(-125px); } section { display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e8e8ea; flex-wrap: wrap; } p { color: var(--main-text-color); font-size: 0.75rem; } .answer { flex-basis: 100%; text-align: left; display: none; } .expanded .answer { display: block; }
 <div class="card"> <h1 class="no">faq</h1> <section class="faq"> <p class="question">How many team members can i invite?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> You can invite up to 2 additional users on the Free plan. There is no limit on team members for the Premium plan. </p> </section> <section class="faq"> <p class="question">What is the maximum file upload size?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> No more than 2GB. All files in your account must fit your allotted storage space. </p> </section> <section class="faq"> <p class="question">How do I reset my password?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Click “Forgot password” from the login page or “Change password” from your profile page. A reset link will be emailed to you. </p> </section> <section class="faq"> <p class="question">Can I cancel my subscription?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Yes! Send us a message and we'll process your request no questions asked. </p> </section> <section class="faq"> <p class="question">Do you provide additional support?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Chat and email support is available 24/7. Phone lines are open during normal business hours. </p> </section> </div>

You can also fix it by simply keeping a reference ( currentlyOpen ) of the currently opened section and using it in your event listener.您还可以通过简单地保留currentlyOpen打开的部分的引用 (currentOpen) 并在事件侦听器中使用它来修复它。

It would only require you to change some js and keep the same css.它只需要您更改一些 js 并保持相同的 css。

Here's a little bit of code to explaining the change:这里有一些代码来解释这个变化:

let currentlyOpen; // Keep track of the currently openend section.

function toggle() {
    if (currentlyOpen) {
    // Something currently open
    currentlyOpen.lastElementChild.style.display = "none" // Close it
  }
  
  if (currentlyOpen === this) {
    // The element the user clicked on is already opened, close it!
    currentlyOpen.lastElementChild.style.display = "none" // Close it
    currentlyOpen = undefined; // Delete it's refrence
    return // And return (stop execution)
  }
  
  // Show clicked element
  this.lastElementChild.style.display = "block"
  
  // Store it for later
  currentlyOpen = this
}

For more info see the code snipped:有关更多信息,请参阅代码片段:

 const questions = document.querySelectorAll("section"); const answers = document.querySelectorAll(".answer"); let currentlyOpen; function toggle() { if (currentlyOpen) { // Something currently open currentlyOpen.lastElementChild.style.display = "none" // Close it } if (currentlyOpen === this) { // Element is already open currentlyOpen.lastElementChild.style.display = "none" // Hide it currentlyOpen = undefined; // Delete it's refrence return // And return } // Show clicked element this.lastElementChild.style.display = "block" // Store it for later currentlyOpen = this } questions.forEach((question) => question.addEventListener("click", toggle));
 .card { width: 20.4375rem; background-color: #ffffff; border-radius: 1.4375rem; margin: auto; padding: 132px 24px 48px; text-align: center; transform: translateY(-125px); } section { display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e8e8ea; flex-wrap: wrap; } p { color: var(--main-text-color); font-size: 0.75rem; } .answer { flex-basis: 100%; text-align: left; display: none; }
 <div class="card"> <h1 class="no">faq</h1> <section> <p class="question">How many team members can i invite?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> You can invite up to 2 additional users on the Free plan. There is no limit on team members for the Premium plan. </p> </section> <section> <p class="question">What is the maximum file upload size?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> No more than 2GB. All files in your account must fit your allotted storage space. </p> </section> <section> <p class="question">How do I reset my password?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Click “Forgot password” from the login page or “Change password” from your profile page. A reset link will be emailed to you. </p> </section> <section> <p class="question">Can I cancel my subscription?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Yes! Send us a message and we'll process your request no questions asked. </p> </section> <section> <p class="question">Do you provide additional support?</p> <img class="arrow" src="./images/icon-arrow-down.svg" alt="" /> <p class="answer"> Chat and email support is available 24/7. Phone lines are open during normal business hours. </p> </section> </div>

Or a working version hosted on jsfiddle: https://jsfiddle.net/fmr9tvw8/或托管在 jsfiddle 上的工作版本: https ://jsfiddle.net/fmr9tvw8/

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

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