65.9K
CodeProject 正在变化。 阅读更多。
Home

用于排序 HTML 表格的 jQuery 插件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (9投票s)

2016 年 4 月 19 日

CPOL

10分钟阅读

viewsIcon

23103

downloadIcon

353

在本文中,我将描述我创建用于排序 HTML 表的 jQuery 插件所采用的方法。

图 1. 演示截图。按“接收日期”降序排序的表

引言

此 jQuery 插件通过选定列中的值对 HTML 进行排序。它可以附加到表的标题行,以便当用户单击列标题时,该表将按该列进行排序。如果用户再次单击列标题,排序将反转。开发人员还可以实现基于其他事件的排序,并将表恢复到其原始顺序。该插件识别数字列和日期列并进行相应排序。

背景

我在我编写的基于浏览器的 文字游戏 中有一个“名人堂”表格。它需要排序功能,所以我创建了这个插件。

限制

  • 对于使用“colspan”或“rowspan”的表,它不能正常工作。
  • 它只识别带有数字成分和单个分隔符的日期。默认分隔符是 '/',但可以使用可选设置进行更改。它识别美国(mm/dd/yy][yy])、英国日期(dd/mm/yy[yy])和欧洲日期([yy]yy/mm/dd)。它不关心日期是否完全有效 - 这是在数据库中填充表时应用程序的职责。它检查列中的每个日期以确定日期格式。如果日期很少且模糊不清,则可能会出错。如果表很小,您可能需要使用可选设置来指定日期格式。
  • 如果在列中找到一个单元格的值,看起来既不像数字也不是日期,它会将整个列视为字符串值列。
  • 当表包含数千行时,它的速度会变慢。

优点

  • 为网页添加 HTML 表排序功能很容易。
  • 它能识别日期和数字并正确排序。有一个选项可以显式指定日期格式。
  • 排序在任何排序中都会保留原始表顺序。这意味着它是稳定的。
  • 表可以恢复到其原始排序顺序。
  • 它不会影响表的 HTML 结构。
  • 它使用 jQuery 和 JavaScript Array sort 方法,因此它可以在大多数浏览器上运行。
  • 在现代浏览器中,对于不足千行的表,它几乎是瞬时的。
  • 字符串排序是区分大小写的,但有一个选项可以覆盖它。
  • 不需要服务器活动。

演示网页

下载中包含两个演示网页。

DemoTable.html

此页面包含一个具有逼真数据的表。请参阅图 1。该表具有一些简单的样式,使其看起来不错。它下面有四个按钮,用于说明插件的基本功能。第一个按钮可让您将表恢复到其原始顺序。接下来的三个按钮允许您为 接收日期 列选择日期格式,以便您可以验证自动日期格式选择算法。有一些代码用于对 价格里程 列进行求和,但这只是为了填充页脚行。

BigDemoTable.html

此页面包含一个包含 3000 行数据的表。请参阅图 2。它旨在测试所有选项和性能测试。该表还具有一些简单的样式,使其看起来不错。在按另一列排序之前按 随机 列排序,有效地将所有其他列随机排序。有一些代码用于生成 EURO NUM 列,用于测试可能的欧洲数字格式。

图 2. 演示截图。大型表随机排序。

将引用添加到您的网页

假设您将 tablesort.js JavaScript 文件放在一个名为 js 的子文件夹中,您将在网页中添加以下行以引用 TableSort jQuery 插件。

        <script src="js/tablesort.js" type="text/javascript"></script>
    

使用代码

使用该插件最简单的方法是将一个点击事件附加到您 HTML 表中的每个标题单元格,以调用该插件。如果我们使用像 $('#DemoTable tr:first').children() 这样的选择器,我们就无需担心标题行是否使用 <td><th> 标签。下面的代码示例假定在页面加载完成后,HTML 表已存在。如果您使用 AJAX 调用加载表,或者动态构建表,那么您将在表创建后附加列标题事件。

        <script type="text/javascript">
        <!--
        $(document).ready(function () {
            // Select your column headers
            $('#DemoTable tr:first').children().click(function (e) {
                // Attach a click event to each cell to invoke the Plugin
                $('#DemoTable').sortByColumn($(this).text());
            });
        });
        // --></script>

如果您想将表恢复到其原始状态,只需调用该插件而不指定列标题。

        $('#DemoTable').sortByColumn();

选项

而不是传递列标题文本,您可以传递一个指定扩展选项集的字面对象。您只需要提供您想要的选项。选项是

            columnText
            dateFormat
            dateDelimiter
            decimalDelimiter
            caseSensitive
            ascendingClass
            descendingClass

