点击一个手风琴时隐藏其他打开/活动的手风琴

3
我怀疑问题出在以下JS代码行:const active = document.querySelector(".accordion.active"); 它似乎没有检索到该元素。请帮我调试一下,或者我是否应该使用其他方法而不是querySelector? 另外,当单击手风琴元素时,发现this.classList.add("active"); 没有将“active类”添加到手风琴元素中。

document.addEventListener("DOMContentLoaded", function(event) {
  var acc = document.getElementsByClassName("accordion");
  var i;
  for (i = 0; i < acc.length; i++) {
    acc[i].addEventListener("click", function() {
      const active = document.querySelector(".accordion.active");
      console.log(active);
      if (active) {
        active.classList.remove('active'); // remove active class from accordions
      }
      this.classList.add("active"); // add it to this one
      
      this.classList.toggle("active");
      var panel = this.nextElementSibling;
      if (panel.style.display === "block") {
        panel.style.display = "none";
      } else {
        panel.style.display = "block";
      }
    });
  }
});
.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 1.5rem;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  transition: 0.4s;
}

.active,
.accordion:hover {
  background-color: #ccc;
}

.panel {
  padding: 0 18px;
  display: none;
  background-color: white;
  overflow: hidden;
  background: red;
}

.active+.panel {
  display: block;
}
<div class="row">
  <button class="accordion"><div class="question"><?php echo $label; ?></div></button>
  <div class="panel">
    <p class="answer">
      <?php echo $answer; ?>
    </p>
    </br>
  </div>
</div>
<div class="row">
  <button class="accordion"><div class="question"><?php echo $label; ?></div></button>
  <div class="panel">
    <p class="answer">
      <?php echo $answer; ?>
    </p>
    </br>
  </div>
</div>


你的换行标签格式不正确(而且你也不应该使用它们来进行间距控制)。另外,使用控制台日志代替警告框。警告框会让所有人都疯掉。 - isherwood
请查看[提问指南],然后修改您的帖子标题,只提出一个具体的问题。 - isherwood
5个回答

2

如果你只需要基础功能,那么也许你应该重新考虑你的方法,因为在你的情况下,甚至不需要使用JavaScript!如果你需要一个自定义手风琴,那么可以使用JavaScript,我将尝试向你解释如何实现。

你需要的是一个干净的HTML代码,其中包含<details><summary>标签。看这个例子:

<details class="accordion">
  <summary>Question 1</summary>
  <strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus molestias ex, rem ducimus quibusdam nihil aliquam corporis id sint aperiam dolores, accusantium culpa adipisci similique doloremque eius reiciendis. Veniam, perferendis.
</details>
<details class="accordion">
  <summary>Question 2</summary>
  <strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum doloribus tenetur tempore esse consectetur incidunt, distinctio eaque suscipit error fugit tempora, quas accusantium recusandae autem voluptatibus qui quasi molestiae odit.
</details>

使用CSS,您可以按照自己的想法对其进行样式设置。如果您想删除箭头,则可以尝试使用details > summary { list-style: none; }。此外,您还可以使用任何其他字符。在此示例中,我们使用符号+(加号),当手风琴已经打开时,它应该是-(减号)。

details > summary {
  list-style-type: '+ ';
}
details[open] > summary {
  list-style-type: "- ";
}
details > summary::-webkit-details-marker {
  display: none;
}

summary {
  background-color: #ccc;
}
<details class="accordion">
  <summary>Question 1</summary>
  <strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus molestias ex, rem ducimus quibusdam nihil aliquam corporis id sint aperiam dolores, accusantium culpa adipisci similique doloremque eius reiciendis. Veniam, perferendis.
</details>
<details class="accordion">
  <summary>Question 2</summary>
  <strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum doloribus tenetur tempore esse consectetur incidunt, distinctio eaque suscipit error fugit tempora, quas accusantium recusandae autem voluptatibus qui quasi molestiae odit.
</details>

正如您所看到的,所有未解决的问题仍然保持未解决状态。如果您只想保留活动问题的开放状态,可以使用JavaScript。

document.querySelectorAll('details').forEach((accordion) => {
  accordion.addEventListener('click', (e) => {
    document.querySelectorAll('details').forEach((event) => {
      if (accordion !== event) {
        event.removeAttribute('open');
      }
    });
  });
});
details > summary {
  list-style-type: '+ ';
}
details[open] > summary {
  list-style-type: "- ";
}
details > summary::-webkit-details-marker {
  display: none;
}

