Home > Blockchain >  how to hide expandend item on accordion
how to hide expandend item on accordion

Time:10-27

How to hide opened accordion item by clicking on a <summary>? Currently i can only open an item by clicking on a <details> (visually on <summary> because its only visible until opened), but when clicking again to make an item hidden it does not work and i have to click an another item to close the previous one (which is what i want but i also want to be able to hide currently opened item by clicking on it once again).

There is an exception when i click on opened accordion item's description (<p>) and it gets closed. But i want this closing behaviour on a <summary>.

I want to achieve something like this: https://codepen.io/joekolade/pen/RwymroQ

  • button toggling opens and hides element
  • opening next accordion item closes previous one - done

  const details = document.querySelectorAll("details");

  details.forEach((accordion) => {
    accordion.addEventListener("click", function () {
      details.forEach((elem) => {
        elem.removeAttribute("open");
        elem.querySelector("span").classList.remove("active");
      });
      this.querySelector("span").classList.add("active");
    });
  });
summary{ 
  list-style: none 
}

details{
  padding: 1rem;
  background-color: lightblue;

 }

details summary{
  display: flex;
  justify-content: space-between;
}

details:nth-child(even){
  background-color: lightgreen;
}

p{
  padding-top: 0.3rem;
}

.active{
  transform: rotate(180deg);
}

span{
display: block;
}
<div>
  <details>
    <summary>    
      <h3>item1</h3> 
      <span>arrow</span> 
    </summary>
    <p>desc1</p>
  </details>
  <details>
    <summary>
      <h3>item2</h3> 
      <span>arrow</span> 
    </summary> 
    <p>desc2</p>
  </details>
  <details>
    <summary> 
      <h3>item3</h3> 
      <span>arrow</span> 
    </summary>
    <p>desc3</p>
  </details>
</div>

CodePudding user response:

I think it will be better to write custom accordion - a lot of examples in google or ready-made solutions. But if you want use <details /> I made a sketch based on your code, guess this's the behavior you expect:

const details = document.querySelectorAll("details");

let prevAccordion = null;
let prevSpan = null;

const isOpen = (accordion) =>
    typeof accordion.getAttribute("open") === "object";

const closeAccordion = (accordion, span) => {
    accordion.removeAttribute("open");
    span.classList.remove("active");
};

details.forEach((accordion) => {
    accordion.addEventListener("click", (e) => {
        if (prevAccordion && prevSpan && prevAccordion !== accordion) {
            closeAccordion(prevAccordion, prevSpan);
        }

        const span = accordion.querySelector("span");
        span.classList.toggle("active");

        if (isOpen(accordion)) {
            prevAccordion = accordion;
            prevSpan = span;
        }

        if (e.target.tagName === "P") closeAccordion(accordion, span);
    });
});
summary {
    list-style: none;
}

details {
    padding: 1rem;
    background-color: lightblue;
}

details summary {
    display: flex;
    justify-content: space-between;
}

details:nth-child(even) {
    background-color: lightgreen;
}

p {
    padding-top: 0.3rem;
}

.active {
    transform: rotate(180deg);
}

span {
    display: block;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="index.css">
</head>

<body>
    <div>
        <details>
            <summary>
                <h3>item1</h3>
                <span>arrow</span>
            </summary>
            <p>desc1</p>
        </details>
        <details>
            <summary>
                <h3>item2</h3>
                <span>arrow</span>
            </summary>
            <p>desc2</p>
        </details>
        <details>
            <summary>
                <h3>item3</h3>
                <span>arrow</span>
            </summary>
            <p>desc3</p>
        </details>
    </div>
    <script src="script.js"></script>
</body>

</html>

CodePudding user response:

You need to filter current and toggle current state and remove active from all other element classes.

This is your solution:

const details = document.querySelectorAll("details");

details.forEach((accordion) => {
    accordion.addEventListener("click", function (e) {
        let current = e.currentTarget;
        let others = Array.from(details).filter((item) => current != item);

        others.forEach((ot) => {
            ot.removeAttribute("open");
            ot.querySelector("span").classList.remove("active");
        });

        current.querySelector("span").classList.toggle("active");
    });
});
 summary{ 
      list-style: none 
    }

    details{
      padding: 1rem;
      background-color: lightblue;

     }

    details summary{
      display: flex;
      justify-content: space-between;
    }

    details:nth-child(even){
      background-color: lightgreen;
    }

    p{
      padding-top: 0.3rem;
    }

    .active{
      transform: rotate(180deg);
    }

    span{
    display: block;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
 <div>
      <details>
        <summary>    
          <h3>item1</h3> 
          <span>arrow</span> 
        </summary>
        <p>desc1</p>
      </details>
      <details>
        <summary>
          <h3>item2</h3> 
          <span>arrow</span> 
        </summary> 
        <p>desc2</p>
      </details>
      <details>
        <summary> 
          <h3>item3</h3> 
          <span>arrow</span> 
        </summary>
        <p>desc3</p>
      </details>
    </div>

  • Related