columnText 选项必须与标题列第一个单元格中的文本匹配。

            $('#DemoTable').sortByColumn({ columnText: $(this).text()});

dateFormat 选项可以是任何值,但只识别 'US''UK''EU'。默认值为 '',插件将根据列的内容确定使用哪种日期格式。

            $('#DemoTable').sortByColumn({ columnText: $(this).text(), dateFormat: 'UK'});

dateDelimiter 选项可以设置为用于分隔日期组件的单个字符。默认值为 '/'

            $('#DemoTable').sortByColumn({ columnText: $(this).text(), dateDelimiter: '-'});

decimalDelimiter 选项可以设置为用于分隔小数部分的单个字符。默认值为 '.'

            $('#DemoTable').sortByColumn({ columnText: $(this).text(), decimalDelimiter: ','});

caseSensitive 选项可以设置为 truefalse。默认值为 true

            $('#DemoTable').sortByColumn({ columnText: $(this).text(), caseSensitive: false});

ascendingClass 选项是当当前排序列为升序时将添加到其标题单元格的类的名称。descendingClass 选项是当当前排序列为降序时将添加到其标题单元格的类的名称。在这两种情况下,默认值均为 ''

            $('#DemoTable').sortByColumn({ columnText: $(this).text(), ascendingClass: 'redbordertop', descendingClass: 'redborderbottom'});

工作原理

该插件将表中的数据提取到对象数组中。它调用 JavaScript Array.Sort 方法对数组进行排序。然后,它将已排序对象数组中的数据插入回表中。

这与我上次使用的实现不同,它交换了 DOM 元素并使用硬编码的排序算法。它从冒泡排序开始,但我将其切换为梳状排序以提高性能。

该插件通过将列的 jQuery text() 与传递给插件的字符串值进行匹配来识别被单击的列。它在进行比较之前会剥离空格。建议使用 jQuery text 方法指定排序列。

