DataTables: how can I avoid column sorting if I ha

2019-08-07 07:26发布

问题:

I have a DataTables table, which has checkboxes and popups in some of the header columns. (Plus I use FixedColumn and ColReorder plugins too). A mock-up of what I have in a jsfiddle is at the bottom

My problem is that if a user tries to check the checkboxes or push the popups, the sorting event takes over everything. The jsfiddle page is not functioning fully, because in my app I receive the events for the checkbox click, but it's too late at that time, the sorting also happens. The sort icon is just a background CSS for the header cells, and the sorting event is registered for the whole header cell by DataTables.

Plans to solve this problem:

  1. Register handlers and try to prevent the running of Datatables's own handlers. As of now, if I also register an event handler for the header cells, I'll get the event only after DataTable's handler, the events are delivered in registration order. I also registered handlers for the checkbox and the popup itself, but those are also delivered only after DataTables's handlers (bubbling up). This could only work if I could somehow register my handler before DataTables's own handler, but I haven't found an entry point to do that. The table DOM should be already generated, when the event is registered. Right now I see too late occasions, I can only register.
  2. Finding an API call point where I could cancel DataTables's sorting. The order event (http://datatables.net/reference/event/order) seems to be after the fact, I don't see a way for cancellation.
  3. At some point I restructured the non-fixed column headers to contain two rows: the top row was for sorting and the bottom contained my checbox and popup controls. That seemed to work except that it turned out that it doesn't work with the ColReorder plugin. I need that plugin, and it only reordered the top row of the header, the bottom stayed there. If I could fix that, it would be a solution also.

http://jsfiddle.net/csabatoth/pgue1sf5/8/

