资讯详情

资讯详情

jQuery进度条插件:从功能应用到源码拆解与二次开发

简介jQuery UI progressbar 是前端开发中非常实用的进度条组件常用于文件上传、数据处理、资源加载等需要向用户反馈任务完成状态的场景。这套资源围绕该插件整理出一份可直接上手的小型示例包包含两个可直接在浏览器中运行的 HTML 页面配套的 jQuery 核心脚本和 tipsy 插件脚本进度条与进度容器的设计图片以及一张用于交互提示的 GIF 动图另有必读说明文档帮助快速了解文件用途。压缩包内共 9 个文件整体大小约 34KB体积轻巧、结构清晰尤其适合想快速体验插件效果的前端学习者。资源中演示了进度条的初始化方法、通过 value 接口动态更新百分比数值以及结合 tipsy 插件在鼠标悬停或进度变化时实时显示当前百分比的典型写法读者不仅能对照示例快速复现效果还能基于这套基础代码继续扩展自定义配色、动画过渡和事件回调灵活嵌入到自己的网页项目中。目前已有 405 人学习下载对于希望使用 jQuery 实现轻量进度反馈的开发者而言是一份省时高效的实用参考。1. jQuery progressbar进度条插件一个老项目里最实用的小组件第一次在模拟项目A里用 jQuery progressbar 的时候我踩了个不大不小的坑进度条已经跑到 100%页面上却一个百分比数字都看不到。后来才意识到这个插件本身只负责画一条进度条要想把“进度到哪了”直观地标出来得靠它自带的文本提示位。如果你手头正好有一个维护中的 jQuery 老项目需要做上传进度、数据导入进度或者表单分步加载那这个进度条插件可以说是一个救急的好东西。它体积小、依赖少、开箱即用最关键的是能把进度百分比直接显示在条形上方用户不用猜当前到底完成了多少。这篇文章我会从功能设计讲到源码实现再给出二次开发的思路看完你就能用在自己的项目里。2. 功能与设计进度条的三种形态和两套通知机制2.1 插件的三种显示形态这个小插件最讨喜的地方是它并不强制你只能有一种长相。默认的进度条是纯色填充但它预留了两种常见形态的切换空间。第一种是基础实心条适合后台任务进度提示第二种是条纹动画条适合不确定耗时的操作比如导出报表时看起来没那么死板第三种就是带百分比文本的形态这也是这个插件的主打卖点。形态的切换本质上就是一个样式类名的变化不涉及额外的逻辑代码对二次开发很友好。实现上插件会在目标元素内部插入两个子节点一个负责条形背景一个负责显示百分比文字。背景节点通过宽度百分比来控制填充比例文字节点默认定位在进度条右侧也可以配置成居中。这里有一个常见的设计取舍是把文本放在进度条内部还是放在条外挨着。插件采用的是默认覆盖在上方但通过textPosition参数可以改成inside或outside算是在灵活性和实现成本之间找了个平衡。2.2 默认配置和能改的参数任何插件拿到手里第一件事就是看它支持哪些配置项。这个进度条插件的参数不算多但每一条都在实际项目里用得上。核心参数包括value、max、showText、textFormat、textPosition以及onChange和onComplete两个回调。value是当前进度值max是最大值默认 100这俩决定了百分比怎么算。textFormat支持percent和ratio两种前者显示 45%后者显示 45/100看个人喜好。如果你需要调整条的颜色、高度、圆角插件没有把样式锁死而是留了一个barClass参数让你传入自定义的 CSS 类名。这样做的考虑是进度条的视觉部分本来就该交给样式去管插件代码只负责结构、行为和状态这样职责更清晰。我一般在项目里会直接覆盖.progressbar-bar的背景色和高度而不是在选项里再加一个 color 参数。2.3 事件通知机制change 与 complete真正让这个插件脱离“玩具”级别的是它的两个回调事件。onChange在每一次进度值变化时触发参数里带着当前值和百分比适合用来联动其他 DOM 元素比如把百分比同步到一个表格的某个字段上。onComplete则是在进度值到达max时触发一次典型用途是隐藏 loading 遮罩、弹出完成提示或者自动跳转。这两个回调的设计思路是“只通知不干预”插件本身不会因为你传入了一个onComplete就去执行页面跳转或者弹窗它只负责在合适的时机把控制权交给你。这种松耦合的设计在组件里非常常见我后面拆源码的时候你会发现触发点的位置其实就藏在setValue方法里。3. 快速跑通从引入文件到完成一个带百分比的上传进度示例3.1 引入方式与静态文件结构拿到这份源码包解压后你能看到两个核心文件jquery.progressbar.js和jquery.progressbar.css。前者是插件逻辑后者是默认样式。因为它是 jQuery 插件所以你还得先引入 jQuery版本上我建议用 1.9 以上但不用太新的 3.x因为老项目里往往还跑着旧插件。静态文件顺序不能乱先引 jQuery再引插件然后引样式最后才是你的业务脚本。如果你用的是打包工具也可以直接把插件源码放进 vendor 目录里按 CommonJS 或 ES Module 的方式导入。不过这个插件本身没有做模块化封装我一般会手动给它外面包一层module.exports这样既不影响行内script引入也能在 webpack 项目里正常工作。3.2 一行初始化体验最简用法默认的 HTML 结构非常简单你只需要一个空的div容器插件会在初始化时自动往里填充进度条内部的 DOM。下面的代码展示的是最基础、最不需要动脑子的用法div iduploadProgress/div script srcjquery.min.js/script script srcjquery.progressbar.js/script script $(#uploadProgress).progressbar({ value: 0, max: 100, showText: true, textFormat: percent }); /script这段代码里value: 0表示初始进度为 0max: 100表示最大值是 100showText: true打开百分比文本显示textFormat: percent指明文本格式为百分比。初始化之后页面上会出现一条灰色的空进度条文字位置显示 0%。如果你把value改成 60刷新就能看到 60% 的蓝色填充和对应的文字。3.3 配合 AJAX 实时更新百分比最常用的场景还是配合文件上传或者数据请求来用。这里我给出一个基于 jQuery 的$.ajax的完整示例它通过xhr的回调拿到上传进度然后调用插件的更新方法function startUpload(file) { $(#uploadProgress).progressbar({ value: 0 }); $.ajax({ url: /api/upload, method: POST, data: file, processData: false, contentType: false, xhr: function() { var xhr new XMLHttpRequest(); xhr.upload.addEventListener(progress, function(e) { if (e.lengthComputable) { var percent Math.round(e.loaded / e.total * 100); $(#uploadProgress).progressbar(value, percent); } }); return xhr; } }); }这段代码里最关键的一行是$(#uploadProgress).progressbar(value, percent)它调用的是插件暴露出来的方法接口而不是直接改内部属性。这么做的好处是插件内部会自己去修正数值范围、更新宽度、刷新文字、触发回调你不必关心这些细节。对于不熟悉 jQuery 插件规范的人来说这里有个容易混淆的地方初始化用的是传对象更新数值用的是传字符串加参数这是两种不同的调用约定。4. 核心源码拆解一行.progressbar()背后发生了什么4.1 插件入口与默认参数合并用 jQuery 写插件百分之九十九都会走$.fn扩展这条路。这个插件的入口函数做了两件事判断调用方式是初始化还是更新然后对初始化的情况做参数合并。下面我把简化后的入口逻辑摆出来;(function($) { use strict; var defaults { value: 0, max: 100, showText: true, textFormat: percent, textPosition: outside, onChange: null, onComplete: null }; function Plugin(element, options) { this.element element; this.options $.extend({}, defaults, options); this.value this.options.value; this.max this.options.max; this.init(); } $.fn.progressbar function(options, arg) { if (typeof options string) { var method options; return this.each(function() { var data $.data(this, progressbar); if (data typeof data[method] function) { data[method](arg); } }); } return this.each(function() { if (!$.data(this, progressbar)) { $.data(this, progressbar, new Plugin(this, options)); } }); }; })(jQuery);这里的关键点是$.data缓存实例对象第二次调用同一个元素时插件不会重复创建内部 DOM而是把控制权交给已存在的实例。这种写法在 jQuery 插件里被大量使用是避免状态混乱的标准做法。$.extend({}, defaults, options)保证了自定义参数不会污染默认配置也不受用户传入参数顺序的影响。4.2 DOM 结构的创建与层级关系初始化方法init的部分代码负责把进度条内部结构铺进容器里。结构上它是一个三层嵌套最外层.progressbar中间是填充条.progressbar-bar再往外是文字层.progressbar-text。这里我把创建过程简化示例一下Plugin.prototype.init function() { this.$element $(this.element); this.$element.addClass(progressbar); this.$bar $(div, { class: progressbar-bar }); this.$bar.appendTo(this.$element); this.$bar.css(width, this.value / this.max * 100 %); if (this.options.showText) { this.$text $(div, { class: progressbar-text }); this.$text.text(this.formatText(this.value)); this.$text.appendTo(this.$element); } };在这段代码里$(div, { class: ... })是 jQuery 的快捷创建方式第二个参数里的class会被框架转换成className。条纹动画的原理实际很简单CSS 里给.progressbar-bar加了一个重复的渐变背景配合animation属性让背景位置不断位移代码层面根本不涉及任何定时器。如果你不想用默认的蓝条直接在样式文件里改.progressbar-bar的background就能换色。4.3 值更新与百分比计算的核心方法进度条最核心的活都在setValue里。它要处理三件事值越界修正、视觉宽度刷新、文本内容重算。这段代码我给你拆开了看Plugin.prototype.setValue function(value) { if (typeof value ! number || isNaN(value)) { return; } var oldValue this.value; this.value Math.max(0, Math.min(value, this.max)); var percent this.value / this.max * 100; this.$bar.css(width, percent %); if (this.options.showText) { this.$text.text(this.formatText(this.value)); } if (oldValue ! this.value $.isFunction(this.options.onChange)) { this.options.onChange.call(this.element, this.value, percent); } if (this.value this.max $.isFunction(this.options.onComplete)) { this.options.onComplete.call(this.element, this.value, percent); } };这里最容易被忽略的是Math.max(0, Math.min(value, this.max))这行。它把传入值强行夹在 0 和max之间防止进度值出现负数或者超过最大值。oldValue ! this.value这个判断是为了避免重复触发onChange——如果你连续给进度条设置同一个值回调不应当被反复执行这一点在做动画循环时非常重要。4.4 回调触发与链式调用从setValue可以看到onChange和onComplete的触发位置都在核心更新动作之后。这样处理的好处是回调执行的时候进度条的 UI 已经全部刷新完毕你在回调里读取到的 DOM 状态是可靠的。还有一个细节是call(this.element, ...)这句把回调函数里的this指向了原始的容器元素这样在回调里可以直接用this去改元素样式或者用$(this)重新包装成 jQuery 对象。至于链式调用入口函数末尾的return this.each(...)保证了$(#a, #b).progressbar(...)可以同时作用于多个元素并且调用后还能继续追加其他 jQuery 方法。这算是 jQuery 插件的基本素养不过在实现时容易漏掉each遍历一漏掉多元素场景就会失效。源码包里这一块处理得挺干净。5. 避坑与排查进度条不更新、百分比错乱的四个高频问题5.1 初始化后进度条没出来现象$(#xxx).progressbar()执行完页面上什么都没有控制台也不报错。原因最常见的是容器元素本身的高度或者宽度为 0。进度条内部的填充条是块级元素宽度靠百分比计算但高度如果父元素没确定填充条就会是 0px 高看起来像没渲染。第二个可能原因是 jQuery 和插件的引入顺序反了插件在 jQuery 加载之前执行$.fn上根本挂不上方法。解决给容器设置一个确定的height比如 20px并加上display: block。检查引入顺序确保先加载 jQuery再加载插件脚本。排查时可以先在浏览器控制台执行typeof $.fn.progressbar如果是function说明插件已经挂载成功。5.2 百分比数字一直是 0现象进度条宽度明显在变化但文字始终显示 0%或者文字没出现。原因这个坑往往出在showText参数上。插件默认是显示文字的但如果你复制了一段别人的示例代码里面开了showText: false文字就会消失。另一种情况是textFormat传了percent但初始value为 0造成文字一直是 0%看起来像没生效。解决优先检查初始化参数确认showText为true。如果确认配置无误再打开浏览器控制台查看.progressbar-text是否存在。如果不存在说明初始化走了缓存分支你可以先执行$(#xxx).data(progressbar, null)强行重置再重新初始化。5.3 宽度能变但文本不刷新现象调用setValue之后条变宽了文字还停在旧数值上。原因这不是插件逻辑问题多半是你绕过了插件接口直接操作内部 DOM比如用$(.progressbar-bar).css(width, ...)。插件内部记录的值this.value没有更新下次再调用插件方法时它会拿旧的内部值算百分比把你的宽度刷新覆盖回去。解决更新进度这样的事永远走$(#xxx).progressbar(value, newValue)这样的公开接口不要直接动内部 DOM。如果确实需要手动修改样式改完再调用一次setValue把内部状态和视觉统一回来。5.4 快速更新时回调重复触发现象在setInterval或者上传事件里高频调用progressbar(value, ...)onChange被频繁触发甚至onComplete也执行了好几次导致页面弹出多个完成提示。原因高频更新时如果传入的数值之间差别很小但又不完全相等插件每次都会触发onChange这是符合设计的。onComplete只在值第一次达到max时触发可如果你在回调里把值重新设成了小于max的数下一次更新又会触发一次。解决在业务代码里做节流比如每 100 毫秒只更新一次。对于onComplete重复触发可以在回调函数里加一个标志位执行过一次后置为true后续判断不再处理。这是我在类似插件里惯用的做法简单可靠。6. 二次开发把插件从 jQuery 迁移到原生 JS并增加模拟进度功能6.1 保留核心接口移掉 jQuery 依赖如果哪天你想把这个进度条插件用到新团队里但新项目已经不用 jQuery 了完全可以把它改造成原生组件。我一般保留setValue、setMax和onComplete这几个接口把$.extend换成Object.assign把$.data换成 WeakMap 或者直接挂在元素属性上。改造后的初始化方法长这样function createProgressbar(container, options) { var settings Object.assign({ value: 0, max: 100 }, options); var instance { value: settings.value, max: settings.max, setValue: setValue }; return instance; }迁移的关键在于把 jQuery 链式调用拆解成普通函数调用。你不需要一比一复刻所有参数保留核心业务需要的部分就够了。迁移完成后用单元测试把setValue的边界情况跑一遍尤其是“值超过 max 被夹紧”和“回调只触发一次”这两个行为必须和原插件一致。6.2 给组件添加一个“模拟进度”方法进度条最常见的增值功能是模拟一个不确定耗时的任务进度比如数据清洗时你不知道要跑多久但又不希望用户盯着 0% 看。我习惯在插件上增加一个autoRun(interval, step)方法内部用setInterval自增到达 100% 时自动清除定时器。实现思路很直接Plugin.prototype.autoRun function(interval, step) { var self this; this.timer setInterval(function() { var next self.value step; if (next self.max) { next self.max; clearInterval(self.timer); } self.setValue(next); }, interval); return this; };这样一个方法加进去插件就从手工设定进度的工具升级成了能独立模拟任务的组件。要注意的是定时器必须保存在实例上这样你在页面跳转离开时才能clearInterval清理防止内存泄漏。6.3 改造时的验证路径和我的习惯每次改完这种第三方插件我都会搭一个最精简的 HTML 页面把初始化、更新、回调三个路径手动点一遍。我会写一个测试按钮分别绑定10、-10、setTimeout模拟完成这三种操作打开浏览器控制台观察数值和回调输出。只有这条链路走顺了我才会把插件塞回正式项目里。从那以后我每次接二手插件都会先建一个裸 HTML 文件做好隔离测试再谈业务集成这个习惯帮我少踩了很多隐藏的坑。希望这篇文章的思路和踩坑记录能帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →