基于 Layui 的分步表单(Step Form)高度自适应与状态持久化解决方案

基于 Layui 的分步表单(Step Form)高度自适应与状态持久化解决方案 在前端开发中使用 Layui 的carousel轮播图扩展分步表单时经常会遇到以下三大痛点隐藏步骤不显示/高度塌陷切换到 Step 3 或后续步骤时因 DOM 处于隐藏状态导致高度计算为0内容无法正常显示。性能警告Forced reflow频繁手动获取scrollHeight并设置 CSSheight触发浏览器的强制重排。刷新后状态丢失页面刷新后默认重置到第一步用户体验不佳。本文提供一套完整的重构方案采用原生ResizeObserver实现无感高度自适应并结合localStorage与URL参数实现无缝状态刷新。1、预览图首先导入扩展 js和css 文件可以直接引用官网里面的js或者本地资源位置【免费】step步骤表单分步表单测试测试测试资源-CSDN文库一、 CSS 样式修复/* 覆盖 Layui Carousel 的固定高度限制 */ #stepForm, #stepForm [carousel-item], #stepForm [carousel-item] div { height: auto !important; min-height: 380px; /* 设置底线防塌陷 */ } /* 控制步骤内容的显隐彻底解决切到 Step 3 内容不显示的问题 */ #stepForm [carousel-item] div { display: none; } #stepForm [carousel-item] div.layui-this { display: block !important; }二、 步骤扩展组件优化 (step.js)重构step.js增加精准跳转接口goStep()并处理好头部 DOM 的响应式同步。layui.define([layer, carousel], function (exports) { var $ layui.jquery; var carousel layui.carousel; // 渲染头部步骤条 DOM var renderDom function (elem, stepItems, postion) { $(elem).find(.lay-step).remove(); var stepDiv div classlay-step; for (var i 0; i stepItems.length; i) { stepDiv div classstep-item; // 步骤连接线 if (i (stepItems.length - 1)) { var lineClass i postion ? step-item-tail-done : ; stepDiv div classstep-item-taili class lineClass /i/div; } // 节点数字与状态图标 var number stepItems[i].number || (i 1); if (i postion) { stepDiv div classstep-item-head step-item-head-activei classlayui-icon number /i/div; } else if (i postion) { stepDiv div classstep-item-head step-item-head-donei classlayui-icon layui-icon-ok/i/div; } else { stepDiv div classstep-item-headi classlayui-icon number /i/div; } // 标题和描述 var title stepItems[i].title || ; var desc stepItems[i].desc || ; if (title || desc) { stepDiv div classstep-item-main; if (title) stepDiv div classstep-item-main-title title /div; if (desc) stepDiv div classstep-item-main-desc desc /div; stepDiv /div; } stepDiv /div; } stepDiv /div; $(elem).prepend(stepDiv); var bfb 100 / stepItems.length; $(.step-item).css(width, bfb %); }; var step { render: function (param) { param.indicator none; param.arrow none; param.autoplay false; var stepItems param.stepItems; var elem param.elem; // 渲染 Carousel carousel.render(param); // 渲染头部 var initStep param.step || 0; renderDom(elem, stepItems, initStep); if (param.stepWidth) { $(elem).find(.lay-step).css(width, param.stepWidth); } // 监听轮播图切换事件 carousel.on(change( param.filter ), function (obj) { renderDom(elem, stepItems, obj.index); if (param.stepWidth) { $(elem).find(.lay-step).css(width, param.stepWidth); } if (typeof param.stepChange function) { param.stepChange(obj.index); } }); // 隐藏默认按钮并去掉背景 $(elem).find(.layui-carousel-arrow).css(display, none); $(elem).css(background-color, transparent); }, // 跳转指定步骤 goStep: function (elem, index) { var $elem $(elem); var items $elem.find([carousel-item] div); if (index 0 || index items.length) return; items.removeClass(layui-this).eq(index).addClass(layui-this); $elem.find(.layui-carousel-ind li).eq(index).trigger(click); }, next: function (elem) { var currentIndex $(elem).find([carousel-item] div.layui-this).index(); this.goStep(elem, currentIndex 1); }, pre: function (elem) { var currentIndex $(elem).find([carousel-item] div.layui-this).index(); this.goStep(elem, currentIndex - 1); } }; exports(step, step); });三、 主页面逻辑实现在主页面中利用ResizeObserver代替手动的 DOM 高度计算逻辑无需在展开/收缩等事件里重复手动调用调整高度的方法。script layui.config({ base: ./step-lay/ }).use([form, step, jquery], function () { var $ layui.jquery, form layui.form, step layui.step; // 1. 读取步骤优先 URL 参数其次 LocalStorage var urlParams new URLSearchParams(window.location.search); var savedStep parseInt(localStorage.getItem(currentStepIndex)) || 0; var currentStep parseInt(urlParams.get(step)) || savedStep; // 2. 初始化分步表单 step.render({ elem: #stepForm, filter: stepForm, width: 100%, stepWidth: 92%, stepItems: [ { title: 阅读须知 }, { title: 填写信息 }, { title: 上传材料 }, { title: 结果领取 }, { title: 申请成功 } ], step: currentStep, stepChange: function (index) { // 同步保存到 LocalStorage 并更新 URL 参数 localStorage.setItem(currentStepIndex, index); var url new URL(window.location.href); url.searchParams.set(step, index); window.history.replaceState({}, , url.toString()); // 触发容器高度自适应更新 updateContainerHeight(); } }); // 初始跳至对应步骤 if (currentStep 0) { step.goStep(#stepForm, currentStep); } // 3. 表单事件绑定 form.on(submit(formStep), function () { step.next(#stepForm); return false; }); $(.pre).click(function () { step.pre(#stepForm); }); // 4. 高度实时自适应机制 function updateContainerHeight() { var $activeItem $(#stepForm [carousel-item] div.layui-this); if ($activeItem.length 0) { var activeHeight $activeItem.outerHeight(true); $(#stepForm, #stepForm [carousel-item]).css(height, activeHeight px); } } // 利用 ResizeObserver 自动感知页面内容高度变动折叠、展开、异步渲染等 if (ResizeObserver in window) { var observer new ResizeObserver(function () { updateContainerHeight(); }); $(#stepForm [carousel-item] div).each(function () { observer.observe(this); }); } else { // 兜底方案 setInterval(updateContainerHeight, 300); } }); // 内容展开/收缩控制函数ResizeObserver 会自动响应尺寸变化无需手动计算高度 function toggleContent(divId, toggleImage) { var contentDiv document.getElementById(divId); if (contentDiv.style.display none) { contentDiv.style.display block; toggleImage.src ../image/收缩.png; } else { contentDiv.style.display none; toggleImage.src ../image/展开.png; } } /script