你的表单支持回车自动聚焦吗?

96 阅读8分钟

背景

最近在做产品优化,产品让给表单增加一个功能,就是回车后自动进入下一个表单元素,这样就不用频繁使用鼠标进行切换了,可以大大提升表单输入的流畅性,让用户一路Next。

这个需求很合理,也非常通用,理论上全部表单都应该支持这样的效果,很多大厂的产品,也都是支持这个效果的,现在问题就变成了如何完美设计一个方案,以实现这个效果。

制定目标

在进行技术开发之前,我习惯先给自己定义下技术指标,而不是上来就做。定义好技术指标,就定义好了我们的需求,定义好了要做什么,做成什么样,而不是上来先考虑如何去做,这样更符合做事的方法论。

针对这个问题,我给自己定了如下几个目标:

  • 配置要简单:

    • 因为大量的表单都需要进行这个配置,所以配置一定要尽可能简单,尽量一行代码搞定
    • 不需要配置顺序,根据表单中的顺序,自动聚焦下一个表单
  • 要支持配置哪些表单元素参与回车聚焦

    • 默认应该支持所有含有input和textarea的元素参与回车聚焦
    • 但是也要支持自定义,自定义要简单,比如指定含有某个className的元素参与回车聚焦
  • 要支持自动聚焦首个表单元素
  • 要能够自动滚动到聚焦的表单元素
  • 要能够跳过disabled的元素,以及一些不需要聚焦的表单元素,如radio、checkbox、submit等
  • 要支持vue2和vue3

方案制定

网上有一些文章,基本都是针对某个表单元素,监听keydown事件,然后特殊处理其逻辑,这样的解决方案没有通用性,而且也非常复杂,每个表单都要大量的无效重复代码。

考虑通过指令的方式来解决这个问题,期望开发一个 v-focus-next指令,只要配置了这个指令,其中的表单元素就自动支持回车聚焦。

期望的使用方式:

 <div v-focus-next>
              <input/>
                  <input/>
                      <input/>
                          <input/>
                              <input/>
                                  <textarea/>
                                  </div>

组件也同样支持该指令

<el-form v-focus-next >
              <el-form-item label="名称">
                      <el-input v-model="form.name" id="name" />
                          </el-form-item>
                              <el-form-item label="年龄">
                                      <el-input v-model="form.age" id="age" disabled />
                                          </el-form-item>
                                           </el-form>

如果我们只想让className为 focus-next的参与回车聚焦,则这么配置。

 <div v-focus-next="'.focus-next'">
              <input class=focus-next/>
                  <input/>
                      <input class=focus-next/>
                          <input/>
                              <input class=focus-next/>
                                  <textarea/>
                                  </div>

自动聚焦首个表单,应该只要设置下autoFocus即可

<div v-focus-next.autoFocus>
          </div>

到目前为止,我们只是在定义要做的事情应该是什么样的,并没有开始编码,无论是编写组件,还是指令,都希望先定义好对外的接口,然后评估这样的接口设计是否足够易用,最后再去实现。

记住:定义接口比实现更重要。

核心技术点

如何兼容Vue2和Vue3

Vue2和Vue3支持的指令钩子函数并不相同。

Vue3的指令

const myDirective = {
                // 在绑定元素的 attribute 前
                      // 或事件监听器应用前调用
                            created(el, binding, vnode, prevVnode) {
                                    // 下面会介绍各个参数的细节
                                          },
                                                // 在元素被插入到 DOM 前调用
                                                      beforeMount(el, binding, vnode, prevVnode) {},
                                                            // 在绑定元素的父组件
                                                                  // 及他自己的所有子节点都挂载完成后调用
                                                                        mounted(el, binding, vnode, prevVnode) {},
                                                                              // 绑定元素的父组件更新前调用
                                                                                    beforeUpdate(el, binding, vnode, prevVnode) {},
                                                                                          // 在绑定元素的父组件
                                                                                                // 及他自己的所有子节点都更新后调用
                                                                                                      updated(el, binding, vnode, prevVnode) {},
                                                                                                            // 绑定元素的父组件卸载前调用
                                                                                                                  beforeUnmount(el, binding, vnode, prevVnode) {},
                                                                                                                        // 绑定元素的父组件卸载后调用
                                                                                                                              unmounted(el, binding, vnode, prevVnode) {}
                                                                                                                              }

