2

I used CSS instead of Fontawesome to generate the arrows. I think it makes more sense than loading a separate icon library. However I have trouble positioning those CSS arrow to the center, when it points upwards it looks great, I think it's in the center or at least near to the center, but when it points downwards it looks like the arrow moved towards the bottom. I would be grateful for any suggestion

"use strict";

const panelHeader = document.querySelectorAll(".panel-header");

panelHeader.forEach(item => {
  item.addEventListener("click", event => {
    event.preventDefault();
    item.parentElement.classList.toggle("open");
    const panel = item.nextElementSibling;
    panel.style.height = panel.style.height ? null : panel.scrollHeight + "px";
  });
});
:root {
  box-sizing: border-box;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

body {
  margin: 0;
  padding: 0;
}

.accordion {
  max-width: 1200px;
  margin: 0 auto;
}

.accordion-container {
  padding: 15px;
}

h2 {
  color: #444;
  font-size: 1.75rem;
  position: relative;
  padding: 0 0 25px 0;
  margin: 15px 0 20px 0;
}

h2::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 50px;
  height: 5px;
  background: #f79c31;
}

.panel-container > .panel + .panel {
  margin-top: 15px;
}

.panel {
  background: #f9f9f9;
  border: 1px solid #ddd;
  border-radius: 0.1875em;
}

.panel-header {
  background: #564990;
  border-color: #564990;
  border-top-left-radius: 0.1875em;
  border-top-right-radius: 0.1875em;
  position: relative;
  transition: background .25s linear;
}

.panel-header > h4 {
  margin: 0;
}

.panel-header > h4 > a {
  position: relative;
  display: block;
  color: #fff;
  font-size: 1.125rem;
  text-decoration: none;
  padding: 15px 50px 15px 15px;
}

.panel-header:hover {
  background: #443776;
}

.panel-body {
  height: 0;
  overflow: hidden;
  transition: 0.3s height 0.2s;
}

.panel-body-container {
  padding: 15px;
}

.arrow {
  position: absolute;
  top: 22px;
  right: 10px;
  font-size: 1.7rem;
  border: solid #fff;
  border-width: 0 4px 4px 0;
  display: inline-block;
  padding: 5px;
  opacity: .5;
  transform: rotate(-135deg);
  transition: transform 0.15s linear;
}

.arrow-up {

}

.panel.open .arrow {
  transform: rotate(-315deg);
  transform-origin: center center;
}
<!DOCTYPE html>
<html lang="en-US">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="accordion-faq.css">
  <title>Accordion FAQ</title>
</head>
<body>
  <section class="accordion">
    <div class="accordion-container">
      <header>
        <h2>FAQs</h2>
      </header>

      <div class="panel-container">
        <div class="panel">
          <div class="panel-header">
            <h4>
              <a href="#">First question?</a>
            </h4>
            <div class="arrow"><div class="arrow-up"></div></div>
          </div>
          <div class="panel-body">
            <div class="panel-body-container">
              <p>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Accusantium animi blanditiis corporis dicta, dolor dolores
                enim facilis fuga itaque iure iusto molestiae mollitia
                natus nisi pariatur praesentium quo rerum vel.
              </p>
            </div>
          </div>
        </div> <!-- .panel -->

        <div class="panel">
          <div class="panel-header">
            <h4>
              <a href="#">Second question?</a>
            </h4>
            <div class="arrow arrow-up"></div>
          </div>
          <div class="panel-body">
            <div class="panel-body-container">
              <p>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Accusantium animi blanditiis corporis dicta, dolor dolores
                enim facilis fuga itaque iure iusto molestiae mollitia
                natus nisi pariatur praesentium quo rerum vel.
              </p>
            </div>
          </div>
        </div> <!-- .panel -->
      </div> <!-- .panel-container -->
    </div> <!-- .accordion-container -->
  </section>

  <script src="accordion-faq.js"></script>
</body>
</html>
Zoltan King
  • 1,964
  • 4
  • 18
  • 38

5 Answers5

1

First of all you can avoid using position: absolute for this type of problem where display: flex do the trick:

const panelHeader = document.querySelectorAll(".panel-header");

