8

まとめようとしている JS スクリプトに問題があります。300 行近くの HTML テーブルがあります。テーブル ヘッダーをクリック可能にして並べ替え機能を起動する並べ替え機能を作成しました。ヘッダーがクリックされた後の大きなテーブル (500 ~ 1000 行) では、テーブルの並べ替えに少し時間がかかるため、進行状況バーを統合したいと思います (IE は大きな違反者です)。プログレス バーは、並べ替えが完了するまでの残り時間を示します。私が考えていた方法は、並べ替えループの進行に基づいてサイズを変更する div 要素でした。問題は、そのようなルーチンをループに統合する方法を理解できないように見えることです。

私はこの問題を調査し、これに注意しました: How to change progress bar in loop? そしてこれ:複数の変数をループするときに setTimeout を使用して進行状況バーを更新する

2 番目のトピックには、プログレス バーに関する限り、基本的に私がやりたいことを行うデモがいくつかあります。ただし、これらの 2 つの投稿に示されているソリューションを実装しようとするときはいつでも、次のいずれかを行います。

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

私を正しい方向に向けることができる人に前もって感謝します。

-----編集:-----わかりましたので、ここで回答を読んで、私が行っていた方法に大幅な変更を加えた後、はるかに優れた解決策を思いつきました。進行状況バーは、私が望んでいたものとはまったく異なりますが、近いです。(ただし、ソートが終了する準備ができているのと同じように、ページに表示されていると思います。)

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 に答える