Collapsible headings/accordion from markup exported html

守給你的承諾、 提交于 2020-01-16 19:35:49

问题


I'm trying to achieve a webpage with each h2 acting as items of an accordion. When clicked, the contents under the heading should expand (after being hidden).

I've been through a bunch of tutorials, but they usually use specific html (using links or buttons) but my html is exported from a markup language (orgmode) so can't be customised this way (at least I'd prefer not to). I haven't been able to modify any of them to suit my needs.

I'm not sure if the best solution would use css or javascript, or both. Here is the html body:

  <div id="content">
    <h1 class="title">Recipes</h1>

    <div id="outline-container-org233deb6" class="outline-2">
      <h2 id="org233deb6">Tea</h2>
      <div class="outline-text-2" id="text-org233deb6">
      </div>
      <div id="outline-container-orgfbba0f7" class="outline-3">
    <h3 id="orgfbba0f7">Ingredients</h3>
    <div class="outline-text-3" id="text-orgfbba0f7">
      <ul class="org-ul">
        <li>1 tbsp tea leaves</li>
        <li>1 cup water</li>
      </ul>
    </div>
      </div>
      <div id="outline-container-orgcc98fa7" class="outline-3">
    <h3 id="orgcc98fa7">Instructions</h3>
    <div class="outline-text-3" id="text-orgcc98fa7">
      <ol class="org-ol">
        <li>Boil some water</li>
        <li>Put in tea leaves</li>
        <li>Strain</li>
      </ol>
    </div>
      </div>
    </div>
  </div>

From what I can tell, I'm trying to hide/show the outline-3 class upon clicking the h2 in outline-2.


回答1:


Since you can't control the markup, this is how you can solve this:

const headerH2 = document.querySelectorAll('#content h2'); // first we put all wanted headers on variable
const outlinContent =  document.querySelectorAll("div[class^=outline]"); // you can use regex to catch all div begining with 'outline-'
outlinContent.forEach(function(item){  if (item.id !== 'outline-container-org233deb6') { item.classList.add('hide'); }  }); // hide  them, skipping the one contain the trigger
headerH2[0].addEventListener('click', function() { // adding click listener to the first (or the only one in this example
  outlinContent.forEach(function(item){  if (item.id !== 'outline-container-org233deb6') {item.classList.toggle('hide'); } }); // toggle content display
});
.hide {display: none;}
<div id="content">
  <h1 class="title">Recipes</h1>
  <div id="outline-container-org233deb6" class="outline-2">
    <h2 id="org233deb6">Tea</h2>
    <div class="outline-text-2" id="text-org233deb6">CONTENT</div>
    <div id="outline-container-orgfbba0f7" class="outline-3">
      <h3 id="orgfbba0f7">Ingredients</h3>
      <div class="outline-text-3" id="text-orgfbba0f7">
        <ul class="org-ul">
          <li>1 tbsp tea leaves</li>
          <li>1 cup water</li>
        </ul>
      </div>
    </div>
    <div id="outline-container-orgcc98fa7" class="outline-3">
      <h3 id="orgcc98fa7">Instructions</h3>
      <div class="outline-text-3" id="text-orgcc98fa7">
        <ol class="org-ol">
          <li>Boil some water</li>
          <li>Put in tea leaves</li>
          <li>Strain</li>
        </ol>
      </div>
    </div>
  </div>
</div>

Making content collapsible:

Note: in this example i use h2 for the trigger and sibling div to hold the content.

const headerH2 = document.querySelectorAll('#content h2'); // first we put all wanted headers on variable
for (let i=0; i < headerH2.length; i++) { // you can use the modarn forEach, but this is much faster
  let nextDiv = headerH2[i].nextElementSibling; // this is how you select the next element after the header
  nextDiv.classList.add('hide'); // we hide all of them by default
  headerH2[i].addEventListener('click', function() { // now we are adding click listener:
  if (document.querySelector('#content div:not(.hide)')) {document.querySelector('#content div:not(.hide)').classList.add('hide')}; // this will close other open content divs if they exist
  nextDiv.classList.remove('hide'); // this will show the right content when click
  });
}
.hide {display: none;}
<div id="content">
  <h2>TITLE 1</h2>
  <div>CONTENT 1</div>
  <h2>TITLE 2</h2>
  <div>CONTENT 2</div>
  <h2>TITLE 3</h2>
  <div>CONTENT 3</div>
  <h2>TITLE 4</h2>
  <div>CONTENT 4</div>
  <h2>TITLE 5</h2>
  <div>CONTENT 5</div>
  <h2>TITLE 6</h2>
  <div>CONTENT 6</div>
</div>

For a side note, html5 has the [details] element that create a disclosure widget in which information is visible only when the widget is toggled into an "open" state, and should be using here in order to get the semantic and SEO etc...

Some helpful MDN links where you can learn much more:

querySelectorAll

nextElementSibling

:not selector

Hope that solved your problem and help others with similar question. Enjoy Code!



来源:https://stackoverflow.com/questions/59440201/collapsible-headings-accordion-from-markup-exported-html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!