在項目中往往會有這樣的需求: 彈出框(或Popover)在 show 后,點擊空白處可以將其 hide。
針對此需求柠衍,整理了三種實現(xiàn)方式,大家按實際情況選擇晶乔。
當然珍坊,我們做項目肯定會用到 UI 框架,常見的 Element 中的組件提供了這樣的方法正罢。
但是阵漏,就算使用框架,有些時候還是要用到的翻具,比如:
Element 中的 Popover履怯,當我們想使用手動方式(trigger 觸發(fā)方式為 manual時)控制它的 show & hide 的時候,就要自己實現(xiàn)這個功能啦裆泳。
第一種方式:最普通的手動監(jiān)聽判斷
<span ref="projectButton">
<el-popover v-model="visible" trigger="manual" placement="bottom" @show="show" @hide="hide">
<p>啦啦啦</p>
<el-button slot="reference" type="primary" @click="visible = !visible">show</el-button>
</el-popover>
</span>
data () {
return {
visible: false
}
},
methods: {
show () {
document.addEventListener('click', this.hidePanel, false)
},
hide () {
document.removeEventListener('click', this.hidePanel, false)
},
hidePanel (e) {
if (!this.$refs.projectButton.contains(e.target)) {
this.visible = false
this.hide()
}
}
}
上面就是在 Popover show 的時候監(jiān)聽 document 的 click 事件虑乖,觸發(fā)進入 hidePanel 方法,判斷當前點擊的 el 是否在 Popover 內部晾虑,如果不在疹味,則手動 hide Popover ,并且移除監(jiān)聽事件帜篇。
這個還是蠻好理解的糙捺,我使用的也是這種方式,因為我的項目中需要這種需求的很少(好吧笙隙,就一個地方)洪灯,所以我采用了這種方式。
第二種方式: 指令
<template>
<div>
<div class="show" v-show="show" v-clickoutside="handleClose">
顯示
</div>
</div>
</template>
<script>
const clickoutside = {
// 初始化指令
bind(el, binding, vnode) {
function documentHandler(e) {
// 這里判斷點擊的元素是否是本身竟痰,是本身签钩,則返回
if (el.contains(e.target)) {
return false;
}
// 判斷指令中是否綁定了函數(shù)
if (binding.expression) {
// 如果綁定了函數(shù) 則調用那個函數(shù),此處binding.value就是handleClose方法
binding.value(e);
}
}
// 給當前元素綁定個私有變量坏快,方便在unbind中可以解除事件監(jiān)聽
el.__vueClickOutside__ = documentHandler;
document.addEventListener('click', documentHandler);
},
update() {},
unbind(el, binding) {
// 解除事件監(jiān)聽
document.removeEventListener('click', el.__vueClickOutside__);
delete el.__vueClickOutside__;
},
};
export default {
name: 'HelloWorld',
data() {
return {
show: true,
};
},
directives: {clickoutside},
methods: {
handleClose(e) {
this.show = false;
},
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.show {
width: 100px;
height: 100px;
background-color: red;
}
</style>
上面這種是網上比較火的一種方式(實際我沒有測試過)铅檩,其實思路還是那個思路 (給document增加一個click事件監(jiān)聽,當發(fā)生click事件的時候判斷是否點擊的當前對象莽鸿,不是就隱藏)昧旨,優(yōu)點就是可以封裝成全局/局部的指令拾给,可多處使用。
下面簡單介紹下 vue 指令
一個指令定義對象可以提供如下幾個鉤子函數(shù) (均為可選):
- bind:只調用一次兔沃,指令第一次綁定到元素時調用蒋得。在這里可以進行一次性的初始化設置。
- inserted:被綁定元素插入父節(jié)點時調用 (僅保證父節(jié)點存在乒疏,但不一定已被插入文檔中)额衙。
- update:所在組件的 VNode 更新時調用,但是可能發(fā)生在其子 VNode 更新之前怕吴。指令的值可能發(fā)生了改變入偷,也可能沒有。但是你可以通過比較更新前后的值來忽略不必要的模板更新 (詳細的鉤子函數(shù)參數(shù)見下)械哟。
- componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調用疏之。
- unbind:只調用一次,指令與元素解綁時調用暇咆。
第三種方式:遮罩
<template>
<div>
<div class="mask" v-if="showModal" @click="showModal=false"></div>
<div class="pop" v-if="showModal">
<button @click="showModal=false" class="btn">點擊出現(xiàn)彈框</button>
</div>
<button @click="showModal=true" class="btn">點擊出現(xiàn)彈框</button>
</div>
</template>
<script>
export default {
data() {
return {
showModal: false
};
}
};
</script>
<style scoped>
.mask {
background-color: #000;
opacity: 0.3;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1
}
.pop {
background-color: #fff;
position: fixed;
top: 100px;
left: 300px;
width: calc(100% - 600px);
height:calc(100% - 200px);
z-index: 2
}
.btn {
background-color: #fff;
border-radius: 4px;
border: 1px solid blue;
padding: 4px 12px;
}
</style>
上面這個就是添加一個看不見的遮罩替代 document 锋爪,點擊遮罩就隱藏。但是要注意:mask(遮罩)層的層級(z-index)要比彈出的pop的層級低爸业。