它使用 jQuery data 方法,键为 _dir_,来存储列的当前排序状态。该值可以是 a 表示升序,d 表示降序,或 undefined。如果您的 jQuery 选择器返回了列标题单元格,那么您可以通过检查 $(this).data('_dir_') 的值('a''d'')来确定每列的排序状态。

它使用以下对象来存储每一行的数据。

        var RowData = function () {
            this.Seq = 0;
            this.Key = '';
            this.KeyType = 0;  // 0 = string, 1 = number, 2 = date (mm/dd/yy[yy]), 3 - date (dd/mm/yy[yy]), 4 - date (yy[yy]/mm/dd)
            this.Cells = new Array();
        }

排序是使用 Key 完成的,而不是 Cells 数组中的相应元素。这使得我们能够根据实际数值(数字)或大写字符串(不区分大小写的字符串排序)进行排序。

        //
        // If first time, add original sequence number to each row for stable sorting
        var rownum = 0;
        this.find('tr').not(':first').each(function () {
            if ($(this).data('_seq_')) {
                return false;
            }
            $(this).data('_seq_', rownum);
            rownum++;
        });

表中的数据被传输到一个 RowData 对象数组中。Seq 属性跟踪表的原始序列。它通过使用键为 _seq_ 的 jQuery data 方法保存每行的原始序列来实现。排序比较方法通过查看 Seq 属性来解析相同的键。这确保了相对于原始排序顺序的稳定性。排序在其比较方法中使用 KeyKeyType。以下是执行实际排序的代码。如果未提供 columnText,排序将表恢复到其原始顺序。

        if (settings.columnText) {
            tableData.sort(function (a, b) {
                switch (a.KeyType) {
                    case 0: // string
                    if (a.Key > b.Key) {
                        return sortAsc ? 1 : -1;
                    }
                    if (a.Key < b.Key) {
                        return sortAsc ? -1 : 1;
                    }
                    break;
                    case 1: // Numeric
                    if (parseFloat(a.Key) > parseFloat(b.Key)) {
                        return sortAsc ? 1 : -1;
                    }
                    if (parseFloat(a.Key) < parseFloat(b.Key)) {
                        return sortAsc ? -1 : 1;
                    }
                    break;
                    default: // Date
                    var res = DateCompare(a.KeyType, a.Key, b.Key);
                    if (res == 1) {
                        return sortAsc ? 1 : -1;
                    }
                    if (res == -1) {
                        return sortAsc ? -1 : 1;
                    }
                }
                if (a.Seq > b.Seq) {
                    return 1;
                }
                return -1;
            });
        }
        else {
            tableData.sort(function (a, b) {
                if (a.Seq > b.Seq) {
                    return 1;
                }
                return -1;
            });
        }

请注意,Key 需要与 KeyType 匹配。虽然数字字段可能包含“$12,123.00”,但 key 会使用正则表达式将其简化为其实际数值“12123”。它在排序之前执行此操作,而不是在比较例程中,以减少正则表达式的性能开销。

排序完成后,数据将从 RowData 对象数组复制回 HTML 表。它会跳过第一行和任何页脚行。

        rowX = -1;
        this.find('tr').not(':first').each(function () {
            if (!$(this).parent().is('tfoot')) {
                rowX++;
                var rowData = tableData[rowX];
                $(this).data('_seq_', rowData.Seq);
                colX = -1;
                $(this).children().each(function () {
                    colX++;
                    $(this).text(rowData.Cells[colX]);
                });
            }
        });

确定日期格式

基本思路是,年份可以是任何值,天数只能在 1-31 的范围内,月份只能在 1-12 的范围内。该插件会扫描包含用 dateDelimiter 分隔的三个整数的任何列。它会计算找到的有效天数和月数,并根据结果确定日期格式。在足够大的样本中,您会发现一个位置没有字段超过 12,另一个位置没有字段超过 31。这将告诉您哪个字段代表日、月和年。它不一定总能奏效,因为一种格式的日期在另一种格式中也有效。但几率很高!如果您知道日期格式,可以使用 dateFormat 选项显式设置它。

处理数字

数字的书写方式因国家/地区而异。在英语国家,句点用作小数点分隔符,逗号用作千位分隔符。在欧洲国家,逗号可能用作小数点分隔符,空格或句点用作千位分隔符。为确保正确排序,您可以将 decimalDelimiter 设置为选项。千位分隔符会被一个正则表达式去除,该正则表达式只允许数字、负号和当前的小数分隔符。(var numExp = new RegExp('[^0-9' + settings.decimalDelimiter + '-]+', 'g');)。

这是您将小数点分隔符设置为逗号的选项。

        $('#DemoTable').sortByColumn({ columnText: $(this).text(), decimalDelimiter: ','});

图 3. “接收日期”转换为“EU”,里程使用逗号小数点分隔符排序。

为什么没有符号?

一些表排序系统使用符号在列标题中指示排序状态。如果没有为符号留出空间,或者添加符号会改变列的宽度,这可能会有问题。该插件通过允许开发人员创建可以应用于标题单元格以指示当前升序或降序的 CSS 类,将排序方向的指示方法留给开发人员。在 DemoTable.html 中,开发人员选择使用顶部红色边框表示升序,底部红色边框表示降序。这似乎效果相当好。以下是类以及将它们传递给插件的代码。

        .ascending {
            border-top: solid 3px red;
        }
        …
        .descending {
            border-bottom: solid 3px red;
        }
        …
        $('#DemoTable th').click(function (e) {
            $('#DemoTable').sortByColumn({ columnText: $(this).text(),
                ascendingClass: 'ascending',
                descendingClass: 'descending'});
            });

性能

性能测试是在运行 Windows 10 (64 位) 的戴尔 XPS 计算机上进行的,拥有 16GB RAM 和 Intel i7-3770 处理器 @ 3.40 GHz。它使用了 BigDemoTable.html,该文件包含 3000 行和 8 列。

浏览器 版本 最快时间(秒) 最慢时间(秒)
Google Chrome 50.0.2661.75 m 0.97 1.32
Mozilla Firefox 45.0.2 0.75 0.85
Microsoft Edge 25.10586.0.0 3.66 4.10
Microsoft IE 11.212.10586.0 3.89 4.19

Firefox 比 Chrome 略快,而 Microsoft 的兄弟浏览器则明显较慢。请注意,3000 行相当于 50 到 100 页向下翻页,具体取决于显示器分辨率。大多数应用程序不会向浏览器传递如此大的数据量。

创建 JQuery 插件

这看起来令人生畏,但并非如此困难。基础知识在 jQuery 网站和 Code Project 文章 这里这里 都有介绍。

下载内容

下载包含两个演示页面和一个名为 js 的子文件夹,其中包含 TableSort 插件(tablesort.js)及其精简版本(tablesort.min.js)。

关注点

  • 创建功能强大的 jQuery 插件相对容易,只需很少的代码。
  • 处理各种国际格式和日期使通用排序插件的编码更加复杂。
  • 内部 Array.Sort() 方法速度很快。性能分析显示大部分 CPU 时间都花在了从 html 表中提取数据和将排序后的数据放回。

历史

Code-Project 的第一个版本

© . All rights reserved.