
简介面向Web前端开发者的jQuery progressbar进度条插件演示资源聚焦文件上传、数据处理、加载状态等典型场景帮助解决界面中实时展示任务执行进度与百分比数据的常见需求。资源压缩包共九个文件包括两个可直接运行的HTML示例页面、两个JavaScript脚本、两张进度条效果截图、一张GIF动画、一份TXT说明文档及一个素材源URL快捷方式。HTML示例用于展示完整可交互效果JavaScript脚本覆盖进度条初始化、数值更新以及提示框辅助逻辑图片和动图便于快速对比界面样式说明文档则提供了引入路径与基础用法指引。整个资源包大小仅三十四KB非常轻量目前已有四百零四人浏览学习。通过阅读说明并运行示例代码可以掌握初始化进度条、调用value方法动态更新百分比、设置动画效果等核心操作也能够将这套代码快速迁移至实际项目为后续自定义扩展打下基础是前端初学者理解进度条插件运行机制的一份实用参考。 最近在做一个后台管理系统的上传模块被测试提了一个很“朴素”的需求进度条上能不能直接显示百分比数字我一看页面用的是jQuery progressbar插件默认只渲染一个蓝色色块确实没有百分比文字。这个需求听着简单真动手才发现里面有不少细节今天就把这个“进度条显示进度百分比”的完整玩法拆开讲讲。先说我最终实现的效果进度条跑起来之后色块中间实时显示“42%”这样的数字颜色还可以根据百分比变化比如超过 80% 变红提示整个过程只用了 jQuery jQuery UI Progressbar没引额外库。如果你也正好在调这个插件或者想给已有进度条加个百分比提示这篇文章应该能帮你省不少排查时间。1. 为什么要给 progressbar 加百分比提示1.1 默认 progressbar 的尴尬处境jQuery UI 的 progressbar 插件说实话功能很克制——它本质上就是一个 div 套一个 div外层做底槽内层是填充色块通过控制内层宽度来模拟进度。API 也简单无非是value()取值、value(50)设值、option(max, 100)设最大值以及一个progresschange事件。问题来了用户看到的就是一个色块没法感知具体进度。比如上传一个 200MB 的文件色块走到 80% 和 90% 在视觉上几乎没差别用户就会觉得“卡住了”然后疯狂刷新页面。这种体验在后台管理系统里尤其致命因为操作人员需要知道“还要等多久”而不是“大概走到了某个位置”。所以我给它的定位很明确progressbar 只负责“形”百分比文本负责“神”。形神结合才能算一个合格的进度反馈组件。1.2 需求场景拆解后台管理系统的文件上传、导入导出进度提示表单分步提交时的步骤进度反馈批量任务处理的百分比状态展示比如批量发消息、批量打标签数据加载或图表渲染前的动态加载提示这些场景有个共同点进度值不是写死的而是由异步任务实时驱动的。所以方案设计时不能只做一个静态的“已加载 30%”必须考虑的是“怎么在进度值变化时让百分比数字同步刷新”。2. 基础用法先让 progressbar 正常跑起来2.1 引入依赖与最小实现jQuery UI Progressbar 不是独立插件它依赖 jQuery 核心库、jQuery UI 核心及 Widget 工厂。所以引文件时要注意顺序。我这里用的是 jQuery 3.x jQuery UI 1.13.x兼容性比较好。link relstylesheet hrefhttps://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css script srchttps://code.jquery.com/jquery-3.6.4.min.js/script script srchttps://code.jquery.com/ui/1.13.2/jquery-ui.min.js/scriptHTML 结构就一行div iduploadProgress/div然后初始化$(#uploadProgress).progressbar({ value: 0, max: 100 });此时页面会出现一个带圆角的空进度条。接着你想更新进度调用$(#uploadProgress).progressbar(value, 42);这是最基础的使用方式跑通这一步你的进度条已经能“动”了。核心 API 只有三个value(value)设置进度值value()获取当前值option(max, n)设置最大值。注意 progressbar 的 value 取值范围是 0 到 max默认 max 是 100所以value(50)在默认配置下就是 50%。提示别把 value 传成小数比如 0.5 它会被当成 0.5% 而不是 50%。很多新手在这里翻车进度条死活不动或者只走一点点就是这个原因。2.2 常用参数与事件说明progressbar 常见参数和事件我整理了一个速查表方便你对照使用参数/事件类型说明valueNumber当前进度值默认 0范围 0 ~ maxmaxNumber进度最大值默认 100disabledBoolean是否禁用进度条create事件进度条创建完成后触发change事件进度值变化时触发complete事件进度值达到 max 时触发progresschange事件进度条填充宽度变化时触发推荐用这个一开始我以为用change事件就够了实测发现change事件监听的是 value 属性变化而progresschange监听的是实际渲染到页面上的宽度变化。对于咱们显示进度百分比的需求用哪个都行但要注意一个细节如果你在 change 或 progresschange 事件里调用 value() 方法会再次触发事件容易形成死循环。3. 核心实现让百分比数字显示出来的三种姿势3.1 方案一overlay 覆盖层最省事这个思路最简单在进度条容器里放一个绝对定位的 div专门用来显示百分比文本让它悬浮在色块上方。因为 progressbar 本身有ui-progressbar-value这个子元素我们只需要在初始化前往容器里插一个文本节点就行。$(#uploadProgress).progressbar({ value: 0, max: 100 }); // 在容器内追加百分比显示层 $(#uploadProgress).append(div classprogress-text0%/div);配套 CSS.progress-text { position: absolute; top: 0; left: 0; width: 100%; height: 100%; line-height: 30px; /* 与进度条高度一致 */ text-align: center; font-size: 14px; font-weight: bold; color: #333; pointer-events: none; z-index: 2; }进度条默认高度一般不超过 30px所以 line-height 设置为和进度条高度一致文本就能垂直居中。更新进度时同时更新文本function updateProgress(value) { $(#uploadProgress).progressbar(value, value); $(#uploadProgress .progress-text).text(value %); }这个方案看起来直接但有个用户体验上的问题当进度条走到 70%此时颜色块已经覆盖了文字区域如果文字颜色是深色深色文字配深色底部可能就看不清了。解决办法是给文字加个text-shadow或者用两种颜色做切换后面我会讲怎么优雅处理。3.2 方案二事件驱动更新推荐方案一虽然简单但更新文本还得手动同步调用如果项目里有多个地方都能改进度很容易出现“进度条变了、文字没变”的 bug。更稳妥的做法是把“更新文本”这件事绑定到 progressbar 的事件上让插件自己通知你。我这里用的是progresschange事件它比change更贴近“实际渲染变化”的时机$(#uploadProgress).progressbar({ value: 0, max: 100 }); // 追加文本节点 $(#uploadProgress).append(div classprogress-text0%/div); // 监听进度变化实时更新文本 $(#uploadProgress).on(progresschange, function () { var current $(this).progressbar(value); $(this).find(.progress-text).text(Math.round(current) %); });注意这里用的是on(progresschange, ...)而不是bind因为 progressbar 的事件是自定义事件jQuery 的on方法能正确绑定。另外我在取值后做了Math.round()处理目的是避免 value 为小数时文本显示一长串“42.00000001%”这种反人类内容。这个方案的核心优势进度条的 value 无论如何被修改直接调用 value、动画、外部代码文本都会自动同步更新。后期维护代码时不会出现“进度都 100 了文字还停在 40%”的诡异情况。3.3 方案三重写 _update 方法进阶玩法如果你对 jQuery UI Widget 工厂机制比较熟还可以通过重写 progressbar 的内部方法来绑定文本更新。progressbar 有一个私有方法_update()负责根据 value 值调整色块宽度。我们可以重写它在调用原方法的同时刷新文本$.widget(ui.progressbar, $.ui.progressbar, { _update: function () { this._super(); var current this.value(); $(this.element).find(.progress-text).text(Math.round(current) %); } });为什么有时候 progressbar 的初始化会干扰主要因为progressbar内部会调用_update()如果你重写后的_update()里调用了外部依赖而外部依赖还没创建好比如你的progress-text节点在后面才插入就会报错。所以要保证文本节点先于 widget 初始化存在或者做一次存在性判断。这个方案适合“要封装成公共组件”的项目一次重写全局生效所有页面的 progressbar 都自带百分比文本不用每个页面重复写监听逻辑。但对于大多数页面级开发来说方案二够用了方案三提供的是一个组件化思路。3.4 三种方案对比方案侵入性代码量自动同步适用场景overlay 覆盖层极低最少否临时需求、一次性页面事件驱动更新低适中是多数常规业务页面重写 _update中高中等是公共组件、多页面复用我个人建议如果你只是改一个页面用方案二如果你要做的是公司内部通用组件用方案三。方案一虽然能用但“手动同步文本”这个坑太容易埋 bug不太推荐作为长期方案。4. 对接真实数据从上传进度到进度条4.1 场景AJAX 文件上传进度进度条最常见的真实场景就是文件上传。这里我以 jQuery 的$.ajax为例演示怎么把浏览器原生上传进度换算成 progressbar 的 value。原理是这样的XHR 对象上有upload.onprogress事件浏览器会周期性回调传入loaded已上传字节数和total总字节数二者相除就是当前上传进度。$.ajax({ url: /api/upload, method: POST, data: formData, processData: false, contentType: false, xhr: function () { var xhr $.ajaxSettings.xhr(); if (xhr.upload) { xhr.upload.addEventListener(progress, function (e) { if (e.lengthComputable) { var percent Math.round((e.loaded / e.total) * 100); $(#uploadProgress).progressbar(value, percent); // 方案一手动更新 $(#uploadProgress .progress-text).text(percent %); } }, false); } return xhr; } });热搜词里有个“jquery content type 怎么设置”我多说一嘴上传文件时一定不要手动设置contentType要设置成false让浏览器自动生成带 boundary 的 multipart/form-data 请求体。如果强行contentType: application/json文件数据会被 JSON 序列化搞乱后端接不到文件进度条自然永远停在 0%。我实测过本地上传走progress事件进度值基本是平滑递增的每 100ms 左右回调一次对于百分比文本来说体验已经很流畅了不需要额外做节流。但如果是几百兆大文件progress 回调可能会非常频繁每秒几十次此时你可以在更新文本前做个简单的时间校验比如 200ms 内只刷新一次文本减少 DOM 操作频率。4.2 场景后端返回进度值轮询模式文件上传的进度是浏览器原生支持的但很多业务进度比如批量处理 100 条数据、导出超大报表后端不是实时提供的需要前端轮询接口拿进度值。这时候 progressbar 配合setInterval或setTimeout就是最常见的组合。var progressTimer null; function startPolling(taskId) { var $bar $(#uploadProgress); function poll() { $.get(/api/task/progress, { taskId: taskId }, function (res) { if (res.code 0) { var percent Number(res.data.progress); $bar.progressbar(value, percent); // 事件驱动方案里文本会自动更新这里不需要手动处理 if (percent 100) { clearInterval(progressTimer); // 可以在这里触发完成逻辑 } } else { clearInterval(progressTimer); } }); } poll(); progressTimer setInterval(poll, 1000); }轮询间隔我一般设 1000ms太短会对服务器造成无谓压力太长用户会明显感觉到进度卡顿。如果你用方案二的“事件驱动更新”这段代码只需要处理 value 的变化百分比文本自动就跟着走了这一点在你写业务代码时会特别爽。4.3 计数逻辑的细节坑造轮子写进度计算逻辑时有一个很经典的坑进度值不是必然到 100% 就完成的。比如批量导入 100 条数据后端每处理完 1 条返回一个计数你计算当前数 / 总数 * 100算到 100% 时其实后端可能还要做最后的收尾工作写日志、生成报告。如果这时候直接把进度条标成“完成”用户点了“下一步”却发现还在等体验就很差。我的处理方式是把最后 5% 的进度留给“收尾阶段”前端最多显示到 95%等后端明确返回“任务完成”状态后再一次性跳到 100%。这样进度条的“真实感”反而更强。5. 常见问题与排查技巧实录5.1 常见问题速查表问题现象可能原因解决方案进度条不动一直 0%value 传了小数或字符串确保 value 是 0~100 的整数必要时 Number() 转型百分比文字被色块盖住看不清文字颜色和色块颜色太接近设置超过 50% 进度时切换文字颜色或加 text-shadow更新 value 后文字不同步用的是方案一颜色忘了手动更新改用事件驱动方案或统一封装 update 函数进度到 100% 但 complete 不触发max 值设置不是 100检查 option(max)complete 在 value max 时触发bind 方式绑不上 progresschangejQuery 版本或写法问题使用 on(progresschange, handler)多个进度条共用文本错乱class 选择器匹配到了多个元素用 id 或 closest 定位具体容器别用全局 class5.2 独家避坑技巧第一不要在 progresschange 回调里调用 value() 方法。我一开始在回调里写的是$(#bar).on(progresschange, function () { var v $(this).progressbar(value); // ... });这个问题看着没问题但有极少数版本的 jQuery UI 里value()在内部可能触发一次强制刷新而你又在事件回调里修改 DOM很容易造成重复刷新极端情况下会死循环。稳妥做法是先缓存当前值再处理比如var current $(this).progressbar(option, value);option()方式只读取配置值不会触发刷新动作比value()更安全。第二进度条容器需要设置 position: relative。上面方案一里的.progress-text用了position: absolute它的定位基准是最近的已定位祖先元素。如果进度条的容器没有设置 relative百分比文字会飘到页面其他位置去。这是定位布局最常见的问题但真发生了你可能会排查很久。第三百分比文字颜色建议在 50% 处切换。当进度小于 50%色块还没覆盖到文字区域文字底色是白色的深色文字没问题当进度大于 50%色块已经盖住文字了此时深色文字放在深蓝色块上看不清。我的处理方式是控制在进度到 50% 时切换文字颜色为白色具体可以这样写$(.progress-text).css(color, percent 50 ? #fff : #333);如果不想用 JS 频繁改样式也可以用 CSS 加个 1px 的text-shadow让文字在深色和浅色背景下都可读但这个方案效果不如切换颜色干净。第四如果进度条是动态创建的比如每次弹窗都重新生成注意先销毁旧实例再初始化。很多人在弹窗里用了同一个 id第一次弹窗正常第二次弹窗进度条就不动了原因是 Widget 实例重复初始化。正确做法if ($(#uploadProgress).hasClass(ui-progressbar)) { $(#uploadProgress).progressbar(destroy); } $(#uploadProgress).progressbar({ value: 0, max: 100 });5.3 做一个可复用的迷你封装最后分享一个我实际在项目里用的封装函数把初始化、更新、完成回调都收进一个函数里业务侧只需要调用一个方法代码干净很多function createProgressBar(selector, opts) { var $bar $(selector); var defaults { max: 100, value: 0, onComplete: null }; var settings $.extend({}, defaults, opts || {}); // 存在则先销毁 if ($bar.hasClass(ui-progressbar)) { $bar.progressbar(destroy); } // 追加文本节点 if (!$bar.find(.progress-text).length) { $bar.append(div classprogress-text0%/div); } // 初始化 $bar.progressbar({ value: settings.value, max: settings.max }); // 事件驱动更新文本 $bar.on(progresschange, function () { var val $(this).progressbar(option, value); var percent Math.round(val / settings.max * 100); $(this).find(.progress-text).text(percent %); // 进度条颜色简单切换 var $valueEl $(this).find(.ui-progressbar-value); if (percent 80) { $valueEl.css(background, #e74c3c); } }); // complete 触发回调 if (typeof settings.onComplete function) { $bar.on(progressbarcomplete, settings.onComplete); } return { set: function (val) { $bar.progressbar(value, val); }, get: function () { return $bar.progressbar(option, value); }, destroy: function () { $bar.progressbar(destroy); $bar.off(progresschange); } }; }调用方式var bar createProgressBar(#uploadProgress, { onComplete: function () { alert(上传完成); } }); bar.set(50); // 进度更新文本自动同步这个封装做了一次完整兜底既有事件自动更新又支持完成回调还处理了多次初始化的销毁问题。你直接抄过去改一改样式就能用。在后来的项目里我把这套做法直接做成了团队内部的一个小组件凡是遇到需要展示进度的页面一行代码就能接入。整个调试过程中最耗我时间的反而不是插件本身而是“进度值来源”的链路——到底是接口数据没返回还是返回了但格式不对还是格式对但 value 设置时机不对。所以如果你加了百分比文本还是觉得进度条“不灵动”先别怀疑插件用 console.log 把实时 value 打印出来看看问题往往一眼就能定位。本文还有配套的精品资源点击获取