1

Please I need help with making this accordion work I am not too familiar with StimulusJS. The first accordion works fine but the rest does not respond. I have attached a snippet of the code here please let me know what I am doing wrong thank you.

The script tag contains the stimulusjs code. Please I would appreciate your comments.

<script src="https://cdn.tailwindcss.com"></script>
<script type="module">
    import { Application, Controller } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js"
    window.Stimulus = Application.start()

    Stimulus.register("dropdown", class extends Controller {
     static targets = ["background", "drop", "expand", "button"];
    static values = { accordionValue: Number };
    connect() {
        console.log("Drop Down connected");
    }

    initialize() {
        this.isOpen = true;
    }

    onToggle = (e) => {
        Array.prototype.forEach.call(this.buttonTargets, function (element, index) {
            element.addEventListener("click", function () {
                console.log(index)
            })
        })
        this.isOpen ? this.show() : this.hide();
        this.isOpen = !this.isOpen;
    };

    show() {
        this.dropTarget.className = "block w-full text-base font-light pt-3";
        this.backgroundTarget.className = "bg-[#F0F0F0] mb-2 py-6 px-4";
        this.expandTarget.innerHTML = "-";
        console.log("dropdown is active");
    }

    hide() {
        this.dropTarget.className = "hidden";
        this.backgroundTarget.className = "bg-white -mb-2 w-full py-6 px-4";
        this.expandTarget.innerHTML = "+";
        console.log("dropdown is closed");
    }
    })
  </script>

<div data-controller="dropdown" class="w-full flex flex-col gap-12 md:flex-row sm:max-w-[400px] md:max-w-[450px] lg:max-w-[500px] text-[#868686]">
  <div class="md:min-h-[450px] w-full mt-3">
    <h4 class="text-4xl font-semibold px-4 pb-3">FAQ's</h4>
    <div data-dropdown-target="background" class=" py-4 px-4">
      <div data-action="click->dropdown#onToggle" data-dropdown-target="button" class="cursor-pointer flex justify-between items-center">
        <h5 class="text-xl font-bold">Waar hebben jullie nieuwe tuinen aangelegd? </h5><span data-dropdown-target="expand" class="font-light text-[18px] self-start">+</span>
      </div>

      <p data-dropdown-target="drop" class="hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
    </div>
    <div data-dropdown-target="background" class=" py-4 px-4">
      <div data-action="click->dropdown#onToggle" data-dropdown-target="button" class="cursor-pointer flex justify-between items-center">
        <h5 class="text-xl font-bold">Waar moet een goede hovenier aan voldoen?</h5><span data-dropdown-target="expand" class="font-light text-[18px] self-start">+</span>
      </div>
      <p data-dropdown-target="drop" class="hidden ">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
    </div>
    <div data-dropdown-target="background" class="py-4 px-4">
      <div data-action="click->dropdown#onToggle" data-dropdown-target="button" class="cursor-pointer flex justify-between items-center">
        <h5 class="text-xl font-bold">Wat kost de aanleg van een nieuwe tuin? </h5><span data-dropdown-target="expand" class="font-light text-[18px] self-start">+</span>
      </div>
      <p data-dropdown-target="drop" class="hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
    </div>
    <div data-dropdown-target="background" class="py-4 px-4">
      <div data-action="click->dropdown#onToggle" data-dropdown-target="button" class="cursor-pointer flex justify-between items-center">
        <h5 class="text-xl font-bold">Zijn de afspraken vrijblijvend?</h5><span data-dropdown-target="expand" class="font-light text-[18px] ml-auto self-start">+</span>
      </div>
      <p data-dropdown-target="drop" class="hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
    </div>
  </div>
</div>
moses ubah
  • 11
  • 1

1 Answers1

7

Firstly - start with the HTML

The Stimulus docs provide a good guide and one of the key principles is 'Start with the HTML'.

All modern browsers support the HTML details disclosure element, and you can find really solid documentation for this on the MDN site. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details

Using Tailwind you can get pretty far but you may need to add a plugin to style against the open attribute.

For styling, see

It would probably be a bit easier without Tailwind but that is up to you.

Get as close as you can without writing a single line of JavaScript, a nicely styled single accordion item that opens & closes without additional code.

The main benefits of using native HTML elements are

  • Less code to maintain is always better.
  • Forces you to think about accessibility and keyboard control instead of rolling your own 'button' like things that are not really buttons and breaking accessibility.
  • Might be good enough to leave as is and move on, you may not need the 'close others when one opens' feature and you can move on to other things in your project.

Secondly - fill in the styling gaps with Stimulus

  • You may find you have to use some JavaScript to add/remove some classes, try your best to avoid this, but if you have to your code approach above is close enough.
  • You may want to be more explicit about what classes to use on the element using the Stimulus classes approach https://stimulus.hotwired.dev/reference/css-classes

Finally - add accordion like behaviour

  • Assuming you want some kind of behaviour like the Bootstrap accordion where expanding one item will collapse any others already open.
  • Below is the simplest JavaScript to have an accordion controller that closes all other items (targets) when one opens.
  • When the method toggle is called, it reads that event's target and then works out if it is opening or closing. If it is opening, we go through all other accordion item targets and close them.
import { Controller } from '@hotwired/stimulus';

class AccordionController extends Controller {
  static targets = ['item'];

  toggle({ target }) {
    const isOpening = target.hasAttribute('open');

    if (isOpening) {
      // if opening - close the others
      this.itemTargets.forEach((item) => {
        if (item === target) return;
        item.removeAttribute('open');
      });
    }
  }
}

export default AccordionController;
  • In the HTML we declare the data-controller="accordion" on the outer container.
  • For each details element we add two attributes:
    • data-accordion-target="item" - this scopes this details element to the accordion controller's this.itemTargets array.
    • data-action="toggle->accordion#toggle" - this adds an event listener for the build in details toggle event, note that this event does not bubble (unlike click) hence why we need it on each details element.
<section class="prose m-5" data-controller="accordion">
  <h2>Multiple 'details' element into an accordion</h2>
  <details
    class="py-4 px-4"
    data-action="toggle->accordion#toggle"
    data-accordion-target="item"
    >
    <summary
      class="flex justify-between items-center text-xl font-bold
      cursor-pointer">
      Section A
    </summary>
    <div class="bg-[#F0F0F0] mb-2 py-6 px-4">
      Content
    </div>
  </details>
  <details
    class="py-4 px-4"
    data-action="toggle->accordion#toggle"
    data-accordion-target="item"
    >
    <summary
      class="flex justify-between items-center text-xl font-bold
      cursor-pointer">
      Section B
    </summary>
    <div class="bg-[#F0F0F0] mb-2 py-6 px-4">
      Content
    </div>
  </details>
  <details
    class="py-4 px-4"
    data-action="toggle->accordion#toggle"
    data-accordion-target="item"
    >
    <summary
      class="flex justify-between items-center text-xl font-bold
      cursor-pointer">
      Section C
    </summary>
    <div class="bg-[#F0F0F0] mb-2 py-6 px-4">
      Content
    </div>
  </details>
</section>

General tips

  • Never use a div for something that is clickable, it is just going to give you a hard time, always use a button with type="button".
  • There are some nuances with accessibility and the details element, be sure you understand these if your application needs to be AA compliant or higher.
LB Ben Johnston
  • 4,751
  • 13
  • 29
  • This is an incredibly well written response. Thank you for taking the time to explain it - it was incredibly helpful to me today - especially the HTML Details. I had no idea that was an HTML native feature. – Rich May 04 '23 at 17:16