基于计数和日期/时间戳的jQuery div元素排序

3

我目前使用sort函数根据计数值对div元素进行排序。以下是当前的实现方式:(我不确定它是否是一种有效的方法...)

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

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

我在考虑添加一个时间戳字段,但不确定如何扩展以支持此功能。

我有一个包含计数和日期/时间/时间戳的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>

我希望按数量(递减)排序,然后按时间戳(递减 - 最新的在顶部)排序。
非常感谢您的帮助!谢谢 :)
2个回答

5
只有排序比较器函数需要更改。我相信有可用的插件来完成这个任务,你可能想看一下它们,但实现你想要的功能非常简单。每次 sortDescending 方法获取两个 div,并且比较必须遵循您指定的标准:
  1. 首先按计数排序
  2. 如果计数相等,则按时间戳排序
  3. 如果时间戳相等,则返回 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结构,可能会很明显,您可以将此方法泛化以处理任何类型的自定义排序。因此,这里是一个尝试使用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选择器,该选择器将选择一个节点,其文本将确定按其排序的值。我们将优先考虑整数,并首先尝试按数字进行排序,如果两个值都是数字,则进行数字比较,否则进行常规比较。

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');

点击此处查看插件示例。

顺便说一下,这些都不会真正地对文档中的元素进行排序。请参阅此问题以了解如何实现。


谢谢Anurag。我不确定是否有地方让你迷失了,但插件方法和非插件方法都无法对div进行排序。我使用以下代码进行排序:$('#list .list_item).sort(sortBy(compareCount, compareTimestamp)).appendTo('#list'); - Lyon
没错,Lyon。这些方法只会对jQuery包装对象中的列表进行排序,而不是文档中的列表。请参见相关问题appendTo同样有效,你也可以将其移入插件或函数中。 - Anurag
@Lyon - 是的,那个插件代码不完整 - 现在已经修复了,并且这里有一个示例 - http://jsfiddle.net/qJCew/ - Anurag
1
@Lyon - 为了添加分页并保持简单,我建议将内容按页面分开,然后可以像这样使用相同的sortBy插件 $("#list #page-1").sortBy('..', '..', '..');。你期望有多少列表项/页面?如果不超过200-300个,我不会太在意优化。jQuery的结果集不是实时的,因此直接对该列表进行排序是可以的。但是,通过首先收集所有值到一个单独的数组中,然后对其进行排序,您可能会看到性能提升。我曾经在一个SO问题上做过类似的性能测试。如果我能找到链接,我会发出来。 - Anurag
谢谢Anurag,我会尝试一下!非常感谢您在这方面的建议和帮助。 :) - Lyon
显示剩余3条评论

1
性能:
在执行 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内容解析这些值。如果您在某个地方动态生成这些节点,则使用jQuery的$.data()方法存储日期并在排序方法中使用/访问可能是一个更好的主意。

请问"var $items ="和"var items ="之间有什么区别?例如,额外的$符号。我注意到如果.count字段为10或以上,则会在0下方排序,而应该在"9"上方。有没有办法解决这样的问题?谢谢! - Lyon
1
@Lyon: $items 或者 items 都没有区别,它只是用来记住这个变量是一个 jQuery 包装集合。至于第二个问题,请看我的更新,我忘了 parseInt() 的基数。 - jAndy
@Andy:谢谢。我尝试实现了你的排序函数,但是它没有排序。也就是说,顺序仍按照服务器上的默认顺序。关于你使用$.data的建议,我实际上是在获取包含所有列表项的xml文件,然后遍历该文件并将其添加到文档中。我想我可以将它全部存储在$.data中,然后遍历以显示列表。这种方式如何进行排序,并且从性能方面来说是否更有效率?感谢你的帮助! - Lyon
@Andy:在查找有关$.data的更多信息时,我发现它无法在iPhone移动Safari上工作。我需要它在桌面和移动浏览器上运行,所以我想现在使用$.data已经不可能了吧? - Lyon
1
@Lyon:在排序方法中,访问“数据对象”看起来像是$.data(a, 'identifier');。但你说得对,iPhone上的移动Safari浏览器似乎有问题存储/访问jQuery“data”对象。 - jAndy
@Andy:谢谢!我一直在尝试让你的代码工作。parseInt()修复了计数大于9的排序问题。但是时间戳排序没有生效。可能是被排序的值的类型有问题吗?时间戳值都来自于php的time()函数,它返回的是1970年以来的秒数。我还注意到只有这个“有效”a_count < b_count && a_time < b_time,但是a_count < b_count && a_time > b_time不行,其中时间戳是按降序排序的... 你有什么想法吗? - Lyon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接