Javascript:如何在'for'循环中更新进度条

时间:2013-07-30 21:32:34

标签: javascript

我遇到了一个我试图整理的JS脚本的问题。我有一个HTML表,其中有300行的邻居。我已经创建了一个sort函数,可以使表头可以点击并启动我的sort函数。我想集成一个进度条,因为在单击标题后,在较大的表(500 - 1000行)中,表需要一些时间来排序(IE是一个大罪犯)。进度条会告诉他们在排序完成之前剩余多少时间。我想到的方法是一个div元素,我将根据排序循环的进展调整大小。问题是我似乎无法弄清楚如何将这样的例程集成到我的循环中。

我研究了这个问题并注意到了这一点:How to change progress bar in loop? 而这:Using setTimeout to update progress bar when looping over multiple variables

第二个主题有几个演示基本上就进度条而言我想做的事情。但是,无论何时我尝试实现这两个帖子中显示的解决方案,我都可以:

A - 崩溃浏览器

B - 进度条似乎有效,但是会立即从0到100%,而不是逐步进行。

我希望有人可以带领我朝着正确的方向前进。这个表排序进度指示器必须使用本机JS完成,因为内容必须是脱机可用的,因此我不能通过CDN包含任何jQuery库,并且不希望整个jQuery库膨胀文档。

我已经用它的代码创建了一个JS小提琴。我已经删除了我对进度条形码的所有内容,因为我一直在崩溃浏览器,所以就脚本而言,就是与排序相关的代码。 jsfiddle

这是JS本身:

//Change this variable to match the "id" attribute of the
//table that is going to be operated on.
var tableID = "sortable";

/**
 * Attach click events to all the <th> elements in a table to 
 * call the tableSort() function. This function assumes that cells  
 * in the first row in a table are <th> headers tags and that cells
 * in the remaining rows are <td> data tags.
 *
 * @param table The table element to work with.
 * @return void
 */
function initHeaders(table) {
    //Get the table element
    table = document.getElementById(table);
    //Get the number of cells in the header row
    var l = table.rows[0].cells.length;
    //Loop through the header cells and attach the events
    for(var i = 0; i < l; i++) {
        if(table.rows[0].cells[i].addEventListener) { //For modern browsers
            table.rows[0].cells[i].addEventListener("click", tableSort, false);
        } else if(table.rows[0].cells[i].attachEvent) { //IE specific method
            table.rows[0].cells[i].attachEvent("onclick", tableSort);
        }
    }
}

/**
 * Compares values in a column of a table and then sorts the table rows.
 * Subsequent calls to this function will toggle a row between ascending
 * and descending sort directions.
 *
 * @param e The click event passed in from the browser.
 * @return mixed No return value if operation completes successfully, FALSE on error.
 */
