8

我想组合的 JS 脚本有问题。我有一个 HTML 表格,其中有大约 300 行。我制作了一个排序功能,可以使表格标题可点击并启动我的排序功能。我想集成一个进度条,因为在单击标题后的较大表格(500 - 1000 行)中,表格需要一些时间进行排序(IE 是一个大罪魁祸首)。进度条会告诉他们在排序完成之前还剩多少时间。我想到的方法是一个 div 元素,我将根据排序循环的进程调整它的大小。问题是我似乎无法弄清楚如何将这样的例程集成到我的循环中。

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

第二个主题有一些演示,就进度条而言,它们基本上可以完成我想做的事情。但是,每当我尝试实施这两篇文章中显示的解决方案时,我要么:

A - 使浏览器崩溃

B - 进度条似乎可以工作,但会立即从 0 到 100%,而不是渐进式。

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

我用我的代码创建了一个 JS fiddle。我已经去掉了进度条码的内容,因为我一直在崩溃浏览器,所以就脚本而言,只有与排序相关的代码。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);
}

提前感谢任何可以为我指明正确方向的人。

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

我修改了循环以进行 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);
}

再次感谢大家!!

4

2 回答 2

12

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

这里的想法是用使用的异步循环替换您的 for 循环setTimeout(),因此您可以从以下内容开始:

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);
    }
})();

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

于 2013-07-30T22:06:21.030 回答
0

它可能不是您正在寻找的 - 恕我直言,当您估计特定操作将花费多少时间或需要传输多少字节时,必须使用进度条。在其他不确定的情况下,您必须显示微调器:-)

于 2013-07-30T21:39:57.537 回答