2013-07-30 44 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