el-form 数据校验不通过时滚动到对应错误位置

待解决的问题/需求

业务里出现长表单提交时,用户/业务方/产品经理经常会提的一个需求就是表单校验失败你应该给我滚动定位到对应的错误位置,方便我知道哪里出现了错误。也是提升填写长表单时的用户体验一个很常见的做法。

// el-form校验失败滚动到对应的错误位置
export function elFormErrorScrollIntoView() {
  // 获取第一个校验错误的元素
  const element = document.querySelectorAll('.el-form-item__error')[0]
  // 滚动到错误元素对应位置
  element.scrollIntoView({
    behavior: 'smooth',
    block: 'center'
  })
}

下面是在Vue2中的实现:

<template>
    <el-form ref="form" :model="form" :rules="rules">
        ...
    </el-form>
    <el-button @click="submitForm">提交</el-button>
</template>
<script>
    import { elFormErrorScrollIntoView } from '@/utils'
    
    export default {
        data() {
            return {
                form: { ... },
                rules: { ... }
            }
        },
        method: {
            submitForm() {
                this.$refs.form.validate((valid) => {
                    if (valid) {
                        // 数据校验成功
                        ...
                    } else {
                        // 数据校验失败
                        // 使用$nextTick的原因是,错误提示的元素是在视图更新后出现的,不使用$nextTick获取元素是undefined
                        this.$nextTick(() => {
                            elFormErrorScrollIntoView()
                        })
                    }
                })
            }
        }
    }
</script>

Vue3 中使用 try-catch的方式实现:

const submit = async (methodParams: MethodParams) => {
  try {
    if (!formEl.value) return
    await formEl.value.validate()
    // 校验成功
    //...
  } catch (error) {
    // 校验失败
    elFormErrorScrollIntoView()
    console.log(error)
  }
}
 
const elFormErrorScrollIntoView = async () => {
  // 等在这儿使用nextTick无效,则使用setTimeout等待校验错误的元素可以获取
  setTimeout(() => {
    // 获取第一个校验错误的元素
    const element = document.querySelectorAll('.el-form-item__error')[0]
    if (!element) return
    // 滚动到错误元素对应位置
    element.scrollIntoView({
      behavior: 'smooth',
      block: 'center',
    })
  }, 100)
}