项目打包后,触屏设备无法唤醒键盘

最近做了一个 vue2 + vant 的项目。遇到了一个很奇怪的问题,输入框有时候无法唤醒键盘。
通过 deepseek 的帮助,得到了一些解决方案。

测试的代码如下,这两个框都可以正常唤醒键盘

        <input
          value="input1"
          @pointerdown="handlePointerDown"
        />
        <input
          value="input2"
          ref="myInput"
          @click="forceFocus"
          @touchstart="forceFocus"
        />
{
  methods: {
    forceFocus() {
      this.$refs.myInput.focus();
      // 额外延迟触发,确保 Windows 接收事件
      setTimeout(() => {
        this.$refs.myInput.focus();
      }, 100);
    },
    handlePointerDown(e) {
      e.preventDefault();
      e.target.focus();
    },
  }
}

原因分析:
可能还是因为 input 框没有实际的 focus ,或者是 focus 后系统卡顿,导致又失去焦点了。导致系统没有识别到需要输入的操作,所以才有了e.preventDefault() 与延时再次进行 focus

根据上面的代码,将这部分代码写成一个指令
仅适合输入框少的情况,这事件监听过多,存在效率问题

export default {
  inserted(el) {
    const input = el.querySelector('input');
    if (input) {
      el.addEventListener('pointerdown', (e) => {
        e.preventDefault();
        input.focus();
        setTimeout(() => input.focus(), 50);
      });
    }
  }
}

再 main.js 中引入这个指令

import Vue from 'vue'
import focusTouch from '@/utils/directives/focusTouch'
Vue.directive('focus-touch', focusTouch)

在 vue 中使用这个指令

        <van-field
          v-model="value"
          v-focus-touch
          clearable
          clickable
          name="name"
          label="姓名"
          placeholder="请输入姓名"
        />

======= 更新 =======

上面代码经过测试会出现一个问题,当页面 input 框过多,可能会导致卡顿,或者失灵,现在增加了一个更简洁的方案

使用新的指令,该指令只需要在根元素上绑定一次,全局生效。不需要给每个元素再次绑定了

// directives/focusTouch.js
export default {
  inserted(el) {
    const handlePointerDown = (e) => {
      // 只处理 input/textarea 且未被禁用
      const target = e.target.closest('input:not(:disabled), textarea:not(:disabled)');
      if (target) {
        if (document.activeElement !== target) {
          e.preventDefault();
          target.focus({ preventScroll: true }); // 防止意外滚动
        }
      }
    };

    el.addEventListener('pointerdown', handlePointerDown, { passive: false });
    el._focusTouchHandler = handlePointerDown;
  },
  unbind(el) {
    if (el._focusTouchHandler) {
      el.removeEventListener('pointerdown', el._focusTouchHandler);
    }
  }
}

vue 绑定方式

<!-- App.vue -->
<template>
  <div id="app" v-focus-touch> <!-- 指令绑定在这里 -->
    <router-view/> <!-- 所有页面内容都会继承这个监听 -->
  </div>
</template>

✅ 哪些元素会被捕获?

所有原生 <input> 元素

<input type="text"> <!-- 会触发 -->

组件库的输入框(如 Vant/ElementUI)

<van-field> <!-- 内部生成的 input 会触发 -->
<el-input>  <!-- 内部生成的 input 会触发 -->

<textarea> 和其他可聚焦元素
(如果选择器包含它们时也会触发)

❌ 哪些元素不会触发?

被排除的元素

// 指令中已通过 :not(:disabled) 排除禁用状态元素
<input disabled> <!-- 不会触发 -->

被阻止冒泡的元素

<div @pointerdown.stop>
  <input> <!-- 不会触发(事件被阻止冒泡到顶级容器) -->
</div>

CSS 阻止指针事件的元素

input {
  pointer-events: none; /* 不会触发 */
}

使用说明

生效范围:所有未被排除的可聚焦输入元素
冒泡机制:依赖事件冒泡到顶级容器
特殊场景:注意第三方组件库的DOM结构差异
提示:可通过添加特定class(如.focus-touch-input)实现更精确的控制

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容