You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于原生JS实现多步骤表单分步验证方案问询

多步骤表单必填字段验证(含单选组)解决方案

你的问题核心是单选组的验证逻辑不对——单选按钮的required是作用于整个组的,而非单个按钮。直接遍历所有input会把未选中的单选按钮都标记为无效,但实际上只要组内有一个选中就符合要求。以下是原生JS的正确实现:

核心思路

  1. 编写独立的验证函数,专门处理当前步骤的必填字段
  2. 区分普通输入控件(文本框、下拉框)和单选组:
    • 普通控件直接检查validity.valid(复用你已经添加的required属性)
    • 单选组按name属性分组,检查每组是否至少有一个选项被选中
  3. 验证不通过时阻止步骤切换,并给无效字段添加.invalid类

修改后的完整代码

const nextBtns = document.querySelectorAll('.next-btn');
const stepBoxes = document.querySelectorAll('.step-box');
const backBtn = document.querySelector('.back-btn');
const stepCircles = document.querySelectorAll('.step-circle');
let stepIndex = 0;

// 验证当前步骤的必填字段
function validateCurrentStep() {
    const currentStep = stepBoxes[stepIndex];
    let isValid = true;
    const invalidClass = 'invalid';

    // 清除之前的无效标记
    currentStep.querySelectorAll(`.${invalidClass}`).forEach(el => el.classList.remove(invalidClass));

    // 处理普通输入控件(非单选)和下拉框
    const normalInputs = currentStep.querySelectorAll('input:not([type="radio"])[required], select[required]');
    normalInputs.forEach(input => {
        if (!input.validity.valid) {
            input.classList.add(invalidClass);
            isValid = false;
        }
    });

    // 处理单选组
    const radioGroups = {};
    // 收集所有必填单选按钮,按name分组
    currentStep.querySelectorAll('input[type="radio"][required]').forEach(radio => {
        const groupName = radio.name;
        if (!radioGroups[groupName]) {
            radioGroups[groupName] = [];
        }
        radioGroups[groupName].push(radio);
    });

    // 检查每个单选组是否有选中项
    Object.values(radioGroups).forEach(group => {
        const hasChecked = group.some(radio => radio.checked);
        if (!hasChecked) {
            // 给组内所有单选按钮添加无效类,也可改为给容器加样式(按需调整)
            group.forEach(radio => radio.classList.add(invalidClass));
            isValid = false;
        }
    });

    return isValid;
}

nextBtns?.forEach((nextBtn) => {
    nextBtn.addEventListener('click', (e) => {
        e.preventDefault();
        
        // 先验证当前步骤,不通过则终止操作
        if (!validateCurrentStep()) {
            return;
        }

        // 验证通过,执行步骤切换
        stepBoxes[stepIndex].classList.add('hide');
        stepCircles[stepIndex].classList.remove('active');
        
        stepIndex++;
        stepBoxes[stepIndex].classList.remove('hide');
        stepCircles[stepIndex].classList.add('active');
        backBtn.classList.toggle("hide", stepIndex === 0);
    });
});

// 表单提交代码(新增全步骤验证逻辑)
const membershipForm = document.getElementById('membershipForm');
let jsonObject = {};

membershipForm?.addEventListener('submit', (e) => {
    e.preventDefault();
    // 提交前验证所有步骤
    let allStepsValid = true;
    let firstInvalidStepIndex = 0;

    stepBoxes.forEach((step, index) => {
        stepIndex = index;
        if (!validateCurrentStep()) {
            allStepsValid = false;
            firstInvalidStepIndex = index;
            return;
        }
    });

    if (!allStepsValid) {
        // 跳转到第一个无效步骤
        stepBoxes.forEach((s, i) => s.classList.toggle('hide', i !== firstInvalidStepIndex));
        stepCircles.forEach((circle, i) => circle.classList.toggle('active', i === firstInvalidStepIndex));
        stepIndex = firstInvalidStepIndex;
        backBtn.classList.toggle("hide", stepIndex === 0);
        return;
    }

    // 收集表单数据
    const formData = new FormData(membershipForm);
    jsonObject = {};
    for (const pair of formData.entries()) {
        jsonObject[pair[0]] = pair[1];
    }
    console.log(jsonObject);
});

关键说明

  • 利用HTML5原生的validity API,无需手动判断输入值,直接复用required属性的验证逻辑
  • 单选组按name分组检查,避免了单个单选按钮的误判
  • 验证前先清除旧的无效标记,防止样式残留
  • 表单提交时新增全步骤验证,确保用户没有跳过必填项

内容的提问来源于stack exchange,提问作者Jorn Reed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 23:30:41