function tableSort(e) { 

    /**
     * Checks to see if a value is numeric.
     *
     * @param n The incoming value to check.
     * @return bool TRUE if value is numeric, FALSE otherwise.
     */
    tableSort.isNumeric = function (n) {
        var num = false;
        if(!isNaN(n) && isFinite(n)) {
            num = true;
        }
        return num;
    }

    //Get the element from the click event that was passed.
    if(e.currentTarget) { //For modern browsers
        e = e.currentTarget;
    } else if(window.event.srcElement) { //IE specific method
        e = window.event.srcElement;
    } else {
        console.log("Unable to determine source event. Terminating....");
        return false;
    }

    //Get the index of the cell, will be needed later
    var ndx = e.cellIndex;

    //Toggle between "asc" and "desc" depending on element's id attribute
    if(e.id == "asc") {
        e.id = "desc";
    } else {
        e.id = "asc";
    }

    //Move up from the <th> that was clicked and find the parent table element.
    var parent = e.parentElement;
    var s = parent.tagName;
    while(s.toLowerCase() != "table") {
        parent = parent.parentElement;
        s = parent.tagName;
    }

    /*
    Executes two different loops.  A "for" loop to control how many
    times the table rows are passed looking for values to sort and a
    "while" loop that does the actual comparing of values.  The "for"
    loop also controls how many times the embedded "while" loop will
    run since each iteration with force at least one table row into 
    the correct position.   
    */

    //var interval = setInterval( function () { progress.updateProgress() } , 100);
    var rows = parent.tBodies[0].rows.length; //Isolate and count rows only in the <tbody> element.
    if(rows > 1) {  //Make sure there are enough rows to bother with sorting
        var v1; //Value 1 placeholder
        var v2; //Value 2 placeholder
        var tbody = parent.tBodies[0];  //Table body to manipulate
        //Start the for loop (controls amount of table passes)
        for(i = 0; i < rows; i++) {
            var j = 0;  //Counter for swapping routine
            var offset = rows - i - 1;  //Stops next loop from overchecking

            // WANT TO UPDATE PROGRESS BAR HERE

            //Start the while loop (controls number of comparisons to make)
            while(j < offset) {             

                //Check to make sure values can be extracted before proceeding
                if(typeof tbody.rows[j].cells[ndx].innerHTML !== undefined && typeof tbody.rows[j + 1].cells[ndx].innerHTML !== undefined) {

                    //Get cell values and compare
                    v1 = tbody.rows[j].cells[ndx].innerHTML;
                    v2 = tbody.rows[j + 1].cells[ndx].innerHTML;
                    if(tableSort.isNumeric(v1) && tableSort.isNumeric(v2)) {
                        //Dealing with two numbers
                        v1 = new Number(v1);
                        v2 = new Number(v2);
                        if(v1 > v2) {
                            if(e.id == "asc") { //v1 moves down
                                tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]);
                            }
                        } else {
                            if(e.id == "desc") { //v1 moves down
                                tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]);
                            }
                        }
                    } else if(tableSort.isNumeric(v1) && !tableSort.isNumeric(v2)) {
                        //v2 is a string, v1 is a number and automatically wins
                        if(e.id == "asc") { //v1 moves down
                            tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]);
                        }
                    } else if(!tableSort.isNumeric(v1) && tableSort.isNumeric(v2)) {
                        //v1 is a string, v2 is a number and automatically wins
                        if(e.id == "desc") { //v1 moves down
                            tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]);
                        }
                    } else {
                        //Both v1 and v2 are strings, use localeCompare()
                        if(v1.localeCompare(v2) > 0) {
                            if(e.id == "asc") { //v1 moves down
                                tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]);
                            }
                        } else {
                            if(e.id == "desc") { //v1 moves down
                                tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]);
                            }
                        }
                    }
                    j++;
                } else {
                    console.log("One of the values turned up undefined");
                }
            }
        }
    }
}

//Wait until DOM is ready and then initialize the table headers.
window.onload = function () {
    initHeaders(tableID);
}