var initPage = function () {
    var columnsArray = [
        { "title": "index", "class": "dt-center" },
        { "title": "lastname", "class": "dt-head-center dt-body-left" },
        { "title": "firstname", "class": "dt-head-center dt-body-left" },
        { "title": '<div>foo1</div><input type="checkbox" class="filterchk" checked="checked">&nbsp;<select class="paramsel"><option value="1" selected="selected"/><option value="2"/><option value="3"/></select>', "class": "dt-head-center dt-body-left" },
        { "title": '<div>foo2</div><input type="checkbox" class="filterchk" checked="checked">&nbsp;<select class="paramsel"><option value="1" selected="selected"/><option value="2"/><option value="3"/></select>', "class": "dt-center rulecolumn" },
        { "title": '<div>foo3</div><input type="checkbox" class="filterchk" checked="checked">&nbsp;<select class="paramsel"><option value="1" selected="selected"/><option value="2"/><option value="3"/></select>', "class": "dt-center rulecolumn" },
        { "title": '<div>foo4</div><input type="checkbox" class="filterchk" checked="checked">&nbsp;<select class="paramsel"><option value="1" selected="selected"/><option value="2"/><option value="3"/></select>', "class": "dt-center rulecolumn" },
        { "title": '<div>bar1</div><input type="checkbox" class="filterchk" checked="checked">&nbsp;<select class="paramsel"><option value="1" selected="selected"/><option value="2"/><option value="3"/></select>', "class": "dt-center rulecolumn" },
        { "title": '<div>bar2</div><input type="checkbox" class="filterchk" checked="checked">&nbsp;<select class="paramsel"><option value="1" selected="selected"/><option value="2"/><option value="3"/></select>', "class": "dt-center rulecolumn" },
        { "title": '<div>bar3</div><input type="checkbox" class="filterchk" checked="checked">&nbsp;<select class="paramsel"><option value="1" selected="selected"/><option value="2"/><option value="3"/></select>', "class": "dt-center rulecolumn" }
    ];

    var dataArray = [
        [ 1, "aaa", "rrr", "x", "x", "x", "x", "x", "x", "x" ],
        [ 2, "bbb", "qqq", "x", "x", "x", "x", "x", "x", "x" ],
        [ 3, "ccc", "ppp", "x", "x", "x", "x", "x", "x", "x" ],
        [ 4, "ddd", "ooo", "x", "x", "x", "x", "x", "x", "x" ],
        [ 5, "eee", "nnn", "x", "x", "x", "x", "x", "x", "x" ],
        [ 6, "fff", "mmm", "x", "x", "x", "x", "x", "x", "x" ],
        [ 7, "ggg", "lll", "x", "x", "x", "x", "x", "x", "x" ],
        [ 8, "hhh", "kkk", "x", "x", "x", "x", "x", "x", "x" ],
        [ 9, "iii", "jjj", "x", "x", "x", "x", "x", "x", "x" ]
    ];

    viewModel.table = $('#MyTable').DataTable({
        dom: "Rrtip",
        autoWidth: false,
        deferRender: true,
        info: true,
        lengthChange: false,
        ordering: true,
        orderMulti: true,
        orderFixed: {
            pre: [0, 'asc'],
            post: [1, 'asc']
        },
        paging: true,
        pagingType: "full_numbers",
        renderer: "bootstrap",
        processing: true,
        scrollX: true,
        scrollY: false,
        searching: false,
        columns: columnsArray,
        data: dataArray,
        initComplete: function (settings, json) {
            viewModel.attachTableEventHandlers();
        },
        displayLength: 5,
        colReorder: {
            fixedColumnsLeft: 3,
            fixedColumnsRight: 0
        }
    });
    new $.fn.dataTable.FixedColumns(viewModel.table, {
        leftColumns: 3
    });

回答1:

I have read only the title of the question and the first 3 lines of it (so I hope it helps), but if all that you need is to stop the propagation of the click even of the clicked checkbox that is located inside the table header you should add onclick="event.stopPropagation()" to that checkbox, or even better a function that will work on IE (old) and other browsers

//taken from my yadcf plugin, you can call it in your code like this:
//yadcf.stopPropagation(event)
function stopPropagation(evt) {
    if (evt.stopPropagation !== undefined) {
        evt.stopPropagation();
    } else {
        evt.cancelBubble = true;
    }
}

and in your checkbox add onclick="stopPropagation(event)"

for your select you should also add onmousedown="..." so it look like this:

<select onclick="event.stopPropagation()"  
        onmousedown="event.stopPropagation()"

Here is a working jsfiddle, I only fixed the first checkbox/select



回答2:

To reiterate my attempts:

  1. I couldn't prevent DataTables itself from registering to that click event, or hijack that click.
  2. I also didn't find API where I could prevent it.
  3. The complex header first looked like a good solution, until I realized that it didn't work with ColReorder.

So the only method I could think of is to take a deep breath and hack DataTables itself:

function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
{
    return;

So I added that return statement to the beginning of the function body. This will prevent the attachment of the listener. I left the unaccessable code below the return to cause as less modification as possible. Problems with this modification:

  1. Every time I upgrade DataTables, I need to repeat this hack.
  2. I need to perform the hack on the minimized version too: function Ka(a,b,c,d){return;var e=a.aoColumns[c];Ta(b ...
  3. Now that the click doesn't work, I need attach my own listeners, and call the sorting programatically, if the click was not on an input element.

    $('.dataTables_scrollHead thead th').on('click', function (e) {
        if ($(e.target).hasClass("yadcf-filter-reset-button") || $(e.target).hasClass("paramchk") || $(e.target).hasClass("paramsel") || $(e.target).is("option"))
            return;
        var colIdxStr = $(this).attr("data-column-index");
        var colIdx = parseInt(colIdxStr, 10);
        viewModel.sortColumn(colIdx, e.shiftKey);
    });
    

What you see here is a combination of yacdf filters or popups ("option") or edit boxes marked with "paramsel" class by me or check boxes marked by "paramchk" class by will receive the click event, and I don't sort. But otherwise I need to programatically sort. The following code even tries to support multiple column sorting if shift key is pressed.

viewModel.sortColumn = function (colIdx, shiftPressed) {
    var order = viewModel.table.order();
    var direction = "";
    for (var i = 0; i < order.length; i++) {
        if (order[i][0] == colIdx) {
            direction = order[i][1];
            break;
        }
    }
    if (direction === "")
        direction = "desc";
    else if (direction === "desc")
        direction = "asc";
    else
        direction = "";
    if (shiftPressed) {
        order.push([colIdx, direction]);
        viewModel.table.order(order).draw();
    } else {
        viewModel.table.order([colIdx, direction]).draw();
    }
}

One more needed piece is the decorating the header cells with the column index:

viewModel.table = $('#reportTable').DataTable({
    ...
    headerCallback: function (thead, data, start, end, display) {
        $(thead).find("th").each(function (thindex, thelement) {
            $(thelement).attr("data-column-index", thindex);
        });
    }
});

This is all so complicated in the end, that please don't hesitate to tell me if it can be made simpler, or you know any kind of elegant or other solution.



回答3:

not sure if this would help, but I was in a similar situation - my header contains a dropdown button, which opens a .dropdown-pane pane element with a slider. Every slider move would have caused column sorting. I was desperate and wrote nearly 100 lines trying to unbind and override this behavior. At the end I would either disable both sorting and slider with preventDefault() (obviously, not a solution), or nothing would work at all. Surprisingly, after few hours of mistakes and trials, I found that this works:

$('.dropdown-pane').click(function() {
return false;
});