JQuery сортировка элементов div на основе count и дата/время (временная метка)

В настоящее время я использую функцию сортировки для сортировки моих элементов div на основе значения count. Вот как это делается сейчас: (я не уверен, эффективный метод или нет.)

$('#list .list_item').sort(sortDescending).appendTo('#list');

function sortDescending(a, b) {
  return $(a).find(".count").text() < $(b).find(".count").text() ? 1 : -1;
};

Я собираюсь добавить поле timestamp и не знаю, как я могу его расширить, чтобы поддержать это.

У меня есть список элементов div со своим собственным счетчиком и датой/временем/меткой времени. Вот как выглядит HTML-код:

<div id="list">
<div id="list_item_1" class="list_item">
  <div class="count">5</div>
  <div class="timestamp">1272217086</div>
  <div class="text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis ipsum rutrum metus rhoncus feugiat non vel orci. Etiam sit amet nisi sit amet est convallis viverra</div>
</div>
<div id="list_item_2" class="list_item">
  <div class="count">5</div>
  <div class="timestamp">1272216786</div>
  <div class="text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis ipsum rutrum metus rhoncus feugiat non vel orci. Etiam sit amet nisi sit amet est convallis viverra</div>
</div>
<div id="list_item_3" class="list_item">
  <div class="count">10</div>
  <div class="timestamp">1272299966</div>
  <div class="text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis ipsum rutrum metus rhoncus feugiat non vel orci. Etiam sit amet nisi sit amet est convallis viverra</div>
</div>
</div>

Я хотел бы сортировать по счету (уменьшению), а затем по метке времени (уменьшение - новее вверху).

Любая помощь очень ценится! Спасибо:)

Ответ 1

Необходимо изменить только функцию компаратора сортировки. Я уверен, что для этого доступны плагины, и вы можете взглянуть на них, но реализовать то, что вы хотите, довольно тривиально. Метод sortDescending получает два div каждый раз, и сравнение должно соответствовать указанным критериям:

  • Сначала по счету
  • Если count равен, то по timestamp
  • Если временные метки равны, то верните 0

Вот уродливая простая не оптимизированная версия:

function sortDescending(a, b) {
    if(getCount(a) < getCount(b)) {
        return -1;
    }
    else if(getCount(a) > getCount(b)) {
        return 1;
    }
    else if(getTimestamp(a) < getTimestamp(b)) {
        return -1;
    }
    else if(getTimestamp(a) > getTimestamp(b) {
        return 1;
    }
    else {
        return 0;
    }
}

Если вы видите структуру if-else, может показаться очевидным, что вы можете обобщить этот подход, чтобы иметь возможность обрабатывать любой тип пользовательского заказа. Итак, здесь jab в методе sortBy, который принимает число функций обратного вызова, где каждый обратный вызов определяет один критерий сортировки.

function sortBy() {
    var callbacks = arguments;

    return function(a, b) {
        for(var i = 0; i < callbacks; i++) {
            var value = callbacks[i](a, b);
            if(value != 0) {
                return value;
            }
        }
        return 0;
    };
}

Затем передайте все критерии как обратные вызовы этой функции sortBy. Вот пример для вашего кода:

function compareCount(a, b) {
    return getCount(a) - getCount(b);
}

function compareTimestamp(a, b) {
    return getTimestamp(a) - getTimestamp(b);
}

$("selector").sort(sortBy(compareCount, compareTimestamp));

И пока мы находимся в этом, давайте также сделаем плагин jQuery из этого. Он будет иметь приятный и легкий интерфейс:

$("parent selector").sortBy("child selector 1", "child selector 2", ...);

Идея состоит в том, чтобы передать селектор jQuery, который выберет node, текст которого определит значение для сортировки. Мы дадим целые числа с более высоким приоритетом и сначала попытаемся отсортировать численно, если оба значения будут такими, в противном случае сделать регулярное сравнение.

jQuery.fn.sortBy = function() {  
    var selectors = arguments;

    this.sort(function(a, b) {
        // run through each selector, and return first non-zero match
        for(var i = 0; i < selectors.length; i++) {
            var selector = selectors[i];

            var first = $(selector, a).text();
            var second = $(selector, b).text();

            var isNumeric = Number(first) && Number(second);
            if(isNumeric) {
                var diff = first - second;
                if(diff != 0) {
                    return diff;
                }
            }
            else if(first != second) {
                return first < second ? -1 : 1;
            }
        }

        return 0;
    });

    this.appendTo(this.parent());

    return this;
};

Использовать как

$('#list .list_item').sortBy('.count', '.timestmap');

Ниже приведен пример плагина .

Btw, ничто из этого не будет действительно сортировать элементы в самом документе. Смотрите, как это сделать question.

Ответ 2

Производительность:

Я настоятельно рекомендую cache завернутый набор перед выполнением sort().

var $items = $('#list .list_item');
$items.sort(sortDescending);

Это должно дать шипение/ваш DOM перерыв, как большой раз.

Чтобы связать ваше значение метки времени, вы должны расширить свою сортировку.

function sortDescending(a, b) {
  var a_count = parseInt((a).find(".count").text(), 10),
      b_count = parseInt((b).find(".count").text(), 10),
      a_time  = parseInt((a).find(".timestamp").text(), 10),
      b_time  = parseInt((b).find(".count").text(), 10);

  return (a_count < b_count && a_time > b_time);
};

При написании этого я фактически понял, что я не буду разбирать эти значения через element контент. Если вы сгенерируете эти узлы где-то динамически, возможно, лучше использовать метод jQuerys $.data() для хранения даты и использования/доступа в рамках вашего метода сортировки.