Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for modifiers to event handlers #1088

Closed
tomcon opened this issue Jan 9, 2018 · 8 comments
Closed

Add support for modifiers to event handlers #1088

tomcon opened this issue Jan 9, 2018 · 8 comments

Comments

@tomcon
Copy link

tomcon commented Jan 9, 2018

A (relatively) easy and useful enhancement to Svelte would be to adopt one or more of the 5 Vue ideas in this area.

i.e.

  1. .stop
  2. .prevent
  3. .capture
  4. .self
  5. .once

"It is a very common need to call event.preventDefault() or event.stopPropagation() .... "
https://vuejs.org/v2/guide/events.html#Event-Modifiers

<!-- the click event's propagation will be stopped -->
<a on:click.stop="doThis"></a>

<!-- the submit event will no longer reload the page -->
<form on:submit.prevent="onSubmit"></form>

<!-- modifiers can be chained -->
<a on:click.stop.prevent="doThat"></a>

<!-- just the modifier -->
<form on:submit.prevent></form>

<!-- use capture mode when adding the event listener -->
<!-- i.e. an event targeting an inner element is handled here after being handled by that element -->
<div on:click.capture="doThis">...</div>

<!-- only trigger handler if event.target is the element itself -->
<!-- i.e. not from a child element -->
<div on:click.self="doThat">...</div>
@Rich-Harris
Copy link
Member

I like those. At present you can do

<a on:click="event.stopPropagation()"></a>

<!-- the submit event will no longer reload the page -->
<form on:submit="event.preventDefault()"></form>

but that's it.

One consideration that applies to any syntax change is that we wouldn't then be able to use that syntax for anything else, e.g. if we want to do something like namespaced event handlers in future...

<input on:keys.tab='advance()' on:keys.enter='submit()' on:keys.escape='cancel()'>

<script>
  function keyHandler(which) {
    return (node, callback) => {
      function handleKeydown(event) {
        if (event.which !== which) return;
        callback(event);
      }
  
      node.addEventListener('keydown', handleKeydown);
  
      return {
        teardown() {
          node.removeEventListener('keydown', handleKeydown);
        }
      };
    };
  }

  export default {
    events: {
      keys: {
        tab: keyHandler(9),
        enter: keyHandler(13),
        escape: keyHandler(27)
      }
    }
  };
</script>

...we'd be stuffed. (This is one reason I prefer to move at a speed people often find excruciatingly slow when it comes to introducing new APIs, by the way! These conflicts often reveal themselves only gradually, in my experience.)

Indeed, I'm not sure that a namespace-like syntax makes that much sense in this context. Maybe there's a different character we could use — maybe the colon again?

<!-- modifiers can be chained -->
<a on:click:stop:prevent="doThat()"></a>

@tomcon
Copy link
Author

tomcon commented Jan 9, 2018

(This is one reason I prefer to move at a speed people often find excruciatingly slow when it comes to introducing new APIs, by the way! These conflicts often reveal themselves only gradually, in my experience.)

Good point. You're the man Rich and also have the experience to know what is best. Slow is good!

Indeed, I'm not sure that a namespace-like syntax makes that much sense in this context

I'd agree. Re. your example, interestingly was going to add the link to the key modifiers in this item but didn't, it seems elegant too?
https://vuejs.org/v2/guide/events.html#Key-Modifiers

@Rich-Harris
Copy link
Member

Would definitely be keen to move forward with this in the near future, as it comes up from time to time. Had a thought — would it be crazy to use the pipe character here?

<a on:click|stop|prevent="doThat()"></a>

It sort of works as a metaphor for what's happening — the click event has to flow through the 'stop' and 'prevent' steps on the way to doThat — and avoids the problem of overloading the : or . characters.

@Conduitry
Copy link
Member

I think that seems nice - it would be good to use a different character than colon for this. One question I do have (I don't know whether we'd need to consider this right away, but it might make sense to) is whether/how we want to support modifiers on custom DOM events. Or (somewhat more tenuous) on component events.

@Rich-Harris
Copy link
Member

Easiest thing would definitely be to disallow modifiers for custom and component events. I guess in theory you could support them in custom events like this?

events: {
  longpress: (node, callback, modifiers) => {
    // modifiers is an array like ['stop', 'capture']
    // ...
  }
}

I would definitely file that under YAGNI to begin with though.

@Rich-Harris
Copy link
Member

Finally added in 2.15 — demo here.

We can use the preventDefault, stopPropagation, once, capture and passive modifiers.

passive only applies to touch/wheel events, and is applied automatically where it makes sense.

once and passive aren't implemented in legacy mode — the compiler will throw an error in this situation. That could be fixed in future.

@CAJazzer
Copy link

Feature Request: Key Modifiers for event handlers

<a on:keyup.delete|preventDefault={onDeleteDoStuff}

@Conduitry
Copy link
Member

An old, closed issue is not the best place to raise a feature request.

I'm also not particularly sold on the idea of modifiers for key events - I think I'd prefer just doing this manually, since it's easy to have inline expressions in the handler that test for key codes. If you want to have separate handlers for different keys, that would also be made a little tidier with the proposal in #2688 / #2709.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants