【问题标题】:Speed up selectors and method加速选择器和方法
【发布时间】:2011-05-02 02:59:18
【问题描述】:

在我深入研究 jQuery/Sizzle 源之前,我想我会在这里询问如何加快以下方法的速度。

这是一个标准的“全选”复选框场景。标题单元格(@ 987654321)有一个复选框,当检查时检查它的表的tbody中的所有其他复选框时,它在同一列中的@。

这行得通:

// We want to check/uncheck all columns in a table when the "select all"
// header checkbox is changed (even if the table/rows/checkboxes were 
// added after page load).
(function () {
    // use this dummy div so we can reattach our table later.
    var dummy = $("<div style=display:none />");

    // hook it all up!
    body.delegate(".js_checkAll", "change", function () {

        // cache selectors as much as possible...
        var self = $(this),
            // use closest() instead of parent() because 
            // the checkbox may be in a containing element(s)
            cell = self.closest("th"),
            // Use "cell" here to make the "closest()" call 1 iteration 
            // shorter. I wonder if "parent().parent()" would be faster 
            // (and still safe for use if "thead" is omitted)?
            table = cell.closest("table"),
            isChecked,
            index;

        // detaching speeds up the checkbox loop below.
        // we have to insert the "dummy" div so we know
        // where to put the table back after the loop.
        table.before(dummy).detach();

        index = cell.index();
        isChecked = this.checked;

        // I'm sure this chain is slow as molasses
        table
            // get only _this_ table's tbody
            .children("tbody")
            // get _this_ table's trs
            .children()
            // get _this_ table's checkboxes in the specified column
            .children(":eq(" + index + ") :checkbox")
            // finally...
            .each(function () {
                this.checked = isChecked;
            });

        // put the table back and detach the dummy for
        // later use
        dummy.before(table).detach();

    });
} ());

但是,对于 250 多行,它开始变慢(至少在我的机器上)。用户可能需要多达 500 行数据,因此分页数据不是解决方案(项目已分页 @ 500/页)。

任何想法如何加快速度?

【问题讨论】:

  • 也许您可以为所有复选框指定一个类,或者您可以尝试停止使用 jQuery,因为在某些情况下它可能比自定义代码慢
  • 我会尝试删除一些 jquery 选择器。
  • 你可以使用 JS 模板引擎来处理大列表,它比 DOM 更快

标签: javascript jquery performance css-selectors


【解决方案1】:

我不会像那样使用所有对.children() 的调用。最好使用.find() 找到复选框,然后检查父母:

table.find('input:checkbox').each(function(_, cb) {
  var $cb = $(cb);
  if ($cb.parent().index() === index) cb.checked = isChecked;
});

通过像这样使用标签名称('input')调用.find(),Sizzle 将只使用本机getElementsByTagName(如果不是querySelectorAll)来获取输入,然后过滤复选框的输入。我真的怀疑那会更快。

如果查找父级的索引变得昂贵,您始终可以预先计算并将其存储在父级上的 .data() 元素中(或者就在复选框上)。

【讨论】:

    【解决方案2】:
    // I wonder if "parent().parent()" would be faster 
    // (and still safe for use if "thead" is omitted)?
    

    没有。如果 &lt;thead&gt; 被省略,那么在 HTML 中会自动添加一个 &lt;tbody&gt; 元素,因为在 HTML4 中,开始标签和结束标签都是“可选的”。所以在 HTML 中,它将是 parent().parent().parent(),但在 XHTML-served-as-XML 中,它没有可选标签的废​​话,它将是 parent().parent()

    最好坚持使用closest()。比较清楚,也不是特别慢,而且你只用过一次,反正也不是很关键。

    index = cell.index();
    

    虽然每个表格只有一次,所以并不重要,一个标准的 DOM 属性可以直接获取表格单元格的索引,这将比要求 jQuery 搜索和计算以前的兄弟姐妹:index= cell[0].cellIndex

    // we have to insert the "dummy" div so we know
    // where to put the table back after the loop.
    

    这有点难看。标准 DOM 对此有更好的回答:记住元素的 parentNodenextSibling(如果这是最后一个兄弟元素,则可能是 null),完成后你可以 parent.insertBefore(table, sibling)

            .children("tbody")
            .children()
            .children(":eq(" + index + ") :checkbox")
            .each(function () {
                this.checked = isChecked;
            });
    

    您应该考虑使用.children().eq(index),而不是将其隐藏在选择器中。不会产生的不同,但会更清晰一些。

    无论如何,你可以通过使用一些更标准的 DOM 来遍历表,从而为 jQuery 的选择器引擎节省大量工作:

    $.each(table[0].tBodies[0].rows, function() {
        $(this.cells[index]).find('input[type=checkbox]').each(function() {
            this.checked = isChecked;
        });
    });
    

    选择器查询可以很快,当它们针对文档进行操作并且仅使用标准 CSS 选择器时。在这种情况下,jQuery 可以将工作传递给浏览器的快速document.querySelectorAll 方法。但是范围选择器(find$() 的第二个参数)无法优化,因为 jQuery 和 Selectors-API 之间的含义存在分歧,以及像 :eq:checkbox 这样的非标准 Sizzle 选择器只会被拒绝。所以这个:

    $('#tableid>tbody>tr>td:nth-child('+(index+1)+') input[type=checkbox]')
    

    在带有querySelectorAll的现代浏览器上实际上可能更快!

    【讨论】:

    • 即使没有 querySelectorAll,通过getElementsByTagName 获取表中的所有input 元素也会非常快,不是吗?
    • 会,但我不知道这是否是 OP 想要的。从获取列索引和避免嵌套表的努力来看,我猜可能在某个地方还有其他输入/复选框/表需要避免其内容。如果不是,那么是的,$(table[0].tBodies[0]).find('input'),使用getElementsByTagName 确实是最快的。
    • 对 - 如果确实有很多复选框列,那么最好的办法是在页面生成时用一个类标记复选框,或者,如果失败,请在何时执行一次页面加载。
    • 酷。你有没有用更大的桌子测试它并发现任何显着的速度差异?老实说,我不相信那里有足够的加速,值得为此维护两个代码分支。
    • 我实际上已经在带有基准的 jsbin 上制作了一个测试页面......在测试测试页面时,我不小心创建了一个无限循环,迫使我“硬”关闭我的机器。显然 Chrome 开发频道没有日志运行脚本超时消息。无论如何,querySelectorAll 在 Chrome 8.0.552.18 开发中对于大型表来说要快 40%。我使用它的其中一张表非常“重”,包含图像、嵌套表、可排序列等。
    猜你喜欢
    • 2011-05-04
    • 1970-01-01
    • 1970-01-01
    • 2019-04-22
    • 1970-01-01
    • 2019-12-10
    • 2011-05-03
    • 1970-01-01
    • 2021-12-27
    相关资源
    最近更新 更多