简体   繁体   中英

Alpinejs Accordion and Toggle on click

I'm using Alpinejs and Tailwind.

I'm trying to create an accordion with a toggle on each tab, but I want that toggle to trigger at the same time as when a user clicks to open each part of the accordion... here's what I got so far:

  <ul class="block mb-4" x-data="{pay:null}">
    <li class="flex flex-col">
      <div @click="pay !== 'cc' ? pay = 'cc' : pay = null" class="cursor-pointer px-5 py-3 flex items-center bg-blue-50 border text-xl font-semibold border-blue-800 text-blue-800 inline-block hover:shadow rounded-t">
        <button type="button" class="relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 bg-gray-200" x-data="{ on: false }" aria-pressed="false" :aria-pressed="on.toString()" @click="on = !on" x-state:on="Enabled" x-state:off="Not Enabled" :class="{ 'bg-orange-500': on, 'bg-white': !(on) }">
          <span class="sr-only">Credit Card</span>
          <span aria-hidden="true" class="pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200 translate-x-0" x-state:on="Enabled" x-state:off="Not Enabled" :class="{ 'translate-x-5': on, 'translate-x-0': !(on) }"></span>
        </button>
        <span class="ml-2">Credit / Debit Card</span>
      </div>
      <p x-show="pay == 'cc'" class="bg-white border-l border-r border-blue-800 py-4 px-2">
        This is made with Alpine JS and Tailwind CSS
      </p>
    </li>
    <li class="flex align-center flex-col">
      <h4 @click="pay !== 1 ? pay = 1 : pay = null" class="cursor-pointer px-5 py-3 bg-indigo-400 text-white text-center inline-block hover:opacity-75 hover:shadow hover:-mb-3">Accordion item 2</h4>
      <p x-show="pay == 1" class="border py-4 px-2">
        There's no external CSS or JS
      </p>
    </li>
    <li class="flex align-center flex-col">
      <h4 @click="pay !== 2 ? pay = 2 : pay = null" :class="{'cursor-pointer px-5 py-3 bg-indigo-500 text-white text-center inline-block hover:opacity-75 hover:shadow hover:-mb-3': true, 'rounded-b': pay != 2}">Accordion item 3</h4>
      <p x-show="pay == 2" :class="{'border py-4 px-2': true, 'rounded-b': pay == 2}">
        Pretty cool huh?
      </p>
    </li>
  </ul>

https://codepen.io/kennyk3/pen/eYBwEXN

I'm pretty green, so bear with me. I actually just want to comment but don't have enough reputation.

First of all, you need to remove bg-gray-200 from your <button class> because you're binding :class="{ bg-{color} } for on: true and on: false states. Another one is bg-orange-500 and ring-orange-500 isn't a defined utility on Tailwind CSS. Maybe you mean bg-yellow-500 and ring-yellow-500 ? Other than that, what is x-state ? I can't find it in Alpine.js docs.

Okay, about your requested problem, your code actually has 2 issues.

  1. You want a parent component to communicate with a child component, like a nested component. Alpine.js doesn't do that. Either you merge both components holding multiple properties in the parent component so that you only have one x-data or you $dispatch('customEvent') on your <div> at the @click event (together with pay toggling) and listen to the @customEvent.window on your <button> component.

Alternatively, you can install Alpine Magic Helpers for the $component/$parent communication or install Spruce for saving all states.

  1. Because of your <button> is nested in your <div> , you're firing both @click events every time you @click the <button> . This is why your code looks like the <button> component communicates with your <ul> component which is untrue. You're firing two different @click events. Because of this behavior, if you $dispatch('customEvent') from your <div> component and listen to the @customEvent.window on your <button> , you'll end up toggling your on state twice. Try add @click.debounce.250 on your <button> together with the @customEvent.window and you'll see it toggles to and fro.

As for that, you need to remove @click event on your <button> while listening to the @customEvent.window so that the toggling happens once, or better you merge both x-data making your <ul> holding multiple properties (no need to $dispatch('customEvent') on your <div> and @click on your <button> , just one @click on your <div> ).

In my implementation, I make x-data to hold both:

<ul class="block mb-4" x-data="{pay:null, on: false}">
  <li class="flex flex-col">
    <div @click="pay !== 'cc' ? pay = 'cc' : pay = null, on = !on" class="cursor-pointer px-5 py-3 flex items-center bg-blue-50 border text-xl font-semibold border-blue-800 text-blue-800 inline-block hover:shadow rounded-t">
      <button type="button" class="relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500" aria-pressed="false" :aria-pressed="on.toString()" x-state:on="Enabled" x-state:off="Not Enabled" :class="{ 'bg-yellow-500': on, 'bg-gray-200': !(on) }">
        <span class="sr-only">Credit Card</span>
        <span aria-hidden="true" class="pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200 translate-x-0" x-state:on="Enabled" x-state:off="Not Enabled" :class="{ 'translate-x-5': on, 'translate-x-0': !(on) }"></span>
      </button>
      <span class="ml-2">Credit / Debit Card</span>
    </div>
    <p x-show="pay == 'cc'" class="bg-white border-l border-r border-blue-800 py-4 px-2">
      This is made with Alpine JS and Tailwind CSS
    </p>
  </li>
  <li class="flex align-center flex-col">
    <h4 @click="pay !== 1 ? pay = 1 : pay = null" class="cursor-pointer px-5 py-3 bg-indigo-400 text-white text-center inline-block hover:opacity-75 hover:shadow hover:-mb-3">Accordion item 2</h4>
    <p x-show="pay == 1" class="border py-4 px-2">
      There's no external CSS or JS
    </p>
  </li>
  <li class="flex align-center flex-col">
    <h4 @click="pay !== 2 ? pay = 2 : pay = null" :class="{'cursor-pointer px-5 py-3 bg-indigo-500 text-white text-center inline-block hover:opacity-75 hover:shadow hover:-mb-3': true, 'rounded-b': pay != 2}">Accordion item 3</h4>
    <p x-show="pay == 2" :class="{'border py-4 px-2': true, 'rounded-b': pay == 2}">
      Pretty cool huh?
    </p>
  </li>
</ul>

https://codepen.io/wanahmadfiras/pen/jOVgBrz

Accordions (among other components) are now part of official AlpineJS documentation . Use them for perfect out of the box accordions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM