在Ajax动态加载数据的实际应用中,大家都习惯了一种思维方式:一条数据创建一行。
于是如果数量大的时候,一次性要加载完数据的话,浏览器就会卡上半天受Flex的DataGrid控件的启发,在Flex的DataGrid控件中,展示数据的方法并不是有多少条数据就创建多少行,它最多只创建你在界面上所看到的十几二十行(假设为n行),如果数据多的话,在滚动过程中,会从数据中抽取你应该看到的这n行数据,重新展示在已经创建好的那n行控件中。
也就是说,Flex的DataGrid控件中,我们实际上看到的仅仅是那n行控件,只是它们展示的数据是根据滚动条状态来筛选出来的。所以,如果在JS中,也用类似的方法实现,那么就是上万条数据,可能也只要创建几十个Dom而已,效率自然快得多了。
废话不多说,上代码。首先,需要一个滚动条function Scrollbar() {
    this.options = {
        total: 0,   //数据总数
        pos: 0,     //当前滚动位置
        itemSize: 20,  //单项尺寸
        size: 200  //控件尺寸
    };
}
Scrollbar.prototype = (function() {
    function setOptions(options) {
        for (var attr in options) {
            this.options[attr] = options[attr];
        }
        Refresh(this);
    }
    function Refresh(_this) {
        if (!_this.created) return;        //设置控件高度
        _this.bar.style.height = _this.options.size + "px";        //设置内容高度
        var ch = _this.options.total * _this.options.itemSize;
        _this.content.style.height = ch + "px";
    }
    //获取滚动位置
    function getPos() {
        var top = this.bar.scrollTop;
        var pos = parseInt(top / this.options.itemSize);
        return pos;
    }
    //每页可展示的数据数量
    function getPageItems() {
        return this.options.size / this.options.itemSize;
    }
    
    //滚动事件响应
    function OnScroll(_this) {
        var pos = _this.getPos();
        if (pos == _this.options.pos) return;
        _this.options.pos = pos;
        _this.onScroll(pos);
    }    //滚动条创建
    function CreateAt(dom) {
        var _this = this;        var bar = document.createElement("div");
        var content = document.createElement("div");
        bar.appendChild(content);        bar.style.width = "19px";
        bar.style.overflowY = "scroll";
        bar.style.overflowX = "hidden";
        if (bar.attachEvent) {
            bar.attachEvent("onscroll", function() { OnScroll(_this); });
        }
        else {//firefox兼容
            bar.addEventListener("scroll", function() { OnScroll(_this); }, false);
        }
        content.style.backgroundColor = "white";
        content.style.width = "1px";        this.bar = bar;
        this.content = content;        if (typeof (dom) == "string") {
            dom = document.getElementById(dom);
        }
        dom.innerHTML = "";
        dom.appendChild(this.bar);
        this.created = true;
        Refresh(this);
    }    return {
        setOptions: setOptions,
        CreateAt: CreateAt,
        getPos: getPos,
        getPageItems: getPageItems,
        onScroll: null  //模拟滚动条事件
    };
})();
页面代码<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>表格控件</title>    <script src="Scrollbar.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
    var data = [];
    //创建一万条示例数据
    for (var i = 0; i < 10000; i++) {
        var row = { id: i, text: "text" + i };
        data.push(row);
    }    //创建滚动条
    var scrbar = new Scrollbar();
    window.onload = function() {
        scrbar.CreateAt("divScroll");
        scrbar.setOptions({ total: 10000,size:300 });
        scrbar.onScroll = function(pos) {
            ShowData(pos);
        }        //获取模板
        var items = scrbar.getPageItems();
        var tpl = document.getElementById("trTpl");
        tpl.parentNode.removeChild(tpl);        //仅创建所看到的几十行表格,所以自然快得多
        var list = document.getElementById("tblList");
        for (var i = 0; i < data.length && i < items; i++) {
            var nr = tpl.cloneNode(true);   //从模板行复制新行
            list.appendChild(nr);
        }
        ShowData(scrbar.getPos());
    }
    
    //根据滚动条,展示数据
    function ShowData(pos) {
        var n=scrbar.getPageItems();
        var rows=document.getElementById("tblList").rows;
        for (var i = 0; i < n; i++) {
            var row = rows[i];
            var item = data[i + pos];
            row.cells["tdID"].innerHTML = item["id"];
            row.cells["tdText"].innerHTML = item["text"];
        }
    }
</script>
</head>
<body>
    <div id="divScroll" style="float:right">
    </div>
    <table border="1">
        <!--行标题-->
        <thead><tr>
            <th>ID</th>
            <th>Text</th>
        </tr></thead>
        
        <!--数据展示区-->
        <tbody id="tblList"><tr id="trTpl">
            <td id="tdID">&nbsp;</td>
            <td id="tdText">&nbsp;</td>
        </tr></tbody>
    </table>
</body>
</html>OK!上万条数据,也是瞬间的事
当然,页面很粗糙,不过加点JS代码把那个滚动条和表格对齐一下,也就差不多了。
代码还没封装好,以后再慢慢封装了
而且,还轻易地解决了一个常见问题:固定表格的标题行,哈哈。
代码简陋,见笑了

解决方案 »

  1.   

    经我测试:10000条数据为平均时间为78ms,100000条数据平均时间为820ms。虾们好赞!
      

  2.   

    思路不错 学习下。
    不过有个疑问 如果此时频繁的拖动滚动条 时间密度大 横向数据量也大 拖动时会多次触发数据库查询程序,在时间密度中是否能很好的显示也应该是个问题。当然应该可以标记鼠标按下与松开来实现一次滚动调用一次AJAX,但可以对于苛刻的用户体验度来说又不尽人意了。
    找个空测试一下。说的不对还请拍砖。
      

  3.   


    我也有这疑惑  没用过FLEX  所以不知道效果如何 但如果拖动很频繁 密度大和数据库交互也就频繁了。
      

  4.   

    我的思路前提是:一次Ajax请求下载上万条数据,在这个例子中来展示数据
    当然,横向数据量也大的时候,用控制一下表格的宽度就行了。
    这个例子仅仅是展示了一个思路,我还没真正把它封装好。
      

  5.   

    蛮好
    再次证明了MVC的优势
    希望楼主开发成 jquery插件
      

  6.   

    不错!
     var list = document.getElementById("tblList");
            for (var i = 0; i < data.length && i < items; i++) {
                var nr = tpl.cloneNode(true);   //从模板行复制新行
                list.appendChild(nr);
            }
    这里换用document.createDocumentFragment()这个不知道好些不
      

  7.   

    还没有用到数据库,速度还可以,但是在拉动滚动条时,你可以看下CPU的占用。
    占用的很高。
      

  8.   

    控件已经初步封装完成,请参见网址
    http://topic.csdn.net/u/20100310/18/8bb1dd36-4b1c-480d-a89f-be302163dc57.html?12500
      

  9.   

    楼主好,我使用你的程序部署在IIS后,出现错误,提示是“由于出现错误80020101而导致此项操作无法完成”,不知可否指点一下,我的QQ:398982221