Browse Source

Fixes @keyup and @keydown listeners with .capture modifier never executed (#3398)

* Fixed x-on .capture test to actually test the order of listener executions

* Added tests for x-on .capture in conjunction with keyup events

* Fixed .capture modifier ignored in keyup/keydown event listeners

* Added docs for x-on .capture modifier
mgschoen 2 years ago
parent
commit
2a859bb177

+ 1 - 1
packages/alpinejs/src/utils/on.js

@@ -104,7 +104,7 @@ function isKeyEvent(event) {
 
 function isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers) {
     let keyModifiers = modifiers.filter(i => {
-        return ! ['window', 'document', 'prevent', 'stop', 'once'].includes(i)
+        return ! ['window', 'document', 'prevent', 'stop', 'once', 'capture'].includes(i)
     })
 
     if (keyModifiers.includes('debounce')) {

+ 13 - 0
packages/docs/src/en/directives/on.md

@@ -298,3 +298,16 @@ If you are listening for touch events, it's important to add `.passive` to your
 ```
 
 [→ Read more about passive listeners](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners)
+
+### .capture
+
+Add this modifier if you want to execute this listener in the event's capturing phase, e.g. before the event bubbles from the target element up the DOM.
+
+```
+<div @click.capture="console.log('I will log first')">
+    <button @click="console.log('I will log second')"></button>
+</div>
+```
+
+[→ Read more about the capturing and bubbling phase of events](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#usecapture)
+

+ 35 - 2
tests/cypress/integration/directives/x-on.spec.js

@@ -99,8 +99,8 @@ test('.stop modifier',
 
 test('.capture modifier',
     html`
-        <div x-data="{ foo: 'bar' }">
-            <button @click.capture="foo = 'baz'">
+        <div x-data="{ foo: 'bar', count: 0 }">
+            <button @click.capture="count = count + 1; foo = 'baz'">
                 <h1>h1</h1>
                 <h2 @click="foo = 'bob'">h2</h2>
             </button>
@@ -110,6 +110,39 @@ test('.capture modifier',
         get('div').should(haveData('foo', 'bar'))
         get('h2').click()
         get('div').should(haveData('foo', 'bob'))
+        get('div').should(haveData('count', 1))
+    }
+)
+
+test('.capture modifier with @keyup',
+    html`
+        <div x-data="{ foo: 'bar', count: 0 }">
+            <span @keyup.capture="count = count + 1; foo = 'span'">
+                <input type="text" @keyup="foo = 'input'">
+            </span>
+        </div>
+    `,
+    ({ get }) => {
+        get('div').should(haveData('foo', 'bar'))
+        get('input').type('f')
+        get('div').should(haveData('foo', 'input'))
+        get('div').should(haveData('count', 1))
+    }
+)
+
+test('.capture modifier with @keyup and specified key',
+    html`
+        <div x-data="{ foo: 'bar', count: 0 }">
+            <span @keyup.enter.capture="count = count + 1; foo = 'span'">
+                <input type="text" @keyup.enter="foo = 'input'">
+            </span>
+        </div>
+    `,
+    ({ get }) => {
+        get('div').should(haveData('foo', 'bar'))
+        get('input').type('{enter}')
+        get('div').should(haveData('foo', 'input'))
+        get('div').should(haveData('count', 1))
     }
 )