2013-07-30 43 views
3

我有一個JS腳本的問題,我正試圖放在一起。我有一個HTML表格,它有300行左右的地方。我已經做了一個排序函數,可以使表頭可點擊並啓動排序功能。我想集成一個進度條,因爲在單擊標題後,在較大的表(500 - 1000行)中,表需要一些時間來排序(IE是一個大罪犯)。進度條會告訴他們在排序完成之前還剩多少時間。我想到的方法是基於排序循環的進度調整div元素。問題是,我似乎無法弄清楚如何將這樣的例程集成到我的循環中。Javascript:如何更新'for'循環中的進度條

我研究這個問題,並注意到這一點:How to change progress bar in loop? 和這樣的:Using setTimeout to update progress bar when looping over multiple variables

第二個話題有幾個演示該做的基本上是我想盡可能的進度條去做些什麼。但是,任何時候我嘗試實現這兩個職位我要麼所示的解決方案:

A - 崩潰的瀏覽器

乙 - 進度條顯示工作,但是從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); 
} 

再次感謝大家!

+1

起初我試圖通過使用本地['Array :: sort'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects /數組/排序)與'O(n log n)',你當前的算法有'O(n²)'。只有這樣做沒有幫助,你將不得不編寫自己的快速和異步排序函數(從當前代碼質量來看,這看起來有點過頭了)。 – Bergi

+0

不要在原地排序DOM。儘管JavaScript本身可以很快排序,但DOM訪問速度可能會很慢,尤其是在較舊的瀏覽器上! –

+0

這對於重新排列和更新進度條來說太慢了。你可能會嘗試設置可見性:在排序時隱藏在桌面上,這可以避免重繪,並且在某些條件下大大加快了操作速度。 – dandavis

回答

8

看看以下內容:
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

+1,這是一個帶有更新的UI(OP旨在供OP使用的CSS)的版本:http:// jsfiddle。 net/6JxQk/1/ – Bergi

+0

這是我所知道的唯一的便攜式解決方案,但是這種情況不是yield()構造所支持的? (尚未便攜) – symcbean

+0

@ F.J setTimeout()方法是一個好的開始。您和Bergi向我展示的工作示例給出了一些關於我的實施出錯的信息。我決定使用一個不依賴於基於標記的循環的進度條。但是,您的答案仍然像我想要的那樣提供了一個進度條。 – Crackertastic

0

它可能不是你正在尋找的 - 恕我直言,當你估計一個特定的操作需要多少時間或需要傳輸多少個字節時,必須使用一個進度條。在其他非確定性的情況下,你必須顯示一個微調:-)

+2

排序操作是相當確定的我會說 - 除非它是一個適應性排序,那麼你需要測量之前的預分類。 – Bergi

+0

對不起,但我不完全明白 - 排序操作是非常確定性的 - 你是否在這裏說一個算法會在X.YYY秒內精確排序,而與它運行的機器無關? – JVXR

+0

沒有。既不知道傳輸已知數量的字節時連接速度有多快。但是,您可以顯示一個比例欄,上面寫着「* 30%的元素已排序*」 – Bergi