需求:
老板希望通过右键的菜单选项,来实现对数据的操作,其中有一个菜单的功能---重命名需要默认将input中的文字全部选中,并且输入时input样式也会变。
我的解决方案:
生成一个menu,通过鼠标右键传递的事件对象控制menu的位置
基本操作:
因为是第一次做这个功能,我的menu内部是嵌套的原生input标签,涉及到了很多问题
1.需要阻止浏览器默认的右击行为(弹出默认的菜单)---@contextmenu.prevent
2.@contextmenu鼠标右击的事件---实际上1.2可以合在一起
3.菜单的动态位置(希望用户只要在限定区域内,菜单永远保持在鼠标右下角)
通过vue结合内链样式---:style="`position: absolute;top: ${topVal}px;left:${leftVal}px;`"
是不是感觉很简单!很快就完成了一个菜单
//主体的html代码
//每一个v-row都是一个选项,注意这并不是菜单选项,而是单纯的一个列表,待交互的(这里我就只截取主要)
<v-row
v-for="(page, index) in pages"
:key="page.id"
class="item"
tabindex="0"
@contextmenu.prevent
@contextmenu="handleRightClick($event, index, page)"
@click="onPageClick(page.id)"
>
<v-col class="item-name">
<input
ref="myInput"
class="text-input-default"
:readonly="isdisabled"
:value="page.name"
@blur="blurInput"
@keydown="inputKeydown"
/>
</v-col>
------------------------------------------------
//菜单部分代码实现
<p-menu
:style="`position: absolute;top: ${topVal}px;left:${leftVal}px;`"
:model-value="控制这个菜单的显示与隐藏"
:items="菜单中的选项"
></p-menu>
在input标签内部禁用输入状态,这里之所以不使用disabled而是使用readonly是因为一个需求:老板希望用户右键时菜单弹出,但是input标签在禁用状态下,是无法触发事件(click等等),所以导致无法触发事件去控制menu菜单的显示,所以这里选择了readonly。
bug以及知识点
知识点:
1.整体功能是实现是利用vue实现的,涉及到input获取焦点和失去的焦点时样式的切换问题,并且还牵扯到一个问题,样式的切换应该采用切换焦点样式来做即(获取焦点时是一个样式,失去焦点时就恢复默认样式)但是在实际场景中发现,用户输入的只是input标签,但是样式改变却是外层的div所以,我们应该通过原生js的形式去删除和添加样式,那么这里就涉及到了通过ref选择对应的dom元素
// 通过ref获取到指定的祖先(父)元素dom---除了可以获取class以外id和标签名也是可以获取到dom的
myInput.value[selsectIndex.value].closest('.item')
// js的方式添加样式(.classList.add) 这里的selectFocus就是样式
myInput.value[selsectIndex.value].closest('.item').classList.add('selectFocus');
// js的形式取消样式(.classList.remove)
myInput.value[selsectIndex.value].closest('.item').classList.remove('selectFocus');
//
2.通过ref获取到当前dom然后通过类获取到祖先元素的dom
// 其中.item是祖先元素class属性,当然你也可以通过标签等形似
myInput.value[selsectIndex.value].closest('.item')
3.用户点击菜单选项(重命名后,input文本框内容默认全选并自动获取焦点立刻输入)
// 这里展示的是通过dom实现
myInput.value[selsectIndex.value].focus();// 当前dom获取焦点
myInput.value[selsectIndex.value].select();// 当前dom文本内容默认全选!
bug:
1.用户点击更改名称后,确实是高亮了,但是input失去焦点后居然还在高亮
解决方案:使用 window.getSelection().removeAllRanges()
方法清除选中的文本范围。
//解决bug (通过select()全选文字,当失去焦点后,这个默认的样式依然高亮)
window.getSelection().removeAllRanges();
2.文本框默认获取焦点和文字全选,不生效
解决方案:1.在setTimeout中执行即可
2.老板给我说,最好别用setTimeout,因为它的执行时机不确定,可以用nextTick来做,确实也是一样的
// 这里的问题是,第一次重命名无法全选并且获取不了焦点(不执行)
// 添加setTimeout可以
setTimeout(() => {
myInput.value[selsectIndex.value].focus();
myInput.value[selsectIndex.value].select();
}, 0);
-----------------------------------
老板建议我通过切换span 和input来做,想了想确实,在单个标签中判断太多了,还是切换两个标签,一个输入一个展示(菜单逻辑还是一样,就是input标签哪里,改成与span切换)
1.同时生成input和span标签,通过切换父元素的class类,完成display的none和block,可以提升性能,不用在频繁的操作js来实现样式控制
2.项目中通过.closest去获取祖先元素的dom更是不行,它会去遍历整体的效率不好,还是直接绑定一个父的ref来控制