Vue2的指令

                                          })" title="" data-bs-original-title="复制" aria-label="复制"></button>
</div>
      </div><pre class="javascript hljs language-javascript"><span class="hljs-comment">// 注册一个全局自定义指令 `v-focus`</span>
      <span class="hljs-title class_">Vue</span>.<span class="hljs-title function_">directive</span>(<span class="hljs-string">'focus'</span>, {
        <span class="hljs-comment">// 当被绑定的元素插入到 DOM 中时……</span>
          <span class="hljs-attr">inserted</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">el, binding, vNode, prevVnode</span>) {
              <span class="hljs-comment">// 聚焦元素</span>
                  el.<span class="hljs-title function_">focus</span>()
                    },
                      <span class="hljs-attr">bind</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">el, binding, vNode, prevVnode</span>){},
                        <span class="hljs-attr">update</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">el, binding, vNode, prevVnode</span>){},
                          <span class="hljs-attr">componentUpdated</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">el, binding, vNode, prevVnode</span>){},
                            <span class="hljs-attr">unbind</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">el, binding, vNode, prevVnode</span>){},
                              
                              })</pre><p>可以看到,Vue2和Vue3只是钩子函数周期不同,参数基本还是一致,我们只要判断当前环境的Vue版本,然后设置不同的钩子函数即可。</p><p>我们知道,在开发Vue中间件时,install方法可以拿到当前环境的Vue实例,可以通过Vue.version来获取当前环境的Vue版本。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark far fa-copy rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="import focusNext3 from './focus-next3.js';  //vue3指令的具体实现
                  import focusNext2 from &quot;./focus-next2.js&quot;;  //vue2指令的具体实现
                  
                  export default {
                      install: function (Vue){
                          
                                  let version =  Vue.version;  //拿到Vue版本
                                          
                                                  if(version.startsWith('3.')) {
                                                              Vue.directive('focus-next', focusNext3);
                                                                      }else if(version.startsWith('2.')){
                                                                                  Vue.directive('focus-next', focusNext2);
                                                                                          }else{
                                                                                                      console.error('v-focus-next只支持vue2/3≈')
                                                                                                              }
                                                                                                                  }
                                                                                                                  }" title="" data-bs-original-title="复制" aria-label="复制"></button>
