跳转至

x-on

x-on 允许你轻松地处理分发的 DOM 事件。

以下是一个简单按钮的示例,点击时会显示一个警告框。

<button x-on:click="alert('Hello World!')">Say Hi</button>

x-on 只能监听小写名称的事件,因为 HTML 属性不区分大小写。编写 x-on:CLICK 将监听名为 click 的事件。如果你需要监听使用 camelCase 名称的自定义事件,可以使用 .camel 辅助符 来解决此限制。或者,你可以使用 x-bind 在 JavaScript 代码中为元素附加 x-on 指令(此时大小写将保持不变)。

简写语法

如果 x-on: 对你来说过于冗长,你可以使用简写语法:@

以下是上面同一个组件,但使用了简写语法:

<button @click="alert('Hello World!')">Say Hi</button>

尽管上述代码片段中没有包含,但如果没有父元素定义 x-data,则无法使用 x-on→ 阅读更多关于 x-data

事件对象

如果你希望从表达式中访问原生 JavaScript 事件对象,可以使用 Alpine 的魔术属性 $event

<button @click="alert($event.target.getAttribute('message'))" message="Hello World">Say Hi</button>

此外,Alpine 还会将事件对象传递给任何没有尾随括号的方法引用。例如:

<button @click="handleClick">...</button>

<script>
    function handleClick(e) {
        // 现在你可以直接访问事件对象 (e)
    }
</script>

键盘事件

Alpine 使监听特定键的 keydownkeyup 事件变得简单。

以下是在输入元素中监听 Enter 键的示例:

<input type="text" @keyup.enter="alert('Submitted!')">

你还可以链式组合这些按键修饰符,以实现更复杂的监听。

以下是一个监听器,它在按住 Shift 键的同时按下 Enter 时触发,但在单独按下 Enter 时不触发。

<input type="text" @keyup.shift.enter="alert('Submitted!')">

你可以通过将键名转换为 kebab-case 形式,直接将 KeyboardEvent.key 暴露的任何有效键名用作修饰符。

<input type="text" @keyup.page-down="alert('Submitted!')">

为方便参考,以下是你可能想要监听的常用键列表。

修饰符 键盘键
.shift Shift
.enter Enter
.space Space
.ctrl Ctrl
.cmd Cmd
.meta Mac 上为 Cmd,Windows 上为 Windows 键
.alt Alt
.up .down .left .right 上/下/左/右方向键
.escape Escape
.tab Tab
.caps-lock Caps Lock
.equal 等号 =
.period 句点 .
.comma 逗号 ,
.slash 正斜杠 /

鼠标事件

与上面的键盘事件类似,Alpine 允许使用一些按键修饰符来处理 click 事件。

修饰符 事件键
.shift shiftKey
.ctrl ctrlKey
.cmd metaKey
.meta metaKey
.alt altKey

这些修饰符适用于 clickauxclickcontextdblclick 事件,甚至包括 mouseovermousemovemouseentermouseleavemouseoutmouseupmousedown

以下是一个按钮的示例,当按住 Shift 键时其行为会发生变化。

<button type="button"
    x-data="{ message: 'select' }"
    @click="message = 'selected'"
    @click.shift="message = 'added to selection'"
    @mousemove.shift="message = 'add to selection'"
    @mouseout="message = 'select'"
    x-text="message"></button>

注意:带有某些修饰符(如 ctrl)的普通 click 事件在大多数浏览器中会自动变为 contextmenu 事件。类似地,右键点击会触发 contextmenu 事件,但如果 contextmenu 事件被阻止,也会触发 auxclick 事件。

自定义事件

Alpine 事件监听器是原生 DOM 事件监听器的封装。因此,它们可以监听任何 DOM 事件,包括自定义事件。

以下是一个组件示例,它分发了一个自定义 DOM 事件,同时也监听了该事件。

<div x-data @foo="alert('Button Was Clicked!')">
    <button @click="$event.target.dispatchEvent(new CustomEvent('foo', { bubbles: true }))">...</button>
</div>

当按钮被点击时,@foo 监听器将被调用。

由于 .dispatchEvent API 较为冗长,Alpine 提供了 $dispatch 辅助工具来简化操作。

以下是使用 $dispatch 魔术属性重写的相同组件。

<div x-data @foo="alert('Button Was Clicked!')">
    <button @click="$dispatch('foo')">...</button>
</div>

→ 阅读更多关于 $dispatch

修饰符

Alpine 提供了许多指令修饰符,用于自定义事件监听器的行为。

.prevent

.prevent 相当于在监听器中调用浏览器事件对象上的 .preventDefault()

<form @submit.prevent="console.log('submitted')" action="/foo">
    <button>Submit</button>
</form>

在上面的示例中,使用 .prevent 后,点击按钮不会将表单提交到 /foo 端点。相反,Alpine 的监听器将处理它并"阻止"事件被进一步处理。

.stop

.prevent 类似,.stop 相当于在监听器中调用浏览器事件对象上的 .stopPropagation()

<div @click="console.log('I will not get logged')">
    <button @click.stop>Click Me</button>
</div>

在上面的示例中,点击按钮不会记录消息。这是因为我们立即停止了事件的传播,不允许它"冒泡"到带有 @click 监听器的 <div>

.outside

