HTML表上的Javascript / Jquery过滤器效率非常低

时间:2016-11-27 23:48:09

标签: javascript jquery html

效率低下我的意思是,激活代码会使页面挂起20秒以上。

要设置场景,我目前有一个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 - 输入字段,用于删除小于用户在特定列中输入的所有行
  • teams - 选择字段(文本),删除特定列中不匹配的所有行
  • position - 选择字段(文本),删除特定列中不匹配的所有行
  • 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);

此代码使浏览器挂起的效率低下是什么?我应该改变什么来提高效率?

我查看了Google,但 jquery表过滤器插件不能让我根据上面列出的特定输入根据特定列过滤行(例如https://www.sitepoint.com/12-amazing-jquery-tables/)。< / p>

2 个答案:

答案 0 :(得分:3)

目前您的代码的工作方式如下:

  • 迭代所有行
  • 然后每行:
    • 连续为每个非空过滤器查找其所有子列
    • 然后隔离所涉及的列并获得其值

仅关于上述暴露机制并使用您引用的某些数字意味着,使用像#34;团队&#34;这样的独特简单过滤器。你实际上触摸了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!

这可以通过影响这些相关列的区分类(比如filter-name)来实现,所以我们可能会改变这样的代码:

$("#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 / Flask生成的静态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();
        }
    }
}

这写得有点快,所以它可能包含一些缺陷,也可能会有所改进......

答案 1 :(得分: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);
});

这可能会使您的代码加速几秒钟,但最终取决于您的数据的数量和大小。