</div>
      </div><pre class="javascript hljs language-javascript"><span class="hljs-keyword">import</span> focusNext3 <span class="hljs-keyword">from</span> <span class="hljs-string">'./focus-next3.js'</span>;  <span class="hljs-comment">//vue3指令的具体实现</span>
      <span class="hljs-keyword">import</span> focusNext2 <span class="hljs-keyword">from</span> <span class="hljs-string">"./focus-next2.js"</span>;  <span class="hljs-comment">//vue2指令的具体实现</span>
      
      <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
          <span class="hljs-attr">install</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">Vue</span>){
              
                      <span class="hljs-keyword">let</span> version =  <span class="hljs-title class_">Vue</span>.<span class="hljs-property">version</span>;  <span class="hljs-comment">//拿到Vue版本</span>
                              
                                      <span class="hljs-keyword">if</span>(version.<span class="hljs-title function_">startsWith</span>(<span class="hljs-string">'3.'</span>)) {
                                                  <span class="hljs-title class_">Vue</span>.<span class="hljs-title function_">directive</span>(<span class="hljs-string">'focus-next'</span>, focusNext3);
                                                          }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(version.<span class="hljs-title function_">startsWith</span>(<span class="hljs-string">'2.'</span>)){
                                                                      <span class="hljs-title class_">Vue</span>.<span class="hljs-title function_">directive</span>(<span class="hljs-string">'focus-next'</span>, focusNext2);
                                                                              }<span class="hljs-keyword">else</span>{
                                                                                          <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'v-focus-next只支持vue2/3≈'</span>)
                                                                                                  }
                                                                                                      }
                                                                                                      }</pre><h3 id="item-0-6">指令实现思路</h3><p>在元素绑定了指令v-focus-next之后,我们可以监听当前绑定Dom的keydown事件,然后在keyDown事件中判断是否输入了回车符。如果输入了回车符,则获取当前事件event.target后的第一个有效表单元素,并调用该元素的focus方法。</p><p>在组件卸载之后记得清除掉监听的keydown事件。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark far fa-copy rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="function mounted (el, binding, vNode) {
                   
                       function keyDown(event){
                               if(event.keyCode !== 13){
                                           return;
                                                   }
                                                   
                                                           let targetNode = event.target;
                                                           
                                                                   //找到下一个有效的节点
                                                                           let nextNode = findNextNode(vNode.el, targetNode, binding);
                                                                                   if(!nextNode){
                                                                                               return;
                                                                                                       }
                                                                                                       
                                                                                                               setTimeout(()=>{
                                                                                                                           nextNode.focus();
                                                                                                                                   });
                                                                                                                                       }
                                                                                                                                           el.addEventListener('keydown', keyDown);
                                                                                                                                               el.__FOCUS_NEXT_KEYDOWN_HANDLER__ = keyDown;
                                                                                                                                               }
                                                                                                                                               
                                                                                                                                               function beforeUnmount (el, binding, vNode) {
                                                                                                                                                   el.removeEventListener(&quot;keydown&quot;, el.__FOCUS_NEXT_KEYDOWN_HANDLER__);
                                                                                                                                                   }
                                                                                                                                                   
                                                                                                                                                   export default {
                                                                                                                                                       mounted,
                                                                                                                                                           beforeUnmount
                                                                                                                                                           }" title="" data-bs-original-title="复制" aria-label="复制"></button>
