Open jakedohm opened 4 years ago
How would you then register a normal @click
event on the element that has this event handler?
Example
<button @click="onClick" @click.away="onClickAway" />
Hmm, great question. You should be able to register both, just like that, depending on if Vue's internals will allow that to happen. Because: the @click.outside
doesn't actually register a click handler on the element, it registers one on the window/document, so there's not a conflict there.
I like the idea and I have used the package you mentioned myself in numerous occasions, but I think this would be better if it was included as a directive and not a modifier to the @click
event.
Lets see what others say about this. Thanks for the rfc, I think it will spike up some good discussion :)
I like the idea. But, I'm also unsure about it because v-click-outside
is just one additional character than @click.outside
and actually reflects better what is happening behind the scenes (it's not attaching an event listener to the element).
On the other hand, I find @click.outside
more intuitive since I'd already be thinking about events (not directives) when thinking about adding this logic to an element. I also find in my own habits I am more likely to write this logic myself than bring in a third-party library for a simple directive (maybe just me, not sure if anyone else is similar). It would be nice to have something (directive or modifier) built in for this, since it is so common. That way it can be covered in documentation, so users don't feel the need to find a third-party library or write the logic themselves.
I'd also add, having this functionality built in probably makes sense with the new built-in <Teleport>
component. They sort of go hand-in-hand:
<teleport to="#endofbody" v-if="showModal">
<div id="content" v-click-outside="showModal = false">
<p>
this will be moved to #endofbody.<br />
Pretend that it's a modal
</p>
<Child />
</div>
</teleport>
@aztalbot good points 😄. I agree that I'd often write this logic myself vs pulling in a third-party library. I actually did it this morning in a Vue 3 (Beta) project. For me, it's definitely less about the amount of characters, or even library weight (since vue-click-outside is 1kb
) and more about developer experience. It's a proven common use-case that IMO makes sense to solve out of the box.
@bangjelkoski I don't really love using a directive for this, because this use-case is so close to being the same as handling a normal click event. We've already got a way to manage events, and we've already got modifiers. In my mind this is a valid use-case of an event+modifier.
The idea sounds nice but I'm not sure it's worth it.
Disclaimer: I do use (my own) v-click-outside
as a fundamental block for controls in my application.
v-click-outside
isn't bad. Not every useful directive should have special support in Core, what makes this one worthy of a special treatment?
Semantically it's not correct and it's gonna require twists in Vue compiler.
@click
adds listeners to the click event of its target element. Modifiers are applied to the handler function. Check the codegen here
This is not where @click.away
needs to go. The directive is actually attaching/removing a click handler on the document on mount / unmount.
This might be best served by user-land libraries because there are several useful variations that make its behavior not-so-well-defined for something in core.
For example: it's common to combine the clicking outside with a focus outside. E.g. if you write the popup of a dropdown. When the user tabs away you want to dismiss it and it's nice if your v-click-outside
is able to handle both.
Other example: sometimes I have two distinct elements that are the "click outside" target and my v-click-outside
supports that. This happens if you can't/don't want to put your popup in the same container as the element targets it (e.g. an input control).
Other considerations: is this a click
or mousedown
? Do you take into account touch, maybe pointerdown
? Is it a misnomer then? How far back do you want to support browsers? Or is this modifier gonna apply to other events as well?
(That last bit might make it more interesting for core, although I'm not sure if there are many use cases.)
@jods4 you're last comment is interesting. Do you think it might make more sense to provide a basic building block then, like a v-outside
directive that attaches an event listener to document and only fires if it occurred outside the element? That would be fairly simple to support, and the user would then be able to choose whether to ignore if event came from a second element, or to filter out certain events. It could also be something like v-outside:click
and v-outside:focus
, but then that's going beyond a basic building block.
@aztalbot That's an interesting primitive to have!
Although to be honest I can't come up with much uses besides focusin
and pointerdown
that I use to build v-click-outside
.
To be worth it, we'd need more use cases, otherwise building v-click-outside
directly is enough. Maybe someone else is more creative?
In the realm of "interesting ideas": Vue compiler is extensible you can create plugins.
I don't know if it's flexible enough that creating a userland .outside
modifier would be possible.
Unrelated spam: extending modifiers opens up stuff like a
.delegate
modifier, something that I would find interesting in core.
I was also thinking what about a @focus.outside
.. and for that matter, would @mouseover.outside
or @keydown.outside
make sense... are there any events that don't make sense?
For focus/blur specifically, there's the caveat that focusin
and focusout
bubble while focus
and blur
do not.. so would @focus.outside
have to be smart enough to be a @focusin.outside
or would it be an obscure user trap that does nothing and causes frustration?
I think this RFC brings up interesting things to solve:
<teleport>
).. ideally tabbing from the button takes the user to the menu, regardless of where it is in the dom.. and tabbing past the menu goes to the element after the button and also closes the menuI agree that .outside
is semantically incorrect and I think the caveats make it potentially confusing and inconsistent with how people would normally use @focus
and @blur
.
That said, there's an obvious need to be able to register events outside the DOM of your component in order to even begin to solve these types of issues.
I could see it being a core feature since it's something that every component of a certain type of component would need to care about. Like every dialog/modal/tooltip/dropdown component should care about this.. and if my dialog component is using one lib and dropdown another and tooltip makes up its own solution then that bloats my final app with 3 solutions for the same thing.
There may also be missed performance opportunities... is it better to register a single event handler on the body and iterate through which callbacks it needs to fire or better to register one per callback? I believe jquery still does the former so I assume that's still the better option.
Perhaps a low-level core solution is to simply allow adding event handlers to the body (I don't believe I've seen anything for this as of yet)... and the "filtering" of whether they're inside or outside of your component is up to the implementation so userland could build on top of it to provide inside/outside functionality or likely other useful things as well (game controls come to mind).
I'm also wondering if we'd need to add event handlers to things other than body, but I'm not having a use case come to mind for it.
I'm divided.
Like many others, I'm using v-click-outside. And a @click.outside
seem interesting.
But my main concern is that the modifiers are supposed to alter the event that is attached to the element.
@click.prevent
means "apply the prevent
modifier to the onclick listener"
@click.foobar
means "apply the foobar
modifier to the onclick listener"
But
@click.outside
would mean "attach the click listener to the root document"
FWIW I'd love to see someones implementation of vue-clickaway
(maybe @jods4 ?). I'm using vue 3 RC and that no longer appears to work. I have spent a bit of time digging into how to get it working but to no avail.
@9mm are you in discord? Nag me over there and I'll share what I have with you when I'm at work.
1 and half years later and this is still open? IMHO this modifier should definitely exist. Using Alpinejs for this is super handy and I really miss this in Vue. I don't like to install small packages like that in real applications even though they do a really good job on this, but I don't want to have one more tiny package to audit against security rules in a company for open source projects and dependencies, especially in a growing hostile environment for npm packages. I can't see a package with Vue that doesn't use such a feature. This is simply needed for dropdown elements and floating menus which basically EVERY application has.
I'd love to contribute, but unfortunately, I have no idea where to start and don't have enough time for this right now. But I'd like to leave my feedback on this issue.
By the way, thank you for the awesome framework. 👍🏻 🚀
I don't like installing too many packages either, but it's not a great argument. It is not good for Vue to merge every single small package out there into core. For example everything in Vueuse is pretty small and useful to some people, yet I think we would agree Vueuse should remain its own package.
v-click-outside can be so small that if you don't want to take a dep. on a package, you could just write the code directly in your app. Thorsten has a basic click outside directive in less than 20 lines of JS here: https://jsfiddle.net/Linusborg/yzm8t8jq/
I raised the concern before that the semantics of a useful click-outside might be not so well defined for core inclusion.
To contrast with that 20 lines example, the v-click-outside
npm implementation is 130+ lines and has code to handle touch events and iframes...
https://github.com/ndelvalle/v-click-outside/blob/master/src/v-click-outside.js
For this to make it into core Vue, I think the first step is to discuss the specifications of what we want .outside
to be, exactly.
Proposal
I would love to be able to write either
@click.outside
to handle any click outside of an element. This is a common use-case for things like modals or dropdowns that should close when you click away from them. The demand for this has already been proved by v-click-outside which has 60,000 weekly downloads on NPM.This seems like a trivial thing, but I think it would improve the DX of working with Vue.
Naming
I think
@click.outide
is the most intuitive API. Alpine.js went with@click.away
but when I asked the author why, he said he wasn't sure and that "outside might honestly be better" (source)API
I think a simple API like
@click.outside="handleOuterClick"
makes sense. I'm not sure if there's enough value in adding some of the options that v-click-outside has to warrant the larger API surface area, and kinda awkward API. For more advanced/complex use-cases people can alwaysPrior Art
@click.away
: https://github.com/alpinejs/alpine#x-onv-click-outside
: https://github.com/ndelvalle/v-click-outsideIf this gets support, core team members are okay with it, then I'm happy to take a crack at an implementation and PR it!