如何使用 AlpineJS 和 Flatpickr 停止传播?
How to stop propagation with AlpineJS and Flatpickr?
我的应用程序中有一个过滤器模态 window。过滤器模态具有 @click.outside="filters = false"
,因此如果用户在模态之外单击,它将隐藏。在该过滤器模式中,我可以选择使用 Flatpickr 的最短日期。
问题是当您单击箭头更改月份 - 或者顶部的月份或年份 - 过滤器模式将隐藏。
我认为我需要在元素上使用 e.stopPropagation
或 @click.prevent
,但它在我尝试过的每个地方都不起作用。
如何才能使 Flatpicker window 内的任何点击都不会向上传播并关闭过滤器模式?
这是我的完整代码 -
<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>
我不相信 AlpineJS 能够做到这一点,所以你可以加入一些自定义 JS。您可以在结束正文标记之前的脚本标记中添加此代码:
[...document.getElementsByClassName("flatpickr-calendar")].forEach($el => {
$el.addEventListener("click", e => e.stopPropagation());
});
用户打开 Flatpickr 弹出窗口后,Flatpickr 会将日历附加到正文 的末端 ,而不是在 div 的内部,因此它是“外部”点击.
AlpineJS 通过向 window
(或类似的东西)添加事件侦听器来确定“外部”点击,当事件冒泡(传播)时,它会测试点击目标是否是请求外部的元素点击。如果没有,则触发一个事件,表明有“外部”点击。
这段代码本质上做的是,一旦点击 Flatpickr 弹出日历,我们就会阻止事件冒泡到 AlpineJS,所以 Alpine 不知道 window根本没有,因此 @click.outside
不会在点击日历时触发。
此问题的一个简单解决方案是引入一个 pickerOpen
变量,通过 onOpen
and onClose
hooks 监视 Flatpickr 弹出窗口的状态。然后仅在 Flatkpickr 弹出窗口处于非活动状态时关闭模式 window。
<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>
我的应用程序中有一个过滤器模态 window。过滤器模态具有 @click.outside="filters = false"
,因此如果用户在模态之外单击,它将隐藏。在该过滤器模式中,我可以选择使用 Flatpickr 的最短日期。
问题是当您单击箭头更改月份 - 或者顶部的月份或年份 - 过滤器模式将隐藏。
我认为我需要在元素上使用 e.stopPropagation
或 @click.prevent
,但它在我尝试过的每个地方都不起作用。
如何才能使 Flatpicker window 内的任何点击都不会向上传播并关闭过滤器模式?
这是我的完整代码 -
<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>
我不相信 AlpineJS 能够做到这一点,所以你可以加入一些自定义 JS。您可以在结束正文标记之前的脚本标记中添加此代码:
[...document.getElementsByClassName("flatpickr-calendar")].forEach($el => {
$el.addEventListener("click", e => e.stopPropagation());
});
用户打开 Flatpickr 弹出窗口后,Flatpickr 会将日历附加到正文 的末端 ,而不是在 div 的内部,因此它是“外部”点击.
AlpineJS 通过向 window
(或类似的东西)添加事件侦听器来确定“外部”点击,当事件冒泡(传播)时,它会测试点击目标是否是请求外部的元素点击。如果没有,则触发一个事件,表明有“外部”点击。
这段代码本质上做的是,一旦点击 Flatpickr 弹出日历,我们就会阻止事件冒泡到 AlpineJS,所以 Alpine 不知道 window根本没有,因此 @click.outside
不会在点击日历时触发。
此问题的一个简单解决方案是引入一个 pickerOpen
变量,通过 onOpen
and onClose
hooks 监视 Flatpickr 弹出窗口的状态。然后仅在 Flatkpickr 弹出窗口处于非活动状态时关闭模式 window。
<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>