有条件地在 vue js 中绑定自定义指令以“在元素事件之外单击”
Posted
技术标签:
【中文标题】有条件地在 vue js 中绑定自定义指令以“在元素事件之外单击”【英文标题】:Conditionally bind custom-directives in vue js for 'clicking outside an element event' 【发布时间】:2018-10-21 14:58:27 【问题描述】:参考文献
Add vue directive on condition
Detect click outside element
我正在为元素列表的“click-outside senario”编写自定义指令。
基本上,当在列表中的项目上单击按钮时,它会进入选定模式。现在,如果在其他任何地方发生点击,我需要取消选择模式。 为此,我需要检测 click outside 。 我从中找出了它的指令 为此,我想出了
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
来自上面的参考
现在我只希望每个列表项在该项目处于选定模式时侦听外部点击。
所以我需要完成类似的事情
<div id="list-item" v-on-click-outside="outSideClickHandler" //trigger only when selected>
</div>
<script>
export default
data:
selectedState:false;
,
methods:
outSideClickHandler://......
</script>
【问题讨论】:
【参考方案1】:您为什么不在点击外部处理程序中进行选定的检查?您还需要一种将单击的项目传递给处理程序的方法。
<div id="list-item" v-on-click-outside="outSideClickHandler(item)"></div>
outSideClickHandler(item)
return event =>
if (item.selected)
// Handle the click outside
;
像这样在指令中调用处理程序:
binding.value(event);
您不会像 v-on
那样获得自定义指令的自动“表达式/语句”绑定,这就是为什么当您想要向其传递额外参数时需要对处理函数进行 curry。
我的意思是传递给v-on
的参数可以是表达式或语句,例如:
@click="handler" - handler is an expression (the function itself)
@click="handler(item)" - handler(item) is a statement
但是使用自定义指令你只能传递表达式;除非handler()
返回另一个函数,否则上面的第二行是不可能的。
我认为存在一些混淆,因为您似乎想要一个自定义指令,该指令仅在这种特定情况下与您的列表项一起使用,但我上面的解决方案更多的是关于编写一个通用的“点击外部”指令,该指令您可以在任何情况下使用。
此外,我认为如果未选择列表项(出于性能原因?),您不希望指令注册任何事件侦听器。如果是这种情况,那么您可以改用事件委托。
没有办法有条件地启用/禁用指令,您必须执行Morty's answer 之类的操作,这两者都有些混乱。
这似乎可行,但使用自定义指令的全部意义在于编写可重用的 dom 操作代码
您反对在指令之外编写 DOM 操作代码吗? Angular 1 有这个理念。除非您想在不同的情况下重用该指令,否则为这种情况编写一个指令可能只是为了“DOM 操作代码不会污染我的组件”。如果我要编写一个指令,那么我希望它尽可能通用,以便我可以在许多不同的情况下使用它。
在这种情况下,我什至不需要传递该项目。因为我在 v-for 而不是 div 中有一个组件,并且我将自定义指令绑定到处理程序是方法的那个组件上
那么我不知道你为什么要将它作为一个指令来实现,反正很少需要它。您可以在 mounted
挂钩中注册正文单击事件,然后在 destroyed
挂钩中将其删除。所有的点击外部逻辑都可以包含在组件本身中。
如果您主要关心的是不必为每个列表项注册正文单击事件侦听器,您可以执行以下操作:
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);
,
);
您可以更进一步,当handlers
为非空时自动添加正文单击事件侦听器,并在handlers
为空时删除侦听器。这取决于你。
【讨论】:
似乎outSideClickHandler(item)
将在指令绑定时正确评估。我觉得应该是v-on-click-outside="() => outSideClickHandler(item)"
@MortyChoi 是的,它会,但它会returns another function。
好的。我现在看到了。谢谢。
这似乎可行,但使用自定义指令的全部目的是编写可重用的 dom 操作代码,我虽然通过传递诸如何时激活指令的标志来保持它更通用,但在这种情况下当该标志的值发生变化时,我需要倾听
@SainathS.R 如果您想使用 v-on:click
而不是自定义指令,我的回答与您所做的没有太大区别,所以我不确定您如何认为这是不可重用的DOM 操作代码——它不对应该启用它的情况做任何假设。您是说您希望 enable-if-selected 行为成为指令的一部分吗?这更不通用。【参考方案2】:
目前没有简单的方法来进行条件指令绑定。您可以考虑使用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>
另一种方法是修改指令实现,使其接受另一个活动布尔标志以在 eventListener 中退出。
<div id="list-item" v-on-click-outside="handler: outSideClickHandler, active: item.selected" />
【讨论】:
以上是关于有条件地在 vue js 中绑定自定义指令以“在元素事件之外单击”的主要内容,如果未能解决你的问题,请参考以下文章
vue.js实现内部自定义指令和全局自定义指令------directive