summary {
  background-color: #ccc;
}
<details class="accordion">
  <summary>Question 1</summary>
  <strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus molestias ex, rem ducimus quibusdam nihil aliquam corporis id sint aperiam dolores, accusantium culpa adipisci similique doloremque eius reiciendis. Veniam, perferendis.
</details>
<details class="accordion">
  <summary>Question 2</summary>
  <strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum doloribus tenetur tempore esse consectetur incidunt, distinctio eaque suscipit error fugit tempora, quas accusantium recusandae autem voluptatibus qui quasi molestiae odit.
</details>


我非常喜欢你的第三个解决方案,因为我需要在单击另一个/相同的手风琴时隐藏打开的手风琴。你能在手风琴前面添加加号吗? - Pramod Gangadar
@PramodGangadar 是的,你可以只用CSS来实现这个。我已经更新了我的答案。 - Reza Saadati
Reza,非常感谢。我发现打开的手风琴在点击新的手风琴时没有关闭。这是最后一个修复。请帮忙。请访问此页面 https://dev-packed-with-purpose.pantheonsite.io/gift-concierge-new/。 - Pramod Gangadar
@PramodGangadar 在HTML加载之前调用了脚本。请将脚本放在页脚中。 - Reza Saadati

1

您不需要操作活动元素的display,因为您的CSS已经完成了这一步。另外,您不应该在this上同时添加和切换活动类 - 这相当于将其删除。

我还添加了一个if语句来检查点击的元素是否已经处于活动状态,以便再次折叠它。

document.addEventListener("DOMContentLoaded", function(event) {
  var acc = document.getElementsByClassName("accordion");
  var i;
  for (i = 0; i < acc.length; i++) {
    acc[i].addEventListener("click", function() {
      const active = document.querySelector(".accordion.active");
      console.log(active);
      if (active) {
        active.classList.remove('active'); // remove active class from accordions
      }

      if (active !== this) {
        this.classList.toggle("active");
      }
    });
  }
});
.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 1.5rem;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  transition: 0.4s;
}

.active,
.accordion:hover {
  background-color: #ccc;
}

.panel {
  padding: 0 18px;
  display: none;
  background-color: white;
  overflow: hidden;
  background: red;
}

.active+.panel {
  display: block;
}
<div class="row">
  <button class="accordion"><div class="question">Label1</div></button>
  <div class="panel">
    <p class="answer">
      Answer goes here
    </p>
  </div>
</div>
<div class="row">
  <button class="accordion"><div class="question">Label2</div></button>
  <div class="panel">
    <p class="answer">
      Second answer
    </p>
  </div>
</div>


谢谢,乔纳斯。它完美地工作了。你能帮我在每个手风琴中添加+号,以便让访问者认为它是一个手风琴吗? - Pramod Gangadar

1

我总是更喜欢委托。

我把手风琴包裹在一个DIV中,并从那里委托点击事件。

不需要显示/隐藏面板,因为CSS .active + .panel { display: block; } 已经为我们做了这个。

document.addEventListener("DOMContentLoaded", function(event) { // when page has loaded
  const acc = document.getElementById("accodionContainer"); // the container
  const buttons = acc.querySelectorAll(".accordion");       // the buttons in the container
  acc.addEventListener("click", e => { // any click in the container
    const currentButton = e.target.closest(".accordion"); // you have stuff inside the relevant element, make sure we use the .accordion element
    if (!currentButton) return // something not a (in a ) button was clicked
    // toggle clicked button, remove active from the rest
    buttons.forEach(acc =>  acc.classList[acc === currentButton ? "toggle" : "remove"]("active"));
  });
});
.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 1.5rem;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  transition: 0.4s;
}

.active,
.accordion:hover {
  background-color: #ccc;
}

.panel {
  padding: 0 18px;
  display: none;
  background-color: white;
  overflow: hidden;
}

.active+.panel {
  display: block;
}

.accordion.active span::after {
  content: "➖";
}

.accordion span::after {
  content: "➕";
}
<div id="accodionContainer">
  <div class="row">
    <button class="accordion"><span></span> Question 1</button>
    <div class="panel">
      <p class="answer">
        Answer 1
      </p>
    </div>
  </div>
  <div class="row">
    <button class="accordion"><span></span> Question 2</button>
    <div class="panel">
      <p class="answer">
        Answer 2
      </p>
    </div>
  </div>