//Change this variable to match the "id" attribute of the //table that is going to be operated on. var tableID = "sortable"; /** * Attach click events to all the <th> elements in a table to * call the tableSort() function. This function assumes that cells * in the first row in a table are <th> headers tags and that cells * in the remaining rows are <td> data tags. * * @param table The table element to work with. * @return void */ function initHeaders(table) { //Get the table element table = document.getElementById(table); //Get the number of cells in the header row var l = table.rows[0].cells.length; //Loop through the header cells and attach the events for(var i = 0; i < l; i++) { if(table.rows[0].cells[i].addEventListener) { //For modern browsers table.rows[0].cells[i].addEventListener("click", tableSort, false); } else if(table.rows[0].cells[i].attachEvent) { //IE specific method table.rows[0].cells[i].attachEvent("onclick", tableSort); } } } /** * Compares values in a column of a table and then sorts the table rows. * Subsequent calls to this function will toggle a row between ascending * and descending sort directions. * * @param e The click event passed in from the browser. * @return mixed No return value if operation completes successfully, FALSE on error. */ function tableSort(e) { /** * Checks to see if a value is numeric. * * @param n The incoming value to check. * @return bool TRUE if value is numeric, FALSE otherwise. */ tableSort.isNumeric = function (n) { var num = false; if(!isNaN(n) && isFinite(n)) { num = true; } return num; } //Get the element from the click event that was passed. if(e.currentTarget) { //For modern browsers e = e.currentTarget; } else if(window.event.srcElement) { //IE specific method e = window.event.srcElement; } else { console.log("Unable to determine source event. Terminating...."); return false; } //Get the index of the cell, will be needed later var ndx = e.cellIndex; //Toggle between "asc" and "desc" depending on element's id attribute if(e.id == "asc") { e.id = "desc"; } else { e.id = "asc"; } //Move up from the <th> that was clicked and find the parent table element. var parent = e.parentElement; var s = parent.tagName; while(s.toLowerCase() != "table") { parent = parent.parentElement; s = parent.tagName; } /* Executes two different loops. A "for" loop to control how many times the table rows are passed looking for values to sort and a "while" loop that does the actual comparing of values. The "for" loop also controls how many times the embedded "while" loop will run since each iteration with force at least one table row into the correct position. */ //var interval = setInterval( function () { progress.updateProgress() } , 100); var rows = parent.tBodies[0].rows.length; //Isolate and count rows only in the <tbody> element. if(rows > 1) { //Make sure there are enough rows to bother with sorting var v1; //Value 1 placeholder var v2; //Value 2 placeholder var tbody = parent.tBodies[0]; //Table body to manipulate //Start the for loop (controls amount of table passes) for(i = 0; i < rows; i++) { var j = 0; //Counter for swapping routine var offset = rows - i - 1; //Stops next loop from overchecking // WANT TO UPDATE PROGRESS BAR HERE //Start the while loop (controls number of comparisons to make) while(j < offset) { //Check to make sure values can be extracted before proceeding if(typeof tbody.rows[j].cells[ndx].innerHTML !== undefined && typeof tbody.rows[j + 1].cells[ndx].innerHTML !== undefined) { //Get cell values and compare v1 = tbody.rows[j].cells[ndx].innerHTML; v2 = tbody.rows[j + 1].cells[ndx].innerHTML; if(tableSort.isNumeric(v1) && tableSort.isNumeric(v2)) { //Dealing with two numbers v1 = new Number(v1); v2 = new Number(v2); if(v1 > v2) { if(e.id == "asc") { //v1 moves down tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]); } } else { if(e.id == "desc") { //v1 moves down tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]); } } } else if(tableSort.isNumeric(v1) && !tableSort.isNumeric(v2)) { //v2 is a string, v1 is a number and automatically wins if(e.id == "asc") { //v1 moves down tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]); } } else if(!tableSort.isNumeric(v1) && tableSort.isNumeric(v2)) { //v1 is a string, v2 is a number and automatically wins if(e.id == "desc") { //v1 moves down tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]); } } else { //Both v1 and v2 are strings, use localeCompare() if(v1.localeCompare(v2) > 0) { if(e.id == "asc") { //v1 moves down tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]); } } else { if(e.id == "desc") { //v1 moves down tbody.insertBefore(tbody.rows[j + 1], tbody.rows[j]); } } } j++; } else { console.log("One of the values turned up undefined"); } } } } } //Wait until DOM is ready and then initialize the table headers. window.onload = function () { initHeaders(tableID); }

提前感谢任何能指出我正确方向的人。

----- 编辑: ----- 好的,所以在阅读了这里的答案并对我的工作方式进行了一些重大修改后,我提出了一个更好的解决方案。进度条不是我想要的,但它很接近。 (虽然我相信它正在显示在页面上,就像排序已经准备好完成一样。)

我修改了我的循环以进行O(n log n)快速排序,而不是直接修改DOM,而是隔离表行以对它们进行排序并将它们复制到数组中。然后我直接在数组上进行排序,一旦完成,我重新构建行,然后删除旧行并将新行添加到其中。排序时间已大大减少。

看看:http://jsfiddle.net/jnBmp/5/

这是新的JS代码:

再次感谢大家!!

2 个答案:

答案 0 :(得分:9)

看看以下内容:
http://jsfiddle.net/6JxQk/

这里的想法是用一个使用setTimeout()的异步循环替换你的for循环,所以你会从以下内容:

for (var i = 0; i < rows; i++) {
    // do stuff
}

......对此:

var i = 0;
(function doSort() {
    // update progress
    // do stuff
    i++;
    if (i < rows) {
        setTimeout(doSort, 0);
    }
})();

虽然如您所见,这会显着减慢您的排序例程,因为除了更新进度条之外,这还会重新排序表的行。考虑到这一点,我认为你最好只使用内置的排序而不是你自己的实现,并删除进度条。

答案 1 :(得分:0)

它可能不是您正在寻找的 - 当您估计特定操作将花费多少时间或需要传输多少字节时,必须使用进度条。在其他非确定性案例中,您必须展示一个微调器:-)