如何实现带必填字段校验的Tab页切换拦截功能
Tab页切换拦截与必填字段校验实现方案
需求回顾
- 当前Tab页存在未填写的必填字段时,禁止切换到下一个Tab页
- 为每个未填写的input显示提示信息“the field is required”
- 未完成当前页所有必填字段时,Next按钮处于禁用状态
- 最后一个输入框填写完成(通过
onblur事件触发)后,启用Next按钮
修改方案与代码实现
1. HTML结构调整
修正重复id问题(id需唯一),为每个必填输入框添加错误提示容器:
<div class="container"> <ul class="nav"> <li class="active">1</li> <li>2</li> <li>3</li> </ul> <section class="section section-one active"> <div class="input-group"> <input type="text" id="name1" name="name" required minlength="4" maxlength="8" size="10"> <span class="error-message"></span> </div> <div class="input-group"> <input type="text" id="name2" name="name" required minlength="4" maxlength="8" size="10"> <span class="error-message"></span> </div> <div class="input-group"> <input type="text" id="name3" name="name" required minlength="4" maxlength="8" size="10"> <span class="error-message"></span> </div> <div class="input-group"> <input type="text" id="name4" name="name" required minlength="4" maxlength="8" size="10"> <span class="error-message"></span> </div> </section> <section class="section section-two"> <div class="input-group"> <input type="text" id="name5" name="name" required minlength="4" maxlength="8" size="10"> <span class="error-message"></span> </div> <div class="input-group"> <input type="text" id="name6" name="name" required minlength="4" maxlength="8" size="10"> <span class="error-message"></span> </div> </section> <section class="section section-three"> <h2>section 3</h2> </section> <button class="previous disable" id="prevBtn" onclick="tabbedContent.goPrev()">Previous</button> <button class="next" id="nextBtn" onclick="tabbedContent.goNext()">Next</button> </div>
2. CSS样式补充
添加错误提示信息的样式:
.container { max-width: 600px; margin: 0 auto; text-align: center; } .section { display: none; } .section.active { display: block; } ul { list-style: none; margin: 0; padding: 0; display: flex; justify-content: center; } ul li { background: #ccc; padding: 10px 15px; margin-left: 6px; border-radius: 50%; cursor: pointer; opacity: .5; } ul li.active { opacity: 1 !important; } .next, .previous { padding: 15px 10px; border-radius: 6px; background: deepskyblue; color: white; border: 0; outline: none; cursor: pointer; width: 100px; } .next.disable, .previous.disable { cursor: not-allowed; opacity: .5; } .input-group { margin: 10px 0; } .error-message { display: block; color: #dc3545; font-size: 12px; margin-top: 4px; }
3. JavaScript逻辑改造
新增校验方法、事件监听,修改切换拦截逻辑:
class TabbedContent { constructor() { this.tabs = document.querySelectorAll(".nav li"); this.sections = document.querySelectorAll(".section"); this.nextButton = document.querySelector("#nextBtn"); this.prevButton = document.querySelector("#prevBtn"); this.current = 0; this.init(); } init() { this.addInputEventListeners(); this.validateCurrentSection(); } addInputEventListeners() { document.querySelectorAll('input[required]').forEach(input => { input.addEventListener('blur', () => { this.validateInput(input); this.validateCurrentSection(); }); }); } validateInput(input) { const errorElement = input.nextElementSibling; if (!input.validity.valid) { if (input.validity.valueMissing) { errorElement.textContent = "the field is required"; } else if (input.validity.tooShort) { errorElement.textContent = `Minimum length is ${input.minLength}`; } else if (input.validity.tooLong) { errorElement.textContent = `Maximum length is ${input.maxLength}`; } } else { errorElement.textContent = ""; } } validateCurrentSection() { const currentSection = this.sections[this.current]; const requiredInputs = currentSection.querySelectorAll('input[required]'); let allValid = true; requiredInputs.forEach(input => { this.validateInput(input); if (!input.validity.valid) { allValid = false; } }); if (this.current === this.tabs.length - 1) { this.nextButton.classList.remove("disable"); } else { const method = allValid ? 'remove' : 'add'; this.nextButton.classList[method]("disable"); } } toggleTabs() { this.tabs.forEach(tab => tab.classList.remove('active')); this.tabs[this.current].classList.add("active"); } toggleSections() { this.sections.forEach(section => section.classList.remove('active')); this.sections[this.current].classList.add("active"); } togglePrev() { const method = this.current === 0 ? 'add' : 'remove'; this.prevButton.classList[method]("disable"); } toggleNext() { if (this.current === this.tabs.length - 1) { this.nextButton.classList.add("disable"); } } goNext() { if (this.current < this.tabs.length - 1) { const currentSection = this.sections[this.current]; const requiredInputs = currentSection.querySelectorAll('input[required]'); let allValid = true; requiredInputs.forEach(input => { if (!input.validity.valid) { allValid = false; } }); if (allValid) { this.current++; this.toggleTabs(); this.toggleSections(); this.toggleNext(); this.togglePrev(); this.validateCurrentSection(); } } } goPrev() { if (this.current > 0) { this.current--; this.toggleTabs(); this.toggleSections(); this.toggleNext(); this.togglePrev(); this.validateCurrentSection(); } } } const tabbedContent = new TabbedContent();
关键实现说明
- 输入合法性校验:利用原生
validityAPI判断输入状态,针对空值、长度不符等场景显示对应提示 - 实时状态同步:每个必填输入框失去焦点时触发校验,自动更新Next按钮的禁用/启用状态
- 切换拦截逻辑:点击Next按钮时先校验当前Tab所有必填字段,仅全部合法时才允许切换
- Tab切换后校验:切换到新Tab后自动校验该页字段,确保按钮状态与当前页输入情况匹配
内容的提问来源于stack exchange,提问作者FelipeDeveloper
相关产品推荐
相关产品推荐

