简体   繁体   中英

How to stop propagation with AlpineJS and Flatpickr?

I have a modal window for filters on my application. The filters modal has @click.outside="filters = false" so if the user clicks outside of the modal it will hide. Inside of that filters modal I have an option for choosing the minimum date for which I'm using Flatpickr.

The problem is when you click on the arrows to change the month - or the month or year at the top - the filters modal will hide.

I believe that I need to use e.stopPropagation or @click.prevent on the element but it hasn't worked in each spot that I've tried it.

How do I make it so that any clicks inside of the Flatpicker window doesn't propagate up and close the filters modal?

Here is my full code -

<div x-show="filters" @click.outside="filters = false" x-on:keydown.escape.window="filters = false" class="absolute shadow-lg z-40 mt-4">
  <div x-trap="filters">
    <div>
      <label for="filter-date-min" class="block text-sm font-semibold leading-5 text-gray-700">Minimum Date</label>
      <div class="mt-1 relative rounded-md shadow-sm">
        <div x-data="{
          value: '',
          init() {
            let picker = flatpickr(this.$refs.picker, {
              dateFormat: 'Y-m-d',
              defaultDate: this.value,
              onChange: (date, dateString) => {
                this.value = dateString
              },
            })
          },
        }">
          <input id="filter-date-min" placeholder="MM/DD/YYYY" x-ref="picker" x-bind:value="value" class="flex-1 min-w-0 block w-full px-3 py-2 rounded-none rounded-r-md focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border-gray-300 border active" autocomplete="off">
        </div>
      </div>
    </div>
  </div>
</div>

A simple solution for this issue is to introduce a pickerOpen variable that monitors the Flatpickr popup's state via the onOpen and onClose hooks . Then only close the modal window when Flatkpickr popup is inactive.

<div x-data="{filters: false, pickerOpen: false}">
  <div x-show="filters"
       @click.outside="if (!pickerOpen) {filters = false}"
       x-on:keydown.escape.window="if (!pickerOpen) {filters = false}"
       class="absolute shadow-lg z-40 mt-4">
    <div x-trap="filters">
      <div>
        <label for="filter-date-min" class="block text-sm font-semibold leading-5 text-gray-700">Minimum Date</label>
        <div class="mt-1 relative rounded-md shadow-sm">
          <div x-data="{
            value: '',
            init() {
              let picker = flatpickr(this.$refs.picker, {
                dateFormat: 'Y-m-d',
                defaultDate: this.value,
                onOpen: () => {this.pickerOpen = true},
                onClose: () => {this.pickerOpen = false},
                onChange: (date, dateString) => {
                  this.value = dateString
                },
              })
            },
          }">
            <input id="filter-date-min" placeholder="MM/DD/YYYY" x-ref="picker" x-bind:value="value" autocomplete="off"
                   class="flex-1 min-w-0 block w-full px-3 py-2 rounded-none rounded-r-md focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border-gray-300 border active">
          </div>
        </div>
      </div>
    </div>
  </div>
</div> 

I don't believe AlpineJS is capable of doing this, so you can throw in some custom JS. You can add this code in a script tag right before the closing body tag:

[...document.getElementsByClassName("flatpickr-calendar")].forEach($el => {
    $el.addEventListener("click", e => e.stopPropagation());
});

Once the user opens a Flatpickr popup, Flatpickr appends the calendar to the end of the body , not inside your div, so it's an "outside" click.

AlpineJS determines "outside" clicks by adding an event listener to window (or something similar), and when the event bubbles (propagates) up it tests whether the click target is the element that's requesting an outside click. If not, fire an event that there's an "outside" click.

What this code essentially does is, once there is a click on a Flatpickr popup calendar, we prevent the event from bubbling up to AlpineJS, so Alpine doesn't know there was any click on the window at all, thus @click.outside won't trigger when the calendar is clicked.

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