panelHeader.forEach(item => {
  item.addEventListener("click", event => {
    event.preventDefault();
    item.parentElement.classList.toggle("open");
    const panel = item.nextElementSibling;
    panel.style.height = panel.style.height ? null : panel.scrollHeight + "px";
  });
});
:root {
  box-sizing: border-box;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

body {
  margin: 0;
  padding: 0;
}

.accordion {
  max-width: 1200px;
  margin: 0 auto;
}

.accordion-container {
  padding: 15px;
}

h2 {
  color: #444;
  font-size: 1.75rem;
  position: relative;
  padding: 0 0 25px 0;
  margin: 15px 0 20px 0;
}

h2::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 50px;
  height: 5px;
  background: #f79c31;
}

.panel-container>.panel+.panel {
  margin-top: 15px;
}

.panel {
  background: #f9f9f9;
  border: 1px solid #ddd;
  border-radius: 0.1875em;
}

.panel-header {
  display: flex;  /* <- Use flexbox */
  justify-content: space-between;
  align-items: center;
  background: #564990;
  border-color: #564990;
  border-top-left-radius: 0.1875em;
  border-top-right-radius: 0.1875em;
  position: relative;
  transition: background .25s linear;
}

.panel-header>h4 {
  margin: 0;
}

.panel-header>h4>a {
  position: relative;
  display: block;
  color: #fff;
  font-size: 1.125rem;
  text-decoration: none;
  padding: 15px 50px 15px 15px;
}

.panel-header:hover {
  background: #443776;
}

.panel-body {
  height: 0;
  overflow: hidden;
  transition: 0.3s height 0.2s;
}

.panel-body-container {
  padding: 15px;
}

.arrow {
  /* Don't need position absolute anymore */
  margin: 10px;
  font-size: 1.7rem;
  border: solid #fff;
  border-width: 0 4px 4px 0;
  display: inline-block;
  padding: 5px;
  opacity: .5;
  transform: rotate(-135deg);
  transition: transform 0.15s linear;
}

.arrow-up {}

.panel.open .arrow {
  margin-top: -5px;  /* <- Remove arrow heigth (5px) to stay at the same level */
  transform: rotate(-315deg);
  transform-origin: center center;
}
<!DOCTYPE html>
<html lang="en-US">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="accordion-faq.css">
  <title>Accordion FAQ</title>
</head>

<body>
  <section class="accordion">
    <div class="accordion-container">
      <header>
        <h2>FAQs</h2>
      </header>

      <div class="panel-container">
        <div class="panel">
          <div class="panel-header">
            <h4>
              <a href="#">First question?</a>
            </h4>
            <div class="arrow">
              <div class="arrow-up"></div>
            </div>
          </div>
          <div class="panel-body">
            <div class="panel-body-container">
              <p>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium animi blanditiis corporis dicta, dolor dolores enim facilis fuga itaque iure iusto molestiae mollitia natus nisi pariatur praesentium quo rerum vel.
              </p>
            </div>
          </div>
        </div>
        <!-- .panel -->

        <div class="panel">
          <div class="panel-header">
            <h4>
              <a href="#">Second question?</a>
            </h4>
            <div class="arrow arrow-up"></div>
          </div>
          <div class="panel-body">
            <div class="panel-body-container">
              <p>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium animi blanditiis corporis dicta, dolor dolores enim facilis fuga itaque iure iusto molestiae mollitia natus nisi pariatur praesentium quo rerum vel.
              </p>
            </div>
          </div>
        </div>
        <!-- .panel -->
      </div>
      <!-- .panel-container -->
    </div>
    <!-- .accordion-container -->
  </section>

  <script src="accordion-faq.js"></script>
</body>

</html>
johannchopin
  • 13,720
  • 10
  • 55
  • 101
1

With the current setup you have, the best solution would be to either -

  1. Change the top value in the animation class. You will also want to change the animate value to all so that it also animates the change in the top value without jumping.
.arrow {
  ...
  transition: all 0.15s linear;
}

.panel.open .arrow {
  transform: rotate(-315deg);
  transform-origin: center center;
  top: 18px;
}
  1. You could also change the transform-origin to 100% center, however this causes the animation to spin in a weird way.

You could also look into using built-in HTML arrows and rotating them if you do not want to load a font library or an SVG icon. It may behave more how you are expecting - https://www.toptal.com/designers/htmlarrows/

Kyle Soeltz
  • 306
  • 1
  • 9
0

Try changing your top property of the .arrow class to top: 50% so that it isn't hardcoded.

Then, add to your transform property translate(0, -50%).

Elijah Mock
  • 587
  • 8
  • 21
  • Did you make sure to put both transform properties on the same line? Like, your `rotate` property along with this `translate` property? https://stackoverflow.com/a/10765771/8804293 – Elijah Mock Apr 09 '20 at 19:58
  • Yes, I put them on the same line. transform: rotate(-135deg) translate(0, -50%); – Zoltan King Apr 09 '20 at 19:59
  • 1
    @ZoltanKing I added an answer and it works ! Try it and tell me what is going on :) – Jérôme Apr 09 '20 at 20:03
0

Use the top propriety in CSS.
This will set the top position of the arrow when rotating it.

Here is your code:

"use strict";

const panelHeader = document.querySelectorAll(".panel-header");

panelHeader.forEach(item => {
  item.addEventListener("click", event => {
    event.preventDefault();
    item.parentElement.classList.toggle("open");
    const panel = item.nextElementSibling;
    panel.style.height = panel.style.height ? null : panel.scrollHeight + "px";
  });
});
:root {
  box-sizing: border-box;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

body {
  margin: 0;
  padding: 0;
}

.accordion {
  max-width: 1200px;
  margin: 0 auto;
}

.accordion-container {
  padding: 15px;
}

h2 {
  color: #444;
  font-size: 1.75rem;
  position: relative;
  padding: 0 0 25px 0;
  margin: 15px 0 20px 0;
}

h2::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 50px;
  height: 5px;
  background: #f79c31;
}

.panel-container > .panel + .panel {
  margin-top: 15px;
}

.panel {
  background: #f9f9f9;
  border: 1px solid #ddd;
  border-radius: 0.1875em;
}

.panel-header {
  background: #564990;
  border-color: #564990;
  border-top-left-radius: 0.1875em;
  border-top-right-radius: 0.1875em;
  position: relative;
  transition: background .25s linear;
}

.panel-header > h4 {
  margin: 0;
}

.panel-header > h4 > a {
  position: relative;
  display: block;
  color: #fff;
  font-size: 1.125rem;
  text-decoration: none;
  padding: 15px 50px 15px 15px;
}

.panel-header:hover {
  background: #443776;
}

.panel-body {
  height: 0;
  overflow: hidden;
  transition: 0.3s height 0.2s;
}

.panel-body-container {
  padding: 15px;
}

.arrow {
  position: absolute;
  top: 22px;
  right: 10px;
  font-size: 1.7rem;
  border: solid #fff;
  border-width: 0 4px 4px 0;
  display: inline-block;
  padding: 5px;
  opacity: .5;
  transform: rotate(-135deg);
  transition: transform 0.15s linear;
}

.arrow-up {

}

.panel.open .arrow {
  transform: rotate(-315deg);
  transform-origin: center center;
  top: 15px; //Do it!
}
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="accordion-faq.css">
  <title>Accordion FAQ</title>
</head>
<body>
  <section class="accordion">
    <div class="accordion-container">
      <header>
        <h2>FAQs</h2>
      </header>

      <div class="panel-container">
        <div class="panel">
          <div class="panel-header">
            <h4>
              <a href="#">First question?</a>
            </h4>
            <div class="arrow"><div class="arrow-up"></div></div>
          </div>
          <div class="panel-body">
            <div class="panel-body-container">
              <p>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Accusantium animi blanditiis corporis dicta, dolor dolores
                enim facilis fuga itaque iure iusto molestiae mollitia
                natus nisi pariatur praesentium quo rerum vel.
              </p>
            </div>
          </div>
        </div> <!-- .panel -->

        <div class="panel">
          <div class="panel-header">
            <h4>
              <a href="#">Second question?</a>
            </h4>
            <div class="arrow arrow-up"></div>
          </div>
          <div class="panel-body">
            <div class="panel-body-container">
              <p>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Accusantium animi blanditiis corporis dicta, dolor dolores
                enim facilis fuga itaque iure iusto molestiae mollitia
                natus nisi pariatur praesentium quo rerum vel.
              </p>
            </div>
          </div>
        </div> <!-- .panel -->
      </div> <!-- .panel-container -->
    </div> <!-- .accordion-container -->
  </section>

  <script src="accordion-faq.js"></script>
