Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

How to apply slidedown functionality in an accordion panel?

I have an example included in this question. I've created a minimal JavaScript accordion and I have a transition built in, but it's not functioning the way I've intended. I'm trying to slide down the panel when opened at .2s ease. I'm having trouble applying that using CSS or JS.

function initAccordion() {
  let handlePanelClick = (e) => { showPanel(e.currentTarget.parentNode) };

  function showPanel(panel) {
    let isWide = isWideScreen();
    let isActive = panel.classList.contains("active");    

    if (isWide) {
      !isActive ? panel.classList.add('active') : panel.classList.remove('active');
    } else { // handle mobiles
      let panels = document.querySelectorAll('.panel');
      panels.forEach(p => p.classList.remove('active'));
      !isActive ? panel.classList.add('active') : null;
    }

    if (!isActive) {
      personalbar.classList.add('active');
      panel.style.height = 'auto';

      setTimeout(function() {
        panel.style.height = height;
      }, 0);
    } else {
      panel.style.height = '0px';

      panel.addEventListener('transitionend', function() {
        panel.classList.remove('active');
      }, {
        once: true
      });
    }
  }

  let allPanelElements = document.querySelectorAll(".panel");
  allPanelElements.forEach(allPanels => allPanels.querySelector(".acc-trigger")
    .addEventListener("click", handlePanelClick));
}

let isWideScreen = ()=> { return window.matchMedia('(min-width: 992px)').matches; }
initAccordion(document.getElementsByClassName("accordion"));

Here's the CSS...

.wrapper {
  margin: 50px auto 0;
  display: flex;
  flex-direction: column;
}

.wrapper a {
  text-decoration: none;
}

.wrapper button {
  border-style: none;
  background: white;
}

.wrapper button::-moz-focus-inner {
  border: 0;
}

.wrapper .accordion {
  margin: 0 0.1rem;
}

.wrapper .accordion .panel {
  min-height: 4rem;
  margin: 0 0.1rem;
}

.wrapper .accordion .panel .acc-trigger {
  display: block;
  font-weight: 400;
  margin: 0;
  padding: 0.5em 0;
  position: relative;
  text-align: left;
  width: 100%;
  cursor: pointer;
  background: 0 0;
}

.wrapper .accordion .panel .acc-trigger .acc-title {
  display: flex;
  align-items: center;
  border: transparent 2px solid;
  outline: 0;
  border-bottom: 1px solid #000A70;
  padding-bottom: 0.5rem;
  transition: all 0.2s ease;
}

.wrapper .accordion .panel .acc-trigger .acc-title h4 {
  font-weight: 800;
  color: #000A70;
  font-size: 1.25rem;
  justify-content: space-between;
  margin-right: 1rem;
  outline: 0;
}

.wrapper .accordion .panel .acc-trigger .acc-title h4:hover {
  color: #005fec;
}

.wrapper .accordion .panel .acc-trigger .acc-title svg.acc-icon {
  cursor: pointer;
  pointer-events: none;
  margin-left: auto;
  z-index: 3;
}

.wrapper .accordion .panel .acc-body {
  height: 0;
  opacity: 0;
  overflow: hidden;
  visibility: hidden;
  transition: height 0.2s ease-out, opacity 0.2s ease-out;
}

.wrapper .accordion .panel .acc-body ol.custom-list {
  list-style-type: none;
  -webkit-margin-before: 0;
  margin-block-start: 0;
  -webkit-margin-after: 0;
  margin-block-end: 0;
  counter-reset: my-awesome-counter;
}

.wrapper .accordion .panel .acc-body ol.custom-list li {
  counter-increment: my-awesome-counter;
  padding-left: 1.125rem;
  margin: 2rem 0;
  font-size: 1rem;
  line-height: 1.5rem;
  color: #000A70;
}

.wrapper .accordion .panel .acc-body ol.custom-list li::before {
  content: counter(my-awesome-counter) ". ";
  color: #005fec;
}

.wrapper .accordion .panel .acc-body ol.custom-list li:first-child {
  margin-top: 0;
}

.wrapper .accordion .panel .acc-body ol.custom-list li:last-child {
  margin-bottom: 2rem;
}

.wrapper .accordion .panel .acc-body ol.custom-list li a {
  color: #005fec;
}

.wrapper .accordion .panel .acc-body ol.custom-list li a:hover {
  text-decoration: underline;
  text-decoration-color: #005fec;
}

.wrapper .accordion .panel .acc-body ul.custom-list {
  list-style-type: none;
  -webkit-margin-before: 0;
  margin-block-start: 0;
  -webkit-margin-after: 0;
  margin-block-end: 0;
}

.wrapper .accordion .panel .acc-body ul.custom-list li {
  background-image: url(/assets/svg/bullet.svg);
  background-position: 0 0.5rem;
  background-repeat: no-repeat;
  padding-left: 1.125rem;
  margin: 2rem 0;
  font-size: 1rem;
  line-height: 1.5rem;
}

