产品:给我全局按钮支持搜索

87 阅读2分钟

方案1

创建一个hooks直接全局引用,然后在需要使用的界面中的按钮上绑定search-button样式,这样监听回车事件之后再去找class为search-button上的click事件,直接调用就可以模拟回车搜索功能

import { onMounted, onActivated, onDeactivated, onBeforeUnmount } from "vue";
export const useKeyEnter = () => {
    let mounted = false
    let removed = false
    let handleKeyDown = (event: any) => {
        if (event.keyCode === 13) {
            event.preventDefault();
            event.stopPropagation();
            let dom: any = document.querySelector('.search-button');
            if (dom) {
                dom.click();
            }
        }
    }
    onMounted(() => {
        if (!mounted) {
            mounted = true
            document.addEventListener('keydown', handleKeyDown)
        }
    })
    onBeforeUnmount(() => {
        if (!removed) {
            removed = true
            document.removeEventListener('keydown', handleKeyDown)
        }
    })
}

但这样做有两个缺点

  1. 范围太广只要页面上有键盘操作就会执行
  2. 会和已有的键盘回车事件发生冲突

方案2

使用a-form包裹表单,然后在a-form上添加回车事件即可,这样做的好处是不会影响到别的页面。

<a-form @keypress.enter="handleSearch">
    ...
</a-form>

但是测试之后发现还是有些bug,比如form表单中如果没有a-input,并不会触发回车事件,可见a-form内部应该是监听了input的回车事件,好吧,既然没有input那我们人为插入一个input,然后再把它隐藏不就行了,于是就有了如下代码:

<a-form @keypress.enter="handleSearch">
    ...
    <a-input style="width:0;padding:0;border:none;opacity:0"/>
</a-form>

但真这样简单可就好了,使用完发现并没有什么卵用,好吧排查了一段时间发现只有input聚焦才会触发enter,那么问题来了怎么才能让input一直聚焦?

<a-form @keypress.enter="handleSearch">
    ...
    <a-input ref="aInputRef" @blur=e=>e.focus() style="width:0;padding:0;border:none;opacity:0"/>
</a-form>

onMounted(()=>{
    unref(aInputRef).focus()
})

逻辑就是开始聚焦,blur之后也执行focus方法即可,好了大功告成,接下来就是每个页面复制粘贴即可,但是思来想去总感觉这种方式很low,有没有更简化的方法?那当然是有的,当我想到要插入一个input的时候,自定义指令这颗种子就早已在我心中生根发芽了

方案3

上文说到a-form中只有input才能触发键盘事件,那既然我们已经插入了input是不是连a-form也不需要了,直接封装一个指令插入到按钮的父节点即可

import { createVNode, render } from "vue";
import { Input } from 'ant-design-vue';
const vEnter = {
    mounted(el, binding) {
        let handleKeyPress = (e) => {
            if (e.keyCode === 13) {
                binding.value()
            }
        }
        // 虚拟dom节点
        let vdom = createVNode(Input, { autofocus: true, onKeypress: handleKeyPress, onBlur: (e) => e.target.focus(), style: { padding: 0, width: 0, opacity: 0, border: 'none' } })
        // 真实节点
        let container = (document as any).createElement('div');
        container.style.height = 0
        // 将虚拟dom挂载到真实节点
        container && render(vdom, container);
        if (el.hideInputContainer) {
            if (el.hideInputContainer.parentNode === el) {
                return
            }
        }
        el.appendChild(container)
        el.hideInputContainer = container
    },
    // 注销
    unmounted(el) {
        if (el.hideInputContainer) {
            if (el.hideInputContainer.parentNode) {
                el.hideInputContainer.parentNode.removeChild(el.hideInputContainer);
            }
        }
    }
}

export const setupEnterDirective = (app: any) => {
    app.directive('enter', vEnter);
};

好了,这样注册完指令后页面上就变成了

<div v-enter="search">
    ...
</div>

简洁的一比,怎么看怎么顺心