.outside 是一个方便的辅助工具,用于监听在其所附加的元素外部发生的点击。以下是一个简单的下拉组件示例:

<div x-data="{ open: false }">
    <button @click="open = ! open">Toggle</button>

    <div x-show="open" @click.outside="open = false">
        Contents...
    </div>
</div>

在上面的示例中,通过点击"Toggle"按钮显示下拉内容后,你可以通过点击页面上下拉内容外部的任意位置来关闭下拉菜单。

这是因为 .outside 监听的是不从其注册元素发起的点击。

值得注意的是,.outside 表达式仅在其注册的元素在页面上可见时才会被求值。否则,会出现讨厌的竞争条件,点击"Toggle"按钮也会触发现有元素不可见的 @click.outside 处理程序。

.window

当存在 .window 修饰符时,Alpine 会将事件监听器注册到页面上的根 window 对象上,而不是元素本身。

<div @keyup.escape.window="...">...</div>

上面的代码片段将监听页面上任何位置按下"escape"键的事件。

在这些情况下,将 .window 添加到监听器非常有用,即你的标记中小部分内容关心在整个页面上发生的事件。

.document

.document 的工作方式类似于 .window,只是它将监听器注册到 document 全局对象上,而不是 window 全局对象。

.once

通过向监听器添加 .once,你可以确保处理程序仅被调用一次。

<button @click.once="console.log('I will only log once')">...</button>

.debounce

有时,对事件处理程序进行"防抖"非常有用,这样它只会在一定的不活动时间(默认为 250 毫秒)后调用。

例如,如果你有一个搜索字段,在用户键入时会触发网络请求,添加防抖可以防止在每次按键时都触发网络请求。

<input @input.debounce="fetchResults">

现在,fetchResults 不会在每次按键后被调用,而是在 250 毫秒没有按键操作后才会被调用。

如果你希望延长或缩短防抖时间,可以通过在 .debounce 修饰符后添加一个持续时间来实现:

<input @input.debounce.500ms="fetchResults">

现在,fetchResults 只会在 500 毫秒不活动后调用。

.throttle

.throttle.debounce 类似,不同之处在于它会每 250 毫秒释放一次处理程序调用,而不是无限期地推迟。

这对于那些可能存在重复且长时间的事件触发,且使用 .debounce 无效的情况非常有用,因为你仍需定期处理该事件。

例如:

<div @scroll.window.throttle="handleScroll">...</div>

上面的示例是节流的一个很好的用例。没有 .throttle 时,当用户向下滚动页面时,handleScroll 方法可能会被触发数百次,这会严重拖慢网站速度。通过添加 .throttle,我们确保 handleScroll 每 250 毫秒只被调用一次。

有趣的是:本文档网站正是使用这种策略来更新右侧边栏中当前高亮的章节。

.debounce 一样,你可以为节流事件添加自定义持续时间:

<div @scroll.window.throttle.750ms="handleScroll">...</div>

现在,handleScroll 每 750 毫秒才会被调用一次。

.self

通过向事件监听器添加 .self,你可以确保事件是从声明该监听器的元素本身发出的,而不是来自子元素。

<button @click.self="handleClick">
    Click Me

    <img src="...">
</button>

在上面的示例中,<button> 标签内有一个 <img> 标签。通常情况下,任何从 <button> 元素内部(例如从 <img>)发出的点击,都会被按钮上的 @click 监听器捕获。

但是,在这种情况下,由于我们添加了 .self,只有点击按钮本身才会调用 handleClick。从 <img> 元素发出的点击将不会被处理。

.camel

<div @custom-event.camel="handleCustomEvent">
    ...
</div>

有时你可能希望监听 camelCase 形式的事件,例如示例中的 customEvent。由于 HTML 属性不支持驼峰命名,因此需要添加 .camel 修饰符,以便 Alpine 在内部将事件名称转换为驼峰形式。

通过在上面示例中添加 .camel,Alpine 现在监听的是 customEvent 而不是 custom-event

.dot

<div @custom-event.dot="handleCustomEvent">
    ...
</div>

.camelCase 修饰符类似,在某些情况下你可能希望监听名称中包含点号的事件(如 custom.event)。由于事件名称中的点号被 Alpine 保留,你需要使用短横线书写并添加 .dot 修饰符。

在上面的代码示例中,custom-event.dot 对应于事件名称 custom.event

.passive

浏览器优化页面滚动以使其快速流畅,即使在页面上执行 JavaScript 时也是如此。然而,实现不当的触摸和滚轮监听器可能会阻塞这种优化,并导致页面性能不佳。

如果你正在监听触摸事件,那么向监听器添加 .passive 以避免阻塞滚动性能非常重要。

<div @touchstart.passive="...">...</div>

→ 阅读更多关于 passive 监听器

.passive.false

在现代浏览器中,触摸和滚轮事件监听器默认是 passive 的。传递 .passive.false 可以使这些事件可取消,以便你可以对其调用 preventDefault

<div @touchmove.passive.false="$event.preventDefault()">...</div>

.capture

如果你希望监听器在事件的捕获阶段执行,即在事件从目标元素向上冒泡到 DOM 之前执行,请添加此修饰符。

<div @click.capture="console.log('I will log first')">
    <button @click="console.log('I will log second')"></button>
</div>

→ 阅读更多关于事件的捕获和冒泡阶段