2016-11-27 62 views
2

效率低下我的意思是,激活代碼會使頁面掛起20多秒。HTML表格上的Javascript/jquery過濾器效率極低

要設置場景,我目前有一個如下所示的HTML表格。它可以相當大,容易1,000-1,500行和40列寬。它是從Python/Flask生成的靜態HTML表格,然後JavaScript接管允許用戶過濾和排序行。我使用jquery tablesorter小部件來允許用戶按他們希望的任何列對行進行排序。

表本身具有如下的結構:

<table id="myTablePlayers" class="tablesorter table table-striped table-bordered table-hover" style="overflow: visible"> 
    <thead> 
     <tr> 
      <th>...</th> 
      <th>...</th> 
      <th>...</th> 
      <th>...</th> 
      ... 
      <th>...</th> 
     </tr> 
    </thead> 
    <tbody> 
     <tr class="playerData"> 
      <td>...</td> 
      <td>...</td> 
      <td>...</td> 
      <td>...</td> 
      ... 
      <td>...</td> 
     </tr> 
     ... 
    </tbody> 
</table> 

用戶給出的過濾器如下所示:

  • 最低GP - 輸入字段刪除比用戶在輸入更少的所有行特定列
  • 組 - 選擇刪除特定列中不匹配的所有行的字段(文本)
  • 位置 - 選擇字段(文本)刪除特定列中不匹配的所有行
  • age - 輸入字段,用於刪除特定列中的用戶輸入以下的所有行(例如,如果輸入20,將保持所有行與年齡範圍爲[20.0,21.0)

的JavaScript/jQuery的我已經寫了,很可能是罪魁禍首如下:

function autoRank() { 
    // auto number 
    rank = 0; 
    $("#myTablePlayers .playerData").each(function() { 
     if ($(this).css("display") != "none") { 
      rank++; 
      $(this).find('td').eq(colRank).text(rank); 
     } 
    }); 
} 

function filterTable() { 
    // Need some error checking on input not number 
    minGP = $("#mingp").val() 
    teams = $("#teamFilter").val() 
    position = $("#position").val() 
    age = $("#age").val() 

    $("#myTablePlayers .playerData").show(); 

    $("#myTablePlayers .playerData").each(function() { 
     toHide = false; 

     if (teams != "") { 
      if (!$(this).find('td').eq(colTeam).text().toUpperCase().includes(teams.toUpperCase())) { 
       toHide = true; 
      } 
     } 

     if (Number($(this).find('td').eq(colGP).text()) < minGP) { 
      toHide = true; 
     } 

     if (position != "") { 
      if (position == "D") { 
       if ($(this).find('td').eq(colPos).text().indexOf("D") == -1) { 
        toHide = true; 
       } 
      } else if (position == "F") { 
       if ($(this).find('td').eq(colPos).text().indexOf("D") != -1) { 
        toHide = true; 
       } 
      } else if ($(this).find('td').eq(colPos).text() != position) { 
       toHide = true; 
      } 
     } 

     if (age != "") { 
      column = Number($(this).find('td').eq(colAge).text()) 
      age = Number(age) 
      if ( column < age || column >= age+1 ) { 
       toHide = true; 
      } 
     } 

     if (toHide == true) { 
      $(this).hide(); 
     } 

    }); 

    autoRank(); 
} 

$("#teamFilter").on('change', filterTable); 

$("#mingp").on('change', filterTable); 

$("#position").on('change', filterTable); 

$("#age").on('change', filterTable); 

什麼這種代碼會導致瀏覽器掛起效率低下?我應該改變什麼才能使其高效?

我看着谷歌,但jQuery的表濾波器插件不給我來過濾基於如上(例如https://www.sitepoint.com/12-amazing-jquery-tables/)概述了具體的投入特定的列行的能力。

+1

你有沒有基準把那麼多元素放入沒有javascript的DOM?我認爲這可能會強調瀏覽器只是HTML元素。 – Victory

+1

這可能有助於提高代碼的性能。 [什麼是最快的兒童()或發現()在jQuery?](http://stackoverflow.com/questions/648004/what-is-fastest-children-or-find-in-jquery) – legitghost

+0

看看www .datatables.net jquery插件它比jquery小部件更健壯。 – Bindrid

回答

3

目前你的代碼是這樣的:

  • 迭代所有行
  • 然後爲每一行:
    • 先後爲各不空濾,尋找它的所有子列
    • 然後隔離涉及的列並獲得其值

只有關於上述暴露的機制和使用您引用的一些數字,這意味着,使用像「團隊」這樣的獨特簡單過濾器,您實際上可以觸摸40000列(1000行* 1過濾器* 40列)。
但是,如果2個過濾器不是空的,它立即增長到80000列,依此類推。

這顯然是第一境界在哪裏可以找到一種方法來提高工作效率,有這樣一個非常簡單的變化:

  • 迭代所有行
  • 然後爲每一行:
    • 外觀對於其所有子列
    • 先後爲每個非空濾子,然後隔離所涉及的列並得到其值

的代碼涉及的部分就變成了:

$("#myTablePlayers .playerData").each(function() { 
    var toHide = false, 
     columns = $(this).find('td'); 

    if (teams != "") { 
     if (!columns.eq(colTeam).text().toUpperCase().includes(teams.toUpperCase())) { 
      toHide = true; 
     } 
    } 
    // ...same for next filters 

這樣一來,我們已經擺脫通過不空過濾器的數量列觸摸相乘。

但是,我們可以走得更遠...
在當前形勢下,每個執行實際接觸所有細胞的表,而最多4列參與(針對4個過濾器)。因此,我們可能會嘗試找到一種方法將觸摸列的總數從40000減少到4000!

這可以通過影響一個顯着的類(比如過濾器名稱)對這些涉及的列來實現,所以我們可能會改變這樣的代碼:

$("#myTablePlayers .playerData").each(function() { 
    var toHide = false, 
     classTeam = '.colTeam', 
     classGP = `.colGP`, 
     classPos = `.colPos`, 
     classAge = `.colAge`; 

    if (teams != "") { 
     if (!$(classTeam, this).text().toUpperCase().includes(teams.toUpperCase())) { 
      toHide = true; 
     } 
    } 
    // ...same for next filters 

也許有這個問題:

它是用Python /瓶產生一個靜態HTML表格

,這意味着你不必手工生成的表。
如果是這樣,你可以只添加以下剛剛頁面加載後會影響類的名字:

$(document).ready(function() { 
    $("#myTablePlayers .playerData").each(function() { 
     $('td', this).eq(colTeam).addClass(classTeam); 
     $('td', this).eq(colGP).addClass(classGP); 
     // ... 
    } 
} 

但實際上它可能會提高另一種方式(那麼前面的建議變得無用),採用了完全不同的方法。
由於表格是靜態的,我們可以在頁面加載之後立即採取行動(只做一次),以便在過濾發生時爲更直接的訪問準備所需的數據。

我們可以預先註冊所有參與列每個過濾器:

$(document).ready(function() { 
    var teamCols = $(), 
     GPCols = $(), 
     posCols = $(), 
     ageCols = $(); 
    $("#myTablePlayers .playerData").each(function() { 
     var columns = $(this).find('td'); 
     teamCols.add(columns.eq(colTeam)); 
     GPCols.add(columns.eq(colGP)); 
     posCols.add(columns.eq(colPos)); 
     ageCols.add(columns.eq(colAge)); 
    } 
} 

然後過濾器可以處理直接解決所涉及的列。順便說一句,我們還可以立即隱藏自己的父(這已經有可能在原始版本):

if (teams) { 
    teamCols.each(function() { 
     if (!this.innerHTML.toUpperCase().includes(teams.toUpperCase())) { 
      $(this).parent().hide(); 
     } 
    } 
} 

這是一個有點快寫的,所以它可能包含一些缺陷,也可能還沒有改善...

+0

中的主循環中的〜1,000行。謝謝,您的解決方案已經幫助增加,但它仍然是一個明顯的大延遲。我越看越是它似乎是一個Safari的具體問題。按照第三個例子'teamCols.each(function(){...})執行代碼在Safari上需要花費大約20秒鐘的時間,而在Chrome和Firefox上則需要花費大約20秒鐘時間 –

+1

@ReilyBourne哦!那麼它看起來很漂亮所以我建議你發佈一個新的,獨特的問題,(例如「爲什麼在Chrome瀏覽器/ FF上即時運行,而在Safari上花費約20秒?」)請注意, d在SO上發佈,而不是在CR上發佈!現在,我的第一個目標就是在代碼中加入「時間跟蹤點」來隔離某些部分,比較Safari和Safari之間的時間間隔,比如說FF 。然後改變時間跟蹤點的本地化,使孤立的部分更原子化,等等...... – cFreed

+0

謝謝我提前發佈更新的問題http://stackoverflow.com/questions/40870513/jquerys-each-slower-在野生動物園 - 比 - 鉻 - 火狐 –

1

您可以通過緩存重用元素並減少迭代次數來改進代碼。目前,您可以對記錄進行三次迭代,將其壓縮爲一個記錄。

這是我會怎麼做(未經測試):

$(function() { 
    // cache the elements that you will be re-using 
    var $minGP = $("#mingp"), 
     $team = $("#teamFilter"), 
     $position = $("#position"), 
     $age = $("#age"); 

    var $rows = $("#myTablePlayers .playerData"); 

    function filterTable() { 
     // Need some error checking on input not number 
     var minGP = $minGP.val(), 
      team = $team.val(), 
      position = $position.val(), 
      age = $age.val(); 

     // set the rank as we loop (instead of having a separate iteration for ranking) 
     var rank = 0; 

     // when you use show() and .each(), you are iterating over all the rows twice 

     // instead loop once 
     $rows.each(function() { 
      // cache current row as it will be re-used 
      var $row = $(this); 
      // set flag to show or hide 
      var display = true; 

      if (teams != "" && 
       !$row.find('td').eq(colTeam).text().toUpperCase().indexOf(teams.toUpperCase()) > -1) { 
       display = false; 
      } 
      if (Number($row.find('td').eq(colGP).text()) < minGP) { 
       display = false; 
      } 
      if (position != "") { 
       if (position == "D" && 
        $row.find('td').eq(colPos).text().indexOf("D") == -1) { 
        display = false; 
       } else if (position == "F" && 
        $row.find('td').eq(colPos).text().indexOf("D") != -1) { 
        display = false; 
       } else if ($row.find('td').eq(colPos).text() != position) { 
        display = false; 
       } 
      } 
      if (age != "") { 
       column = Number($row.find('td').eq(colAge).text()) 
       age = Number(age) 
       if (column < age || column >= age + 1) { 
        display = false; 
       } 
      } 
      // hide or show row 
      $tr.toggle(display); 
      // set rank number 
      if (display) { 
       $row.find('td').eq(colRank).text(rank++); 
      } 
     }); 
    } 
    // attach change event handler 
    $minGP.add($team).add($position).add($age).change(filterTable); 
}); 

這可能是由幾秒鐘加速你的代碼,但最終它取決於有多少和有多大你的數據。