前段時間在用iView作個項目,其中須要使用自定義的右鍵菜單,而後去官網找了一下,發現有個Dropdown的組件,便想着能不能用來作個右鍵菜單的組件javascript
你可能須要對iView有必定的使用經驗
Dropdown的使用大概是這個樣子css
<template> <Dropdown> <a href="javascript:void(0)"> 下拉菜單 <Icon type="ios-arrow-down"></Icon> </a> <DropdownMenu slot="list"> <DropdownItem>驢打滾</DropdownItem> <DropdownItem>炸醬麪</DropdownItem> <DropdownItem disabled>豆汁兒</DropdownItem> <DropdownItem>冰糖葫蘆</DropdownItem> <DropdownItem divided>北京烤鴨</DropdownItem> </DropdownMenu> </Dropdown> </template> <script> export default { } </script>
發現有個觸發元素slot
,能夠自定義的插入元素,我一想,只要把slot
的內容設置爲position: fixed
,在右鍵的時候給它實時設置一下鼠標所在的位置不就好了嘛,而後一頓搗騰vue
<template> <Dropdown transfer placement="right-start" trigger="custom" :visible="currentVisible" @on-clickoutside="handleCancel" > <div :style="locatorStyle"></div> <DropdownMenu slot="list"> <DropdownItem>驢打滾</DropdownItem> <DropdownItem>炸醬麪</DropdownItem> <DropdownItem disabled>豆汁兒</DropdownItem> <DropdownItem>冰糖葫蘆</DropdownItem> <DropdownItem divided>北京烤鴨</DropdownItem> </DropdownMenu> </Dropdown> </template> <script> export default { data () { return { posX: 0, posY: 0, currentVisible: false } }, computed: { locatorStyle () { return { position: 'fixed', left: `${this.posX}px`, top: `${this.posY}px` } } }, methods: { handleContextmenu ({ button, clientX, clientY }) { if (button === 2) { if (this.posX !== clientX) this.posX = clientX if (this.posY !== clientY) this.posY = clientY this.currentVisible = true } }, handleCancel () { this.currentVisible = false } }, mounted () { document.addEventListener('contextmenu', this.handleContextmenu, true) document.addEventListener('mouseup', this.handleContextmenu, true) }, destroyed () { document.removeEventListener('contextmenu', this.handleContextmenu, true) document.removeEventListener('mouseup', this.handleContextmenu, true) } } </script>
看上去很不錯,而後興高采烈地一試,發現不管怎麼點,菜單始終定位在右上角java
slot
的元素位置確實發生了變化,然而菜單位置始終不變化
這可把我折騰了半天,也沒弄出個結果。抱着 極不情願 一探究竟的心情,我打開了Dropdown
的源碼ios
<template> <div :class="[prefixCls]" v-click-outside="onClickoutside" @mouseenter="handleMouseenter" @mouseleave="handleMouseleave"> <!-- 注意此處 --> <div :class="relClasses" ref="reference" @click="handleClick" @contextmenu.prevent="handleRightClick"><slot></slot></div> <transition name="transition-drop"> <Drop :class="dropdownCls" v-show="currentVisible" :placement="placement" ref="drop" @mouseenter.native="handleMouseenter" @mouseleave.native="handleMouseleave" :data-transfer="transfer" :transfer="transfer" v-transfer-dom><slot name="list"></slot></Drop> </transition> </div> </template> <script> // 如下省略 </script>
能夠看到標註的地方,slot
的外層還有個div
,而Dropdown
的定位是依賴於外層的這個div
的,因此不管你slot
裏的內容位置,在初始化以後再怎麼變化,都不會影響到組件的位置了(也有多是position: fixed
的影響)git
發現slot
外層的div
有一個ref="reference"
的屬性
忽然有了想法,我是否是能夠直接經過Dropdown
的refs
直接把整個外層div
替換掉,因而繼續搗騰,改造了一下github
<template> <Dropdown transfer placement="right-start" trigger="custom" ref="contextMenu" :visible="currentVisible" @on-clickoutside="handleCancel" > <DropdownMenu slot="list"> <DropdownItem>驢打滾</DropdownItem> <DropdownItem>炸醬麪</DropdownItem> <DropdownItem disabled>豆汁兒</DropdownItem> <DropdownItem>冰糖葫蘆</DropdownItem> <DropdownItem divided>北京烤鴨</DropdownItem> </DropdownMenu> </Dropdown> </template> <script> export default { data () { return { posX: 0, posY: 0, currentVisible: false, locator: null } }, methods: { createLocator () { // 獲取Dropdown const contextmenu = this.$refs.contextMenu // 建立locator const locator = document.createElement('div') locator.style.cssText = `position:fixed;left:${this.posX}px;top:${this.posY}px` document.body.appendChild(locator) // 將locator綁定到Dropdown的reference上 contextmenu.$refs.reference = locator this.locator = locator }, removeLocator () { if (this.locator) document.body.removeChild(this.locator) this.locator = null }, handleContextmenu ({ button, clientX, clientY }) { if (button === 2) { if (this.posX !== clientX) this.posX = clientX if (this.posY !== clientY) this.posY = clientY if (this.trigger !== 'custom') { this.createLocator() this.currentVisible = true } } }, handleCancel () { this.currentVisible = false this.removeLocator() } }, mounted () { document.addEventListener('contextmenu', this.handleContextmenu, true) document.addEventListener('mouseup', this.handleContextmenu, true) }, destroyed () { document.removeEventListener('contextmenu', this.handleContextmenu, true) document.removeEventListener('mouseup', this.handleContextmenu, true) } } </script>
根據鼠標的位置實時建立一個position: fixed
的div
,經過給Dropdown
添加ref
屬性,獲取到Dropdown
對象以後再經過$ref
屬性將div
賦值到reference
app
大功告成,如今Dropdown
會根據鼠標所在的位置出現啦iview
最後把一些點擊的回調方法補全,就是一個像樣的右鍵菜單組件了dom
固然做爲一個能夠複用的組件,還須要把一些通用邏輯再提取出來,以及補全一些經常使用的API,具體代碼能夠參考這個倉庫