加快选择器和方法

时间:2010-10-28 21:13:54

标签: javascript jquery performance css-selectors

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

这是标准的“全部检查”复选框。标题单元格(<thead><tr><th>)有一个复选框,选中此复选框后会检查位于同一列中的表格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 /页)。

任何想法如何加速它?

2 个答案:

答案 0 :(得分:2)

我不会像.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()元素中(或者在该复选框的右侧)。

答案 1 :(得分:1)

// I wonder if "parent().parent()" would be faster 
// (and still safe for use if "thead" is omitted)?

没有。如果省略<thead>,则在HTML中将自动添加<tbody>元素,因为在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)当你完成时你可以{{1 }}

parent.insertBefore(table, sibling)

您应该考虑使用 .children("tbody") .children() .children(":eq(" + index + ") :checkbox") .each(function () { this.checked = isChecked; }); 而不是将其隐藏在选择器中。不会产生的差异,但它会更清晰。

在任何情况下,你都可以通过使用一些更标准的DOM来遍历表格来保存jQuery的选择器引擎:

.children().eq(index)

选择器查询可以快速,当他们对文档进行操作并仅使用标准CSS选择器时。在这种情况下,jQuery可以将工作传递给浏览器的快速$.each(table[0].tBodies[0].rows, function() { $(this.cells[index]).find('input[type=checkbox]').each(function() { this.checked = isChecked; }); }); 方法。但是由于jQuery和Selectors-API之间的分歧超出了它们的意思,并且非标准的Sizzle选择器如{{1},因此无法优化范围选择器(document.querySelectorAllfind的第二个参数)。 }和$()将被拒绝。所以这个:

:eq
在使用:checkbox的现代浏览器上,

实际上可能会更快!