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

Cra*_*tic 5 javascript

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

我已经研究过这个问题并注意到了这一点:如何在循环中更改进度条? 并且:在循环多个变量时使用setTimeout更新进度条

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

这是新的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);
        }
    }
}


function tableSort(e) { 

    var runs = 0;
    var pix = 0;
    var ndx = 0;
    var dir = "right";
    var interval = false;

    //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
    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;
    }

    //Get the rows to operate on as an array
    var rows = document.getElementById("replace").rows;
    var a = new Array();
    for(i = 0; i < rows.length; i++) {
        a.push(rows[i]);
    }

    //Show progress bar ticker
    document.getElementById("progress").style.display = "block";

    /**
     * Show the progress bar ticker animation
     *
     * @param pix The current pixel count to set the <div> margin at.
     */
    function updateTicker(pix) {

                var tick = document.getElementById("progressTicker");
                document.getElementById("progressText").style.display = "block";
                document.getElementById("progressText").innerHTML = "Sorting table...please wait";
                if(dir == "right") {
                    if(pix < 170) {
                        pix += 5;
                        tick.style.marginLeft = pix + "px";
                    } else {
                        dir = "left";
                    }
                } else {
                    if(pix > 0) {
                        pix -= 5;
                        tick.style.marginLeft = pix + "px";
                    } else {
                        dir = "left";
                    }
                }
                interval = window.setTimeout( function () { updateTicker(pix); }, 25);
    }
    updateTicker(pix);

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

    /**
     * Compares two values and determines which one is "bigger".
     *
     * @param x A reference value to check against.
     * @param y The value to be determined bigger or smaller than the reference.
     * @return TRUE if y is greater or equal to x, FALSE otherwise
     */
    function compare(x, y) {
        var bigger = false;
        x = x.cells[ndx].textContent;
        y = y.cells[ndx].textContent;
        //console.log(e.id);
        if(isNumeric(x) && isNumeric(y)) {
            if(y >= x) {
                bigger = (e.id == "asc") ? true : false;
            } else {                
                bigger = (e.id == "desc") ? true : false;
            }
        } else {
            if(y.localeCompare(x) >= 0) {
                bigger = (e.id == "asc") ? true : false;
            } else {                
                bigger = (e.id == "desc") ? true : false;
            }
        }
        return bigger;
    }   

    /**
     * Performs a quicksort O(n log n) on an array.
     *
     * @param array The array that needs sorting
     * @return array The sorted array.
     */
    function nlognSort(array) {
        runs++
        if(array.length > 1) {
            var big = new Array();
            var small = new Array();
            var pivot = array.pop();
            var l = array.length;
            for(i = 0; i < l; i++) {
                if(compare(pivot,array[i])) {
                    big.push(array[i]);
                } else {
                    small.push(array[i]);
                }
            }
            return Array.prototype.concat(nlognSort(small), pivot, nlognSort(big));
        } else {
            return array;
        }
    }


    //Run sort routine  
    b = nlognSort(a);

    //Rebuild <tbody> and replace new with the old
    var tbody = document.createElement("tbody");
    var l = b.length;
    for(i = 0; i < l; i++) {
        tbody.appendChild(b.shift());
    }
    parent.removeChild(document.getElementById("replace"));
    parent.appendChild(tbody);
    tbody.setAttribute("id","replace");
    setTimeout(function () {
        document.getElementById("progress").style.display = "none";
        document.getElementById("progressText").style.display = "none";
        clearTimeout(interval);
    },1500);
}


window.onload = function() {
    initHeaders(tableID);
}
Run Code Online (Sandbox Code Playgroud)

再次感谢大家!

And*_*ark 9

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

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

for (var i = 0; i < rows; i++) {
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

......对此:

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

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

  • +1,这是一个更新了 UI 的版本(OP 打算使用的 CSS):http://jsfiddle.net/6JxQk/1/ (2认同)