References
Add vue directive on condition
Detect click outside element
I am writing a custom directive for 'click-outside senario' for a list of elements.
Basically when a button is clicked on a item in the list it goes into selected mode . Now if a click occurs anywhere else I need to cancel selection mode . For that I need to detect click outside . I figured out the directive for it from For that I have e up with
const clickOutside = {
bind: function (el, binding, vnode) {
console.log('bind called')
document.body.addEventListener('click', (event) => {
// check that click was outside the el and his childrens
if (!(el == event.target || el.contains(event.target))) {
// and if it did, call handle method provided in attribute value
console.log('directive working')
vnode.context[binding.expression](event);
}
})
},
unbind: function (el) {
document.body.removeEventListener('click', el.event)
console.log('unbind called')
}
}
export {
clickOutside
}
from the reference sited above
Now I only want each list item to listen for outside clicks when that item is in selected mode .
So I need to acplish something like
<div id="list-item" v-on-click-outside="outSideClickHandler" //trigger only when selected>
</div>
<script>
export default{
data:{
selectedState:false;
},
methods:{
outSideClickHandler:{//......}
}
</script>
References
Add vue directive on condition
Detect click outside element
I am writing a custom directive for 'click-outside senario' for a list of elements.
Basically when a button is clicked on a item in the list it goes into selected mode . Now if a click occurs anywhere else I need to cancel selection mode . For that I need to detect click outside . I figured out the directive for it from For that I have e up with
const clickOutside = {
bind: function (el, binding, vnode) {
console.log('bind called')
document.body.addEventListener('click', (event) => {
// check that click was outside the el and his childrens
if (!(el == event.target || el.contains(event.target))) {
// and if it did, call handle method provided in attribute value
console.log('directive working')
vnode.context[binding.expression](event);
}
})
},
unbind: function (el) {
document.body.removeEventListener('click', el.event)
console.log('unbind called')
}
}
export {
clickOutside
}
from the reference sited above
Now I only want each list item to listen for outside clicks when that item is in selected mode .
So I need to acplish something like
<div id="list-item" v-on-click-outside="outSideClickHandler" //trigger only when selected>
</div>
<script>
export default{
data:{
selectedState:false;
},
methods:{
outSideClickHandler:{//......}
}
</script>
Share
Improve this question
asked May 11, 2018 at 8:25
Sainath S.RSainath S.R
3,3069 gold badges42 silver badges73 bronze badges
2 Answers
Reset to default 4Why don't you just do the selected check inside the click outside handler? You'll also need a way of passing the clicked item to the handler.
<div id="list-item" v-on-click-outside="outSideClickHandler(item)"></div>
outSideClickHandler(item) {
return event => {
if (item.selected) {
// Handle the click outside
}
};
}
Call the handler in the directive like this:
binding.value(event);
You do not get the automatic "expression/statement" binding for custom directives like do you with v-on
which is why you need to curry the handler function when you want to pass extra arguments to it.
By this I mean the argument passed to v-on
can be an expression or a statement, such as:
@click="handler" - handler is an expression (the function itself)
@click="handler(item)" - handler(item) is a statement
But with custom directives you can only pass expressions; the second line above is not possible unless handler()
returns another function.
I think there is some confusion because it seems what you want is to have a custom directive which is used only in this specific situation with your list items, but my solution above is more about writing a general "click outside" directive which you can use in any situation.
Also I think you do not want the directive to register any event listeners if the list item is not selected (for performance reasons?). If that's the case, then you can use event delegation instead.
There is no way to conditionally enable/disable a directive, you would have to do something like Morty's answer, both of which is kind of messy.
This seems workable but the whole point of using custom directives is to write reusable dom manipulation code
Are you against writing DOM manipulation code outside of directives? Angular 1 had this philosophy. Unless you want to reuse the directive in different situations then it may be overkill to write a directive for this situation just so that "DOM manipulation code does not pollute my ponent". If I'm going to write a directive, then I would want it to be as general as possible so that I can use it in many different situations.
I don't even need to pass the item in that case. Cause I have a ponent inside a v-for and not a div and I bind the custom directive on that ponent of which the handler is a method
Then I'm not sure why you'd want to implement this as a directive, which is rarely needed anyway. You can just register the body click event in the mounted
hook and remove it in the destroyed
hook. All of the click-outside logic can be contained within the ponent itself.
If your main concern is not having to register a body click event listener for each list item, you can do something like this:
const handlers = new Map();
document.addEventListener('click', e => {
for (const handler of handlers.values()) {
handler(e);
}
});
Vue.directive('click-outside', {
bind(el, binding) {
const handler = e => {
if (el !== e.target && !el.contains(e.target)) {
binding.value(e);
}
};
handlers.set(el, handler);
},
unbind(el) {
handlers.delete(el);
},
});
You can go one step further and automatically add the body click event listener whenever handlers
is nonempty and remove the listener when handlers
is empty. It's up to you.
Currently there is no easy way to do conditional directive binding. You might considered using v-if
.
<div v-for='item of list'>
<div
v-if='item.selected'
id="list-item"
v-on-click-outside="outSideClickHandler"
/>
<div v-else
id="list-item"
v-on-click-outside="outSideClickHandler"
/>
</div>
Another approach would be modifying you directive implementation, so that it accepts another active boolean flag to opt out inside the eventListener.
<div id="list-item" v-on-click-outside="{handler: outSideClickHandler, active: item.selected}" />
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745478397a4629451.html
评论列表(0条)