</div>
      </div><pre class="javascript hljs language-javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">mounted</span> (el, binding, vNode) {
       
           <span class="hljs-keyword">function</span> <span class="hljs-title function_">keyDown</span>(<span class="hljs-params">event</span>){
                   <span class="hljs-keyword">if</span>(event.<span class="hljs-property">keyCode</span> !== <span class="hljs-number">13</span>){
                               <span class="hljs-keyword">return</span>;
                                       }
                                       
                                               <span class="hljs-keyword">let</span> targetNode = event.<span class="hljs-property">target</span>;
                                               
                                                       <span class="hljs-comment">//找到下一个有效的节点</span>
                                                               <span class="hljs-keyword">let</span> nextNode = <span class="hljs-title function_">findNextNode</span>(vNode.<span class="hljs-property">el</span>, targetNode, binding);
                                                                       <span class="hljs-keyword">if</span>(!nextNode){
                                                                                   <span class="hljs-keyword">return</span>;
                                                                                           }
                                                                                           
                                                                                                   <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">()=&gt;</span>{
                                                                                                               nextNode.<span class="hljs-title function_">focus</span>();
                                                                                                                       });
                                                                                                                           }
                                                                                                                               el.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'keydown'</span>, keyDown);
                                                                                                                                   el.<span class="hljs-property">__FOCUS_NEXT_KEYDOWN_HANDLER__</span> = keyDown;
                                                                                                                                   }
                                                                                                                                   
                                                                                                                                   <span class="hljs-keyword">function</span> <span class="hljs-title function_">beforeUnmount</span> (el, binding, vNode) {
                                                                                                                                       el.<span class="hljs-title function_">removeEventListener</span>(<span class="hljs-string">"keydown"</span>, el.<span class="hljs-property">__FOCUS_NEXT_KEYDOWN_HANDLER__</span>);
                                                                                                                                       }
                                                                                                                                       
                                                                                                                                       <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
                                                                                                                                           mounted,
                                                                                                                                               beforeUnmount
                                                                                                                                               }</pre><p>接下来重点展示下如何获取当前event.target的下一个有效元素。</p><p>我们可以先找到所有支持回车聚焦的表单元素,然后查找当前event.target所在位置index,然后返回index+1位置的元素。</p><p>这里分2种情况:</p><ul><li><p>event.target本身就属于支持回车聚焦的表单元素:</p><ul><li>这种情况可以通过在所有支持回车聚焦的表单元素中,找到target所在位置即可</li></ul></li><li><p>event.target不在支持回车聚焦的表单元素中</p><ul><li>这种情况可根据dom位置,找到target后的第一个支持回车聚焦的表单元素</li><li>dom1.compareDocumentPosition(dom2),可以判断两个dom的位置</li></ul></li></ul><p></p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark far fa-copy rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="
                  export function findNextNode(rootDom, targetNode, binding){
                      let selector = binding.value || 'input, textarea';
                          
                              //先找到该rootDom下所有有效的input、textarea元素
                                  let nodes = findAllInputs(rootDom, selector);
                                      
                                          
                                              let isByCompare = false;
                                                  let index = nodes.findIndex((item,index) => {
                                                          //如果回车事件的target和item相等,则说明找到了
                                                                  if(item === targetNode || item.contains(targetNode)){
                                                                              return true
                                                                                      }
                                                                                              
                                                                                                      //回车事件的target 不一定在所有有效的nodes中
                                                                                                              //比如我们设置了只让 className='test'的元素支持聚焦回车
                                                                                                                      //那么某个没有className='test'的input回车时,nodes就不包含该target
                                                                                                                              //此时可以根据位置来判断,target后面的第一个有效元素,就是要自动聚焦的元素
                                                                                                                                      if(targetNode.compareDocumentPosition(item) &amp;  Node.DOCUMENT_POSITION_FOLLOWING){
                                                                                                                                                  isByCompare = true;
                                                                                                                                                              return true
                                                                                                                                                                      }
                                                                                                                                                                              return false
                                                                                                                                                                                  });
                                                                                                                                                                                      if(isByCompare){
                                                                                                                                                                                              return nodes[index]
                                                                                                                                                                                                  }else{
                                                                                                                                                                                                          if(index === -1 || index == nodes.length - 1){
                                                                                                                                                                                                                      return null;
                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                      return nodes[index + 1];
                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                          function findAllInputs(rootDom, selector){
                                                                                                                                                                                                                                              //查询selector内部的所有有效input、textarea
                                                                                                                                                                                                                                                  //selector可能是className,绑定在div上,而非input、textarea上
                                                                                                                                                                                                                                                      //必须找到其内部的input、textarea
                                                                                                                                                                                                                                                          return [...rootDom.querySelectorAll(selector)].reduce(function(nodes, node) {
                                                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                         if(['INPUT', 'TEXTAREA'].includes(node.tagName)) {
                                                                                                                                                                                                                                                                                     nodes.push(node);
                                                                                                                                                                                                                                                                                                 return nodes;
                                                                                                                                                                                                                                                                                                         }
                                                                                                                                                                                                                                                                                                                 let childNodes = node.querySelectorAll('input, textarea');
                                                                                                                                                                                                                                                                                                                         if(childNodes.length){
                                                                                                                                                                                                                                                                                                                                     let childNode = findFirstAvailableInput(childNodes)
                                                                                                                                                                                                                                                                                                                                                 if(childNode){
                                                                                                                                                                                                                                                                                                                                                                 nodes.push(childNode);
                                                                                                                                                                                                                                                                                                                                                                             }
                                                                                                                                                                                                                                                                                                                                                                                         return nodes;
                                                                                                                                                                                                                                                                                                                                                                                                 }
                                                                                                                                                                                                                                                                                                                                                                                                         return nodes;
                                                                                                                                                                                                                                                                                                                                                                                                             },[]).filter(item=>{
                                                                                                                                                                                                                                                                                                                                                                                                                     if(item.tagName ==='INPUT'
                                                                                                                                                                                                                                                                                                                                                                                                                                 &amp;&amp; !item.disabled
                                                                                                                                                                                                                                                                                                                                                                                                                                             &amp;&amp; !['submit', 'reset', 'file', 'hidden', 'checkbox', 'radio'].includes(item.type)
                                                                                                                                                                                                                                                                                                                                                                                                                                                     ){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                 return true;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                         }else if(item.tagName ==='TEXTAREA'
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     &amp;&amp; !item.disabled
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         return true;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         return false;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             })
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             function findFirstAvailableInput(nodes){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 for(let i=0;i<nodes.length;i++){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         const input = nodes[i];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 if(input.tagName ==='INPUT'
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             &amp;&amp; !input.disabled
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         &amp;&amp; !['submit', 'reset', 'file', 'hidden', 'checkbox', 'radio'].includes(input.type)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             return input;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     }else if(input.tagName ==='TEXTAREA'
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 &amp;&amp; !input.disabled
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         ){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     return input;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 " title="" data-bs-original-title="复制" aria-label="复制"></button>
</div>
      </div><pre class="javascript hljs language-javascript">
      <span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">findNextNode</span>(<span class="hljs-params">rootDom, targetNode, binding</span>){
          <span class="hljs-keyword">let</span> selector = binding.<span class="hljs-property">value</span> || <span class="hljs-string">'input, textarea'</span>;
              
                  <span class="hljs-comment">//先找到该rootDom下所有有效的input、textarea元素</span>
                      <span class="hljs-keyword">let</span> nodes = <span class="hljs-title function_">findAllInputs</span>(rootDom, selector);
                          
                              
                                  <span class="hljs-keyword">let</span> isByCompare = <span class="hljs-literal">false</span>;
                                      <span class="hljs-keyword">let</span> index = nodes.<span class="hljs-title function_">findIndex</span>(<span class="hljs-function">(<span class="hljs-params">item,index</span>) =&gt;</span> {
                                              <span class="hljs-comment">//如果回车事件的target和item相等,则说明找到了</span>
                                                      <span class="hljs-keyword">if</span>(item === targetNode || item.<span class="hljs-title function_">contains</span>(targetNode)){
                                                                  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
                                                                          }
                                                                                  
                                                                                          <span class="hljs-comment">//回车事件的target 不一定在所有有效的nodes中</span>
                                                                                                  <span class="hljs-comment">//比如我们设置了只让 className='test'的元素支持聚焦回车</span>
                                                                                                          <span class="hljs-comment">//那么某个没有className='test'的input回车时,nodes就不包含该target</span>
                                                                                                                  <span class="hljs-comment">//此时可以根据位置来判断,target后面的第一个有效元素,就是要自动聚焦的元素</span>
                                                                                                                          <span class="hljs-keyword">if</span>(targetNode.<span class="hljs-title function_">compareDocumentPosition</span>(item) &amp;  <span class="hljs-title class_">Node</span>.<span class="hljs-property">DOCUMENT_POSITION_FOLLOWING</span>){
                                                                                                                                      isByCompare = <span class="hljs-literal">true</span>;
                                                                                                                                                  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
                                                                                                                                                          }
                                                                                                                                                                  <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
                                                                                                                                                                      });
                                                                                                                                                                          <span class="hljs-keyword">if</span>(isByCompare){
                                                                                                                                                                                  <span class="hljs-keyword">return</span> nodes[index]
                                                                                                                                                                                      }<span class="hljs-keyword">else</span>{
                                                                                                                                                                                              <span class="hljs-keyword">if</span>(index === -<span class="hljs-number">1</span> || index == nodes.<span class="hljs-property">length</span> - <span class="hljs-number">1</span>){
                                                                                                                                                                                                          <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
                                                                                                                                                                                                                  }
                                                                                                                                                                                                                          <span class="hljs-keyword">return</span> nodes[index + <span class="hljs-number">1</span>];
                                                                                                                                                                                                                              }
                                                                                                                                                                                                                              }
                                                                                                                                                                                                                              
                                                                                                                                                                                                                              
                                                                                                                                                                                                                              <span class="hljs-keyword">function</span> <span class="hljs-title function_">findAllInputs</span>(<span class="hljs-params">rootDom, selector</span>){
                                                                                                                                                                                                                                  <span class="hljs-comment">//查询selector内部的所有有效input、textarea</span>
                                                                                                                                                                                                                                      <span class="hljs-comment">//selector可能是className,绑定在div上,而非input、textarea上</span>
                                                                                                                                                                                                                                          <span class="hljs-comment">//必须找到其内部的input、textarea</span>
                                                                                                                                                                                                                                              <span class="hljs-keyword">return</span> [...rootDom.<span class="hljs-title function_">querySelectorAll</span>(selector)].<span class="hljs-title function_">reduce</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params">nodes, node</span>) {
                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                             <span class="hljs-keyword">if</span>([<span class="hljs-string">'INPUT'</span>, <span class="hljs-string">'TEXTAREA'</span>].<span class="hljs-title function_">includes</span>(node.<span class="hljs-property">tagName</span>)) {
                                                                                                                                                                                                                                                                         nodes.<span class="hljs-title function_">push</span>(node);
                                                                                                                                                                                                                                                                                     <span class="hljs-keyword">return</span> nodes;
                                                                                                                                                                                                                                                                                             }
                                                                                                                                                                                                                                                                                                     <span class="hljs-keyword">let</span> childNodes = node.<span class="hljs-title function_">querySelectorAll</span>(<span class="hljs-string">'input, textarea'</span>);
                                                                                                                                                                                                                                                                                                             <span class="hljs-keyword">if</span>(childNodes.<span class="hljs-property">length</span>){
                                                                                                                                                                                                                                                                                                                         <span class="hljs-keyword">let</span> childNode = <span class="hljs-title function_">findFirstAvailableInput</span>(childNodes)
                                                                                                                                                                                                                                                                                                                                     <span class="hljs-keyword">if</span>(childNode){
                                                                                                                                                                                                                                                                                                                                                     nodes.<span class="hljs-title function_">push</span>(childNode);
                                                                                                                                                                                                                                                                                                                                                                 }
                                                                                                                                                                                                                                                                                                                                                                             <span class="hljs-keyword">return</span> nodes;
                                                                                                                                                                                                                                                                                                                                                                                     }
                                                                                                                                                                                                                                                                                                                                                                                             <span class="hljs-keyword">return</span> nodes;
                                                                                                                                                                                                                                                                                                                                                                                                 },[]).<span class="hljs-title function_">filter</span>(<span class="hljs-function"><span class="hljs-params">item</span>=&gt;</span>{
                                                                                                                                                                                                                                                                                                                                                                                                         <span class="hljs-keyword">if</span>(item.<span class="hljs-property">tagName</span> ===<span class="hljs-string">'INPUT'</span>
                                                                                                                                                                                                                                                                                                                                                                                                                     &amp;&amp; !item.<span class="hljs-property">disabled</span>
                                                                                                                                                                                                                                                                                                                                                                                                                                 &amp;&amp; ![<span class="hljs-string">'submit'</span>, <span class="hljs-string">'reset'</span>, <span class="hljs-string">'file'</span>, <span class="hljs-string">'hidden'</span>, <span class="hljs-string">'checkbox'</span>, <span class="hljs-string">'radio'</span>].<span class="hljs-title function_">includes</span>(item.<span class="hljs-property">type</span>)
                                                                                                                                                                                                                                                                                                                                                                                                                                         ){
                                                                                                                                                                                                                                                                                                                                                                                                                                                     <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
                                                                                                                                                                                                                                                                                                                                                                                                                                                             }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(item.<span class="hljs-property">tagName</span> ===<span class="hljs-string">'TEXTAREA'</span>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                         &amp;&amp; !item.<span class="hljs-property">disabled</span>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 })
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 <span class="hljs-keyword">function</span> <span class="hljs-title function_">findFirstAvailableInput</span>(<span class="hljs-params">nodes</span>){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>;i&lt;nodes.<span class="hljs-property">length</span>;i++){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             <span class="hljs-keyword">const</span> input = nodes[i];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     <span class="hljs-keyword">if</span>(input.<span class="hljs-property">tagName</span> ===<span class="hljs-string">'INPUT'</span>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 &amp;&amp; !input.<span class="hljs-property">disabled</span>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             &amp;&amp; ![<span class="hljs-string">'submit'</span>, <span class="hljs-string">'reset'</span>, <span class="hljs-string">'file'</span>, <span class="hljs-string">'hidden'</span>, <span class="hljs-string">'checkbox'</span>, <span class="hljs-string">'radio'</span>].<span class="hljs-title function_">includes</span>(input.<span class="hljs-property">type</span>)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     ){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 <span class="hljs-keyword">return</span> input;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(input.<span class="hljs-property">tagName</span> ===<span class="hljs-string">'TEXTAREA'</span>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     &amp;&amp; !input.<span class="hljs-property">disabled</span>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             ){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         <span class="hljs-keyword">return</span> input;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     </pre><h3 id="item-0-7">自动聚焦</h3><p>自动聚焦实现比较简单,可以在指令mounted时,找到第一个有效的支持回车聚焦的元素,调用其focus方法。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark far fa-copy rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="function mounted (el, binding, vNode) {
                      if(binding.modifiers.autoFocus){
                              autoFocus(vNode.el, binding)
                                  }
                                      
                                          //其他代码
                                          }
                                          
                                          export function autoFocus(rootDom, binding){
                                              let selector = binding.value || 'input, textarea';
                                                  let nodes = findAllInputs(rootDom, selector);
                                                      if(nodes.length){
                                                              setTimeout(()=>{
                                                                          nodes[0].focus()
                                                                                  })
                                                                                      }
                                                                                      }" title="" data-bs-original-title="复制" aria-label="复制"></button>
</div>
      </div><pre class="javascript hljs language-javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">mounted</span> (el, binding, vNode) {
          <span class="hljs-keyword">if</span>(binding.<span class="hljs-property">modifiers</span>.<span class="hljs-property">autoFocus</span>){
                  <span class="hljs-title function_">autoFocus</span>(vNode.<span class="hljs-property">el</span>, binding)
                      }
                          
                              <span class="hljs-comment">//其他代码</span>
                              }
                              
                              <span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">autoFocus</span>(<span class="hljs-params">rootDom, binding</span>){
                                  <span class="hljs-keyword">let</span> selector = binding.<span class="hljs-property">value</span> || <span class="hljs-string">'input, textarea'</span>;
                                      <span class="hljs-keyword">let</span> nodes = <span class="hljs-title function_">findAllInputs</span>(rootDom, selector);
                                          <span class="hljs-keyword">if</span>(nodes.<span class="hljs-property">length</span>){
                                                  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">()=&gt;</span>{
                                                              nodes[<span class="hljs-number">0</span>].<span class="hljs-title function_">focus</span>()
                                                                      })
                                                                          }
                                                                          }</pre><p>完整代码可以查看我的github源码,欢迎动动发财的小手,帮忙点个赞。</p><p><a href="https://link.segmentfault.com/?enc=JSWPJKDYRtUMGp6QijsUmg%3D%3D.YhWWeuj2IfVVsI9FI8G%2BrMVYil4Ee7RGGhWnHodHu5YywPoMT6XLyonNsi1FmjhJ" rel="nofollow" target="_blank">https://github.com/501351981/v-focus-next</a></p><p>建议大家可以给表单元素加上该指令,表单的输入体验简直棒极了~~</p>