You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firefox浏览器中DataTable排序二次点击失效问题问询

DataTable排序在Firefox二次点击失效问题排查与修复

问题现象

DataTable自定义排序功能在Chrome中运行正常,但Firefox下首次点击排序列可正常工作,第二次点击后无任何反应。

核心原因分析

  1. table.ajax.json()的浏览器差异:Firefox中该方法仅在真实AJAX请求完成后才会存储响应数据,第二次点击时因为skipAjax=true跳过了真实请求,此时无法正确获取上次响应数据,导致排序逻辑无数据可处理。
  2. 状态保存逻辑冲突:开启stateSave: true后,DataTable自动保存表格状态,手动修改响应数据重绘的操作会和自动保存的状态冲突,导致Firefox中重绘不生效。
  3. 表头选择器错误:adjustSortIcon函数中$("th.sortable", 0)的上下文参数无效,无法正确选中目标表头,导致排序状态图标更新异常,同时可能影响后续点击逻辑。

修复方案

  1. 缓存AJAX响应到全局变量:不再依赖table.ajax.json(),在每次真实AJAX请求完成后缓存响应数据,手动排序时直接使用缓存数据。
  2. 关闭状态保存:暂时关闭stateSave(若业务不需要状态持久化),避免自动状态与手动排序逻辑冲突。
  3. 修正表头选择器:确保adjustSortIcon能正确选中排序列表头。
  4. 优化排序逻辑:补充排序相等情况的返回值,避免排序不稳定问题。

修改后的完整代码

JavaScript部分

$(document).ready(function () {
    setUpTable();
});

function setUpTable() {
    var skipAjax          = false;
    var skipAjaxDrawValue = 0;
    var isSortAscending   = true;
    var lastAjaxResponse = null; // 新增:缓存上次AJAX响应

    table = $("#paymentTable").DataTable({
        bProcessing: false,
        bSort: false,
        destroy: true,
        dom: '<"top"i>rt<"bottom"lp><"clear">',
        info: false,
        paging: false,
        serverSide: true,
        stateSave: false, // 修改:关闭状态保存避免冲突
        targets: "no-sort",
        ajax: {
            dataType: "JSON",
            type: "POST",
            url: "/BeneficiaryPayment/Search",
            data: function (data) {
                if (skipAjax) {
                    skipAjaxDrawValue = data.draw;
                }
                return data;
            },
            beforeSend: function () {                        
                if (skipAjax) {                             
                    if (!lastAjaxResponse) return false; // 无缓存数据则终止
                    // 复制缓存数据,避免修改原数据
                    const response = {...lastAjaxResponse};
                    response.draw  = skipAjaxDrawValue; 
                    isSortAscending    = !isSortAscending;

                    // 执行排序逻辑,补充相等情况的返回值
                    response.data.sort((r1, r2) => {
                        if (r1.TransactionDate < r2.TransactionDate) {
                            return isSortAscending ? -1 : 1;
                        } else if (r1.TransactionDate > r2.TransactionDate) {
                            return isSortAscending ? 1 : -1;
                        }
                        return 0;
                    });
                    adjustSortIcon(isSortAscending);
                    this.success(response); 
                    skipAjax = false;           
                    return false;
                }
                showSpinner();
                return true;
            },
            success: function(response) {
                // 新增:缓存成功响应的数据
                lastAjaxResponse = response;
            },
            complete: function () {
                removeSpinner();
            }
        },
        aoColumns: [
            {
                className: "details-control",
                orderable: false,
                data: null,
                defaultContent: "",
                render: (data) => {
                    if (!data.Beneficiaries?.length && !data.ForwardDrawdowns?.length) return "";
                    const plus_sign = data.ForwardDrawdowns?.length > 0
                        ? "plus-sign-secondary"
                        : "plus-sign";

                    return `<i class="flag ${plus_sign} hasPointer" aria-hidden="true"></i>`
                }
            },
            { mDataProp: (_data, _type, full) => getActionButtons(_data) },
            { mDataProp: "DisplayTransactionDate" },
            { mDataProp: (_data, _type, full) => getEditClientLink(_data) },
            { mDataProp: "ClientId" },
            { mRender: (_data, _type, full) => getTradeTransactionLink(full) },
            { mDataProp: "DisplaySoldAmount" },
            { mRender: (_data, _type, full) => getDisplayBoughtAmount(full) },
            { mDataProp: "Destination" },
            { mDataProp: "PaymentMethod" },
            { mDataProp: "Partner" },
            { mRender: (_data, _type, full) => getDisplayPaymentStatus(full["DisplayPaymentStatus"]) },
            { mRender: (_data, _type, full) => getDisplayRiskGrade(full["DisplayRiskGrade"]) }
        ],
        initComplete: function() {
            $("#btnSearch").on("click", e => {
                const searchText = $("#search").val().trim();
                window.localStorage.setItem(searchTextKey, searchText);

                const paymentStatus = $("#paymentStatuses").val();
                window.localStorage.setItem(paymentStatusKey, paymentStatus);

                table.columns(0).search(searchText);
                table.columns(1).search(paymentStatus);

                isSortAscending = true;
                adjustSortIcon(isSortAscending);
                table.draw();
                e.preventDefault();
            });

            table.on("click",
                "th.sortable",
                function() {
                    skipAjax = true;
                    table.draw();
                }
            );
        },
        "columnDefs": [
            { type: "currency", targets: [colSoldAmount, colBoughtAmount ] }
        ],
        rowCallback: rowCallback
    });
}

function adjustSortIcon(isSortAscending) {
    // 修改:修正选择器,正确选中排序列表头
    const cell = $("#paymentTable thead th.sortable");
    if (isSortAscending) {
        cell.removeClass("sorting_desc").addClass("sorting_asc");
    } else {
        cell.removeClass("sorting_asc").addClass("sorting_desc");
    }
}

HTML部分(无需修改)

<table id="paymentTable" class="table table-striped table-hover">
    <thead>
        <tr>
            <th></th>
            <th></th>
            <th class="sortable sorting_asc">Transaction Date</th>
            <th>Client</th>
            <th>Client Id</th>
            <th>Deal Id</th>
            <th class="text-right">Client Sold</th>
            <th class="text-right">Client Bought</th>
            <th>Destination</th>
            <th>Payment Method</th>
            <th>Partner</th>
            <th class="text-center">Payment Status</th>
            <th class="text-center">Risk Grade</th>
        </tr>
    </thead>
</table>

额外说明

如果业务需要保留stateSave功能,需手动处理状态保存逻辑,在手动排序后更新保存的状态,避免与自动保存的状态冲突。

内容的提问来源于stack exchange,提问作者Alisa T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 20:59:54