简介:面向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 rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css"> <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script> <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>HTML 结构就一行:
<div id="uploadProgress"></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 常见参数和事件我整理了一个速查表,方便你对照使用:
| 参数/事件 | 类型 | 说明 |
|---|---|---|
| value | Number | 当前进度值,默认 0,范围 0 ~ max |
| max | Number | 进度最大值,默认 100 |
| disabled | Boolean | 是否禁用进度条 |
| 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 class="progress-text">0%</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 class="progress-text">0%</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 方式绑不上 progresschange | jQuery 版本或写法问题 | 使用 on('progresschange', handler) |
| 多个进度条共用文本错乱 | class 选择器匹配到了多个元素 | 用 id 或 closest 定位具体容器,别用全局 class |
5.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 class="progress-text">0%</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 打印出来看看,问题往往一眼就能定位。
本文还有配套的精品资源,点击获取