Sfoglia il codice sorgente

feat(support-passive-listener-option)

Hugo Di Francesco 5 anni fa
parent
commit
886a43b20c
2 ha cambiato i file con 66 aggiunte e 7 eliminazioni
  1. 9 6
      src/directives/on.js
  2. 57 1
      test/on.spec.js

+ 9 - 6
src/directives/on.js

@@ -1,9 +1,12 @@
 import { kebabCase, debounce, isNumeric } from '../utils'
 
 export function registerListener(component, el, event, modifiers, expression, extraVars = {}) {
+    const options = {
+        passive: modifiers.includes('passive'),
+    };
     if (modifiers.includes('away')) {
         let handler = e => {
-            // Don't do anything if the click came form the element or within it.
+            // Don't do anything if the click came from the element or within it.
             if (el.contains(e.target)) return
 
             // Don't do anything if this element isn't currently visible.
@@ -14,12 +17,12 @@ export function registerListener(component, el, event, modifiers, expression, ex
             runListenerHandler(component, expression, e, extraVars)
 
             if (modifiers.includes('once')) {
-                document.removeEventListener(event, handler)
+                document.removeEventListener(event, handler, options)
             }
         }
 
         // Listen for this event at the root level.
-        document.addEventListener(event, handler)
+        document.addEventListener(event, handler, options)
     } else {
         let listenerTarget = modifiers.includes('window')
             ? window : (modifiers.includes('document') ? document : el)
@@ -29,7 +32,7 @@ export function registerListener(component, el, event, modifiers, expression, ex
             // has been removed. It's now stale.
             if (listenerTarget === window || listenerTarget === document) {
                 if (! document.body.contains(el)) {
-                    listenerTarget.removeEventListener(event, handler)
+                    listenerTarget.removeEventListener(event, handler, options)
                     return
                 }
             }
@@ -53,7 +56,7 @@ export function registerListener(component, el, event, modifiers, expression, ex
                     e.preventDefault()
                 } else {
                     if (modifiers.includes('once')) {
-                        listenerTarget.removeEventListener(event, handler)
+                        listenerTarget.removeEventListener(event, handler, options)
                     }
                 }
             }
@@ -65,7 +68,7 @@ export function registerListener(component, el, event, modifiers, expression, ex
             handler = debounce(handler, wait, this)
         }
 
-        listenerTarget.addEventListener(event, handler)
+        listenerTarget.addEventListener(event, handler, options)
     }
 }
 

+ 57 - 1
test/on.spec.js

@@ -42,6 +42,30 @@ test('nested data modified in event listener updates affected attribute bindings
     await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
 })
 
+test('.passive modifier should disable e.preventDefault()', async () => {
+    document.body.innerHTML = `
+        <div x-data="{ defaultPrevented: null }">
+            <button
+                x-on:touchstart.passive="
+                    $event.preventDefault();
+                    defaultPrevented = $event.defaultPrevented;
+                "
+            >
+                <span></span>
+            </button>
+        </div>
+    `
+
+    Alpine.start()
+
+    expect(document.querySelector('div').__x.$data.defaultPrevented).toEqual(null)
+
+    fireEvent.touchStart(document.querySelector('button'))
+
+    await wait(() => {
+        expect(document.querySelector('div').__x.$data.defaultPrevented).toEqual(false)
+    })
+})
 
 test('.stop modifier', async () => {
     document.body.innerHTML = `
@@ -336,6 +360,39 @@ test('click away', async () => {
     await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
 })
 
+test('.passive + .away modifier still disables e.preventDefault()', async () => {
+    // Pretend like all the elements are visible
+    Object.defineProperties(window.HTMLElement.prototype, {
+        offsetHeight: {
+            get: () => 1
+        },
+        offsetWidth: {
+            get: () => 1
+        }
+    });
+    document.body.innerHTML = `
+        <div x-data="{ defaultPrevented: null }">
+            <button
+                x-on:touchstart.away.passive="
+                    $event.preventDefault();
+                    defaultPrevented = $event.defaultPrevented;
+                "
+            ></button>
+            <span></span>
+        </div>
+    `
+
+    Alpine.start()
+
+    expect(document.querySelector('div').__x.$data.defaultPrevented).toEqual(null)
+
+    fireEvent.touchStart(document.querySelector('span'))
+
+    await wait(() => {
+        expect(document.querySelector('div').__x.$data.defaultPrevented).toEqual(false)
+    })
+})
+
 test('supports short syntax', async () => {
     document.body.innerHTML = `
         <div x-data="{ foo: 'bar' }">
@@ -353,7 +410,6 @@ test('supports short syntax', async () => {
     await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
 })
 
-
 test('event with colon', async () => {
     document.body.innerHTML = `
         <div x-data="{ foo: 'bar' }">