</div>


感觉那个解释很容易就可以改进(哈哈)……我在这里看不到“委托”。--顺便提一下,我被要求检查来自另一个帖子的问题和您的答案,根据我对OP问题的理解,您的答案是最好的......显然只是缺少解释。它可能已经有一个重复的了。 - Louys Patrice Bessette

1

//

当单击手风琴元素时,this.classList.add("active");没有将“active class”添加到元素中。

//

它确实添加了。但是立即切换该类,因此它被删除。那个toggle class的行被注释了。

我已经添加了为活动手风琴设置绿色的CSS样式,在将光标移出手风琴元素后可以看到。

document.addEventListener("DOMContentLoaded", function(event) {
  var acc = document.getElementsByClassName("accordion");
  var i;
  for (i = 0; i < acc.length; i++) {
    acc[i].addEventListener("click", function() {
      const active = document.querySelector(".accordion.active");
      console.log(active);
      if (active) {
        active.classList.remove('active'); // remove active class from accordions
        // this is if other heading is clicked
      }
      
      
     // this.classList.toggle("active");  // not needed
      var panel = this.nextElementSibling;
      if (panel.style.display === "block") {
      this.classList.remove("active"); // remove it to this one
        panel.style.display = "none";
      } else {
      this.classList.add("active"); // add it to this one
        panel.style.display = "block";
      }
    });
  }
});
.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 1.5rem;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  transition: 0.4s;
}
.accordion.active{
  background:green;
}

.active,
.accordion:hover {
  background-color: #ccc;
}

.panel {
  padding: 0 18px;
  display: none;
  background-color: white;
  overflow: hidden;
  background: red;
}

.active+.panel {
  display: block;
}
<div class="row">
  <button class="accordion"><div class="question">Accordion Label 1</div></button>
  <div class="panel">
    <p class="answer">
      Accordion Answer 1
    </p>
    </br>
  </div>
</div>
<div class="row">
  <button class="accordion"><div class="question">Accordion Label 2</div></button>
  <div class="panel">
    <p class="answer">
      Accordion Answer 1
    </p>
    </br>
  </div>
</div>


0
下面的答案与问题无关,但保持HTML层次结构是很好的。

const accordionToggles = document.querySelectorAll('.accordion-toggle');
const accordionItems = document.querySelectorAll('.accordion-item');
const flush = true; // set to true,if only one accordion bodyto be open. set false, to open multiple accordion-body
accordionToggles.forEach((toggleBtn) => {
  toggleBtn.addEventListener('click', (event) => {
    const accordionItem = event.target.closest(".accordion-item");
    if (!flush) {
      accordionItem.classList.toggle('active');
      return;
    }
    if (accordionItem.classList.contains('active')) {
      accordionItem.classList.toggle('active');
      return;
    }
    accordionItems.forEach((item) => {
      item.classList.remove('active');
    })
    accordionItem.classList.add('active');
  })

});
.accordion {}

.accordion-item {
  border: 1px solid gray;
}

.accordion-item+.accordion-item {
  border-top: none;
}

.accordion-header {}

.accordion-toggle {
  width: 100%;
  border: none;
  height: 30px;
  border-bottom: 1px solid black;
  text-align: left;
  position: relative;
}

.accordion-toggle:after {
  position: absolute;
  top: 5px;
  right: 5px;
  font-size: 18px;
}

.accordion-body {
  padding: 10px;
  display: none;
}

.accordion-item.active .accordion-body {
  display: block;
}

.accordion-item .accordion-toggle:after {
  content: "+"
}

.accordion-item.active .accordion-toggle:after {
  content: "-"
}
<div class="accordion">
  <div class="accordion-item">
    <div class="accordion-header">
      <button class="accordion-toggle">Title 1</button>
    </div>
    <div class="accordion-body">
      Accordion Content 1
    </div>
  </div>
  <div class="accordion-item">
    <div class="accordion-header">
      <button class="accordion-toggle">Title 2</button>
    </div>
    <div class="accordion-body">
      Accordion Content 2
    </div>
  </div>
</div>


代码一开始很好,如果 OP 想要切换或不切换,都可以选择,但是后半部分的代码似乎非常不干净。 - mplungjan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接