Ускорить селекторы и метод - PullRequest
2 голосов
/ 29 октября 2010

Прежде чем я углублюсь в исходный код 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 ]

2 голосов
/ 29 октября 2010

Я бы не использовал все эти звонки на .children() вот так. Вам было бы намного лучше, если бы вы использовали .find(), чтобы найти флажки, а затем проверили родителей:

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

Простым вызовом .find(), подобным этому, с именем тега ('input'), Sizzle просто использует собственный getElementsByTagName (если не querySelectorAll) для получения входных данных, а затем фильтрует их для флажков. Я действительно подозреваю, что это будет быстрее.

Если поиск родительского индекса становится дорогим, вы всегда можете предварительно рассчитать его и сохранить в элементе .data() на родительском элементе (или прямо в этом поле).

1 голос
/ 29 октября 2010
// 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-serve-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 имеет лучший ответ на этот вопрос: помните parentNode и nextSibling элемента (которые могут быть null, если это последний брат или сестра), и когда вы закончите, вы можете parent.insertBefore(table, sibling).

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

Вы должны рассмотреть возможность использования .children().eq(index) вместо того, чтобы скрывать это в селекторе.Разница не будет большая , но она немного понятнее.

В любом случае, вы можете сохранить движок селектора jQuery, используя более стандартный DOM для обхода таблицы.:

$.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 относительно их значения и нестандартных селекторов Sizzle, таких как :eq и :checkboxбудет просто отклонено.Итак, это:

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

может быть на самом деле быстрее, в современных браузерах с querySelectorAll!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...