.wrapper .accordion .panel .acc-body ul.custom-list li a {
  color: #005fec;
}

.wrapper .accordion .panel .acc-body ul.custom-list li a:hover {
  text-decoration: underline;
  text-decoration-color: #005fec;
}

.wrapper .accordion .panel .acc-body ul.custom-list li:first-child {
  margin-top: 0;
}

.wrapper .accordion .panel .acc-body ul.custom-list li:last-child {
  margin-bottom: 2rem;
}

.wrapper .accordion .panel.active .acc-body {
  opacity: 1;
  height: auto;
  padding: 0.5rem 0;
  visibility: visible;
}

.wrapper .accordion .panel.active .acc-body p {
  font-size: 1rem;
  line-height: 1.5rem;
}

.wrapper .accordion .panel.active .acc-body p+.embed-youtube {
  margin: 1rem 0;
}

.wrapper .accordion .panel.active .acc-body p a {
  color: #005fec;
}

.wrapper .accordion .panel.active .acc-body p a:hover {
  text-decoration: underline;
  text-decoration-color: #005fec;
}

.wrapper .accordion .panel.active .acc-icon {
  transform: rotate(180deg);
}

.wrapper .accordion .panel.active .acc-trigger .acc-title {
  border-bottom: 0;
}

.wrapper .accordion .panel.active .acc-trigger .acc-title h4 {
  color: #005fec;
}

.wrapper .accordion .panel.active p:last-child {
  border-bottom: 1px solid #000A70;
  padding-bottom: 0.5rem;
}

.acc-title svg.acc-icon {
  transition: all 0.2s ease;
  min-width: 1rem;
}

@media (min-width: 768px) {
  .wrapper {
    flex-direction: row;
  }
  .accordion {
    flex: 1;
  }
}

...and the HTML:

<section id="questions">
  <div class="container">
    <div class="row justify-content-center align-items-start">
      <h3>Any Questions?</h3>
      <div class="wrapper">
        <div class="accordion">
          <div class="panel">
            <button class="acc-trigger">
                <span class="acc-title">
                  <h4>Lorem ipsum dolor sit amet.</h4>
                  <svg class="acc-icon" width="16" height="11" viewBox="0 0 16 11" fill="none"
                    xmlns="http://www.w3.org/2000/svg">
                    <path d="M0 2.82813L2 0.828126L8 6.82813L14 0.828126L16 2.82813L8 10.8281L0 2.82813Z"
                      fill="#000A70" />
                  </svg>
                </span>
              </button>
            <div class="acc-body">
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi enim quas laboriosam itaque harum ab voluptas? Deserunt aliquam pariatur voluptatem odit iure autem eos totam eius consequuntur animi. Omnis, ipsam.</p>
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi enim quas laboriosam itaque harum ab voluptas? Deserunt aliquam pariatur voluptatem odit iure autem eos totam eius consequuntur animi. Omnis, ipsam.</p>
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi enim quas laboriosam itaque harum ab voluptas? Deserunt aliquam pariatur voluptatem odit iure autem eos totam eius consequuntur animi. Omnis, ipsam.</p>
            </div>
          </div>
          <div class="panel">
            <button class="acc-trigger">
                <span class="acc-title">
                  <h4>Lorem ipsum dolor sit amet.</h4>
                  <svg class="acc-icon" width="16" height="11" viewBox="0 0 16 11" fill="none"
                    xmlns="http://www.w3.org/2000/svg">
                    <path d="M0 2.82813L2 0.828126L8 6.82813L14 0.828126L16 2.82813L8 10.8281L0 2.82813Z"
                      fill="#000A70" />
                  </svg>
                </span>
              </button>
            <div class="acc-body">
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi enim quas laboriosam itaque harum ab voluptas? Deserunt aliquam pariatur voluptatem odit iure autem eos totam eius consequuntur animi. Omnis, ipsam.</p>
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi enim quas laboriosam itaque harum ab voluptas? Deserunt aliquam pariatur voluptatem odit iure autem eos totam eius consequuntur animi. Omnis, ipsam.</p>
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi enim quas laboriosam itaque harum ab voluptas? Deserunt aliquam pariatur voluptatem odit iure autem eos totam eius consequuntur animi. Omnis, ipsam.</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

I've put together a Codepen with this information as well here.

It's worth noting that I have a site filled with these accordions. I can't really change the markup, just the behavior of the JS.

Any direction on this would be great!

1 Answer

Your code uses height: auto;, but to use transitions, CSS needs fixed values. Instead of trying to modify the height of the panel, have you tried modifying the max-height of the panel and changing the max-height between 0 and something that will be larger than the panel's height, such as 30em?

I have about 150 of these accordions on my site. Each accordion has anywhere between 5 and 10 panels. They vary in heights and content. I can't have any specified heights attached to them. I'm trying to work out a way to read the dynamic height (i.e.; element.clientHeight) and set it accordingly.