</body>

A living demo: https://codepen.io/marchmello/pen/mdedGra

MARSHMALLOW
  • 1,315
  • 2
  • 12
  • 24
0

You need to use the position: absolute and top position on your .panel.open .arrow css like this :

"use strict";

const panelHeader = document.querySelectorAll(".panel-header");

panelHeader.forEach(item => {
  item.addEventListener("click", event => {
    event.preventDefault();
    item.parentElement.classList.toggle("open");
    const panel = item.nextElementSibling;
    panel.style.height = panel.style.height ? null : panel.scrollHeight + "px";
  });
});
:root {
  box-sizing: border-box;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

body {
  margin: 0;
  padding: 0;
}

.accordion {
  max-width: 1200px;
  margin: 0 auto;
}

.accordion-container {
  padding: 15px;
}

h2 {
  color: #444;
  font-size: 1.75rem;
  position: relative;
  padding: 0 0 25px 0;
  margin: 15px 0 20px 0;
}

h2::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 50px;
  height: 5px;
  background: #f79c31;
}

.panel-container > .panel + .panel {
  margin-top: 15px;
}

.panel {
  background: #f9f9f9;
  border: 1px solid #ddd;
  border-radius: 0.1875em;
}

.panel-header {
  background: #564990;
  border-color: #564990;
  border-top-left-radius: 0.1875em;
  border-top-right-radius: 0.1875em;
  position: relative;
  transition: background .25s linear;
}

.panel-header > h4 {
  margin: 0;
}

.panel-header > h4 > a {
  position: relative;
  display: block;
  color: #fff;
  font-size: 1.125rem;
  text-decoration: none;
  padding: 15px 50px 15px 15px;
}

.panel-header:hover {
  background: #443776;
}

.panel-body {
  height: 0;
  overflow: hidden;
  transition: 0.3s height 0.2s;
}

.panel-body-container {
  padding: 15px;
}

.arrow {
  position: absolute;
  top: 22px;
  right: 10px;
  font-size: 1.7rem;
  border: solid #fff;
  border-width: 0 4px 4px 0;
  display: inline-block;
  padding: 5px;
  opacity: .5;
  transform: rotate(-135deg);
  transition: transform 0.15s linear;
}

.arrow-up {

}

.panel.open .arrow {
  transform: rotate(-315deg);
  transform-origin: center center;
  position: absolute;
  top: 30%;
}
<!DOCTYPE html>
<html lang="en-US">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="accordion-faq.css">
  <title>Accordion FAQ</title>
</head>
<body>
  <section class="accordion">
    <div class="accordion-container">
      <header>
        <h2>FAQs</h2>
      </header>

      <div class="panel-container">
        <div class="panel">
          <div class="panel-header">
            <h4>
              <a href="#">First question?</a>
            </h4>
            <div class="arrow"><div class="arrow-up"></div></div>
          </div>
          <div class="panel-body">
            <div class="panel-body-container">
              <p>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Accusantium animi blanditiis corporis dicta, dolor dolores
                enim facilis fuga itaque iure iusto molestiae mollitia
                natus nisi pariatur praesentium quo rerum vel.
              </p>
            </div>
          </div>
        </div> <!-- .panel -->

        <div class="panel">
          <div class="panel-header">
            <h4>
              <a href="#">Second question?</a>
            </h4>
            <div class="arrow arrow-up"></div>
          </div>
          <div class="panel-body">
            <div class="panel-body-container">
              <p>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Accusantium animi blanditiis corporis dicta, dolor dolores
                enim facilis fuga itaque iure iusto molestiae mollitia
                natus nisi pariatur praesentium quo rerum vel.
              </p>
            </div>
          </div>
        </div> <!-- .panel -->
      </div> <!-- .panel-container -->
    </div> <!-- .accordion-container -->
  </section>

  <script src="accordion-faq.js"></script>
</body>
</html>
Jérôme
  • 978
  • 1
  • 9
  • 22