在一些网页应用中,有时会碰到一个超级巨大的列表,成千上万行,这时大部份浏览器解析起来就非常痛苦了(有可能直接卡死)。
也许你们会说可以分页或动态加载啊?但是有可能需求不允许分页,动态加载?网络的延迟也会造成体验不好。
那么适时候介绍本文的实现思路了。
首先上最终的效果:
主要思路如下:
首先,创建如上图所示的三个DIV,scrollbar用于显示具体的滚动条(CSS需要设置zindex,要显示在demo-list的底层),real-panel用于计算列表的实际高度(每一个li的高度是固定的,有多少项数据也是知道的,由此可以计算出列表的实际高度),demo-list主要用于当前滚动条高度对应的实际数据(css需要设置zindex,显示于scrollbar的上层,需要使用JS计算宽度等于real-panel的宽度,这样就刚好覆盖掉底层滚动条外的区域)。
然后,使用KO对数据实现绑定,由于KO的双向绑定,我们可以不用再去实现滚动滚动条时数据变化时带来的更新操作。
最后,就是兼容性了。本文的具体实现在IE6\7\8\9\10\11,FireFox,Chrome,Edge上效果完美。
下面贴代码:flyweight.js
1 /// <summary> 2 /// JQUERY-享元模式之共享DOM元素-插件 3 /// 用法: $("#demo").flyWeight(options); 4 /// 6458450@qq.com 邓西 版权所有 5 /// </summary> 6 7 /// <reference path="jquery-1.7.1.min.js" /> 8 /// <reference path="knockout-2.3.0.js" /> 9 10 /// <param name="options" type="Object"> 11 /// 配置参数 12 /// <para> ulBindScript - ko.js data-bind 语句,写法参见默认值</para> 13 /// <para> liBindScript - ko.js data-bind 语句,写法参见默认值</para> 14 /// <para> items - 要显示的数据JSON数据列表</para> 15 /// <para> viewModel - ko.js ViewModel对像,必须有items及setCurrentItems实现</para> 16 /// <para> itemHeight - 每一项的高度,调整后需要调整对应的CSS以匹配显示效果</para> 17 /// <para> itemShowCount - 列表区域中要显示的个数,调整后需要调整对应的CSS以匹配显示效果</para> 18 /// </param> 19 20 (function ($) { 21 $.fn.extend({ 22 flyWeight: function (options) { 23 if (!options) var options = {}; 24 if (options.ko == undefined) options.ko = null; 25 if (options.ulBindScript == undefined) options.ulBindScript = "foreach: items"; 26 if (options.liBindScript == undefined) options.liBindScript = "text: name, attr: { _id: id, _name: name }, click: $root.select"; 27 if (options.items == undefined) options.items = [{ "id": 1, "name": "test" }]; 28 if (options.viewModel == undefined) options.viewModel = function () { 29 var self = this; 30 self.items = options.ko.observableArray([]); 31 self.setCurrentItems = function (currentItems) { 32 self.items(currentItems); 33 }; 34 self.select = function (item) { 35 console.log(item.id); 36 return false; 37 } 38 }; 39 if (options.itemHeight == undefined) options.itemHeight = 30; 40 if (options.itemShowCount == undefined) options.itemShowCount = 10; 41 42 43 function StringBuilder() { 44 this._stringArray = new Array(); 45 } 46 StringBuilder.prototype.append = function (str) { 47 this._stringArray.push(str); 48 } 49 StringBuilder.prototype.toString = function (joinGap) { 50 return this._stringArray.join(joinGap); 51 } 52 53 var FlyWeight = function (input, options) { 54 CreateDom(input); 55 56 var _vm = new options.viewModel(); 57 options.ko.applyBindings(_vm, document.getElementById("demo-list")); 58 59 AutoSize(); 60 61 //滚动事件 62 $("#scrollbar").scroll(function () { 63 setTimeout(function () { 64 ScrollEvent(_vm); 65 }, 50); 66 }); 67 //第一次初始化加载数据 68 $("#scrollbar").scroll(); 69 70 addEvent('mousewheel', onMouseWheel); 71 addEvent('DOMMouseScroll', onMouseWheel); 72 }; 73 74 return this.each(function () { 75 new FlyWeight(this, options); 76 }); 77 78 //创建相应DOM元素 79 function CreateDom(input) { 80 var sb = new StringBuilder(); 81 82 sb.append("<div id=\"scrollbar\" class=\"scrollbar-panel\">"); 83 sb.append(" <div class=\"real-panel\"></div>"); 84 sb.append("</div>"); 85 sb.append("<div id=\"demo-list\" class=\"list-panel\">"); 86 sb.append(" <ul data-bind=\"" + options.ulBindScript + "\">"); 87 sb.append(" <li><a href=\"javascript:void(0);\" data-bind=\"" + options.liBindScript + "\"></a></li>"); 88 sb.append(" </ul>"); 89 sb.append("</div>"); 90 91 $(input).append(sb.toString("")); 92 } 93 94 //根据浏览器的滚动条宽度自动计算实际的列表区域宽度 95 function AutoSize() { 96 $(".real-panel").height(options.items.length * options.itemHeight); 97 $("#demo-list").width($(".real-panel").width()); 98 } 99 100 //滚动事件101 function ScrollEvent(vm) {102 //获取滚动条距顶部的位置103 var start = getScrollTop();104 //根据top计算应该要显示的区域最大值,需要检查实际个数是否大于默认个数105 var end = 0;106 if (options.items.length > options.itemShowCount) {107 end = start + options.itemShowCount * options.itemHeight;108 }109 else {110 end = start + options.items.length * options.itemHeight;111 }112 113 //根据位置计算应该显示数组中起始下标及结束下标114 var startPos = parseInt(start / options.itemHeight);115 var endPos = parseInt(end / options.itemHeight);116 117 //取出对应的数据并传递给ko被监视的数组118 var currentData = [];119 for (var i = startPos; i < endPos; i++) {120 currentData.push(options.items[i]);121 }122 vm.setCurrentItems(currentData);123 }124 125 //列表区域的滚轮事件绑定126 function onMouseWheel(ev) {127 var ev = ev || window.event;128 var down = true;129 down = ev.wheelDelta ? ev.wheelDelta < 0 : ev.detail > 0;130 var top = getScrollTop();131 //滚动时对滚动条距顶的数量进行加一项高度或减一项高度达到控制滚动条的目的132 if (down) {133 $("#scrollbar").scrollTop(top + options.itemHeight);134 } else {135 $("#scrollbar").scrollTop(top - options.itemHeight);136 }137 if (ev.preventDefault) {138 ev.preventDefault();139 }140 return false;141 }142 143 function addEvent(xEvent, fn) {144 var obj = document.getElementById('demo-list');145 if (obj.attachEvent) {146 obj.attachEvent('on' + xEvent, fn);147 } else {148 obj.addEventListener(xEvent, fn, false);149 }150 }151 152 //获取滚动条距顶部的数值 153 function getScrollTop() {154 var scrollTop = $("#scrollbar").prop('scrollTop');155 return scrollTop;156 }157 }158 });159 })(jQuery
.NET产品源码保护演示下载:www.dllprotect.com
http://www.cnblogs.com/dengxi/p/6755590.html