Javascript: Como atualizar uma barra de progresso em um loop 'for'

Estou com um problema com um script JS que estou tentando reunir. Eu tenho uma tabela HTML com algum lugar na vizinhança de 300 linhas nele. Fiz uma função de classificação que tornará os cabeçalhos da tabela clicáveis ​​e ativará minha função de classificação. Eu gostaria de integrar uma barra de progresso porque em tabelas maiores (500 - 1000 linhas) após um cabeçalho ser clicado, a tabela leva um pouco de tempo para classificar (o IE é um grande ofensor). A barra de progresso informava quanto tempo ainda resta antes que o tipo seja concluído. O método que eu tinha em mente era um elemento div que eu redimensionaria com base na progressão do loop de classificação. O problema é que não consigo descobrir como integrar essa rotina ao meu loop.

Eu pesquisei o assunto e tomei nota disso:Como alterar a barra de progresso em loop? e isto:Usando setTimeout para atualizar a barra de progresso ao fazer o loop sobre múltiplas variáveis

O segundo tópico tem algumas demos que fazem essencialmente o que eu gostaria de fazer no que diz respeito à barra de progresso. No entanto, sempre que tento implementar as soluções mostradas nesses dois posts eu também:

A - Crash the browser

B - Barra de progresso parece funcionar, mas vai de 0 a 100% instantaneamente, não progressivamente.

Espero que alguém possa me levar na direção certa sobre o que fazer. Esse indicador de progresso de classificação da tabela deve ser feito usando o JS nativo porque o conteúdo deve estar disponível offline, portanto, não posso incluir nenhuma biblioteca do jQuery via CDN e o volume excessivo do documento com a biblioteca jQuery inteira não é desejado.

Eu criei um violino JS com o meu código nele. Eu limpei o que eu tinha para o código de barras do progresso, porque eu ficava travando o navegador para que tudo o que estivesse lá, até onde os scripts fossem, fosse o código relacionado à classificação.jsfiddle

Aqui está o próprio JS:

<pre><code>//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); } </code></pre>

Agradeço antecipadamente a qualquer um que possa me apontar na direção certa.

----- EDITAR: ----- Ok, então depois de ler as respostas aqui e fazer algumas modificações pesadas para como eu estava indo sobre as coisas que eu encontrei uma solução muito melhor. A barra de progresso não é exatamente o que eu queria, mas está perto. (Embora eu acredite que esteja aparecendo na página, assim como a classificação está se preparando para terminar.)

Eu modifiquei meu loop para fazer uma ordenação rápida O (n log n) e, em vez de modificar o DOM diretamente, eu isolei as linhas da tabela para classificar e copiá-las em uma matriz. Em seguida, faço o tipo diretamente na matriz e, depois de concluído, reconstruo as linhas, removo as linhas antigas e adiciono as novas. O tempo de classificação foi reduzido significativamente.

Dar uma olhada:http://jsfiddle.net/jnBmp/5/

E aqui está o novo código 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);
}

Mais uma vez obrigado a todos !!

questionAnswers(2)

yourAnswerToTheQuestion