简体   繁体   中英

How to paginate multiple HTML tbodies with jQuery

I have this table drawn from d3 where it has grouped entries that are in separate tbodies so the entries stay grouped when sorted. I am using the tablesorter plugin to help sort the tbodies which as been nothing but awesome, however at this point, you cannot use their pagination widget with the sortable tbodies one.

I have d3 handling all the table drawing, so it would be great if there was a plugin out there that can handle this. I had to use d3 this time since I needed a sortable nested table that wouldn't case the groups to break apart when sorting, otherwise the datatables plugin would have been good. It's sort of like a billing statement where you can view several accounts that have a summary and then the details. D3 allowed the nested groupings to happen and the tablesorter plugin was great to allow sorting of both the details and the totals of the data. Is there a way D3 or any other tool can do pagination for all the tbodies?

To solve this problem, I tried to use traditional paginators, but those only work on regular tables. This is a D3 table that has nested tables and it's important it can sort on the details and summaries. Here's a fiddle of what I am dealing with: https://jsfiddle.net/mcsmitheslc/mns6183o/

This is a paginator I had somewhat working, but it seems like overkill do do it all by hand like this:

    // Paginate the table, there's a plugin but it's not able to handle the nesting yet
    $('.paginated').each(function () {
        var $table = $(this);
        var itemsPerPage = 25;
        var currentPage = 0;
        var pages = Math.ceil($table.find("tr.detail-row:not(:has(th))").length / itemsPerPage);
        var allRows = merged.length;
        var shownRows = $('#table .detail-row:visible').length;

        $table.bind('repaginate', function () {
            if (pages > 1) {
                var pager;
                if ($table.next().hasClass("pager")) {
                    pager = $table.next().empty();
                } else
                    pager = $('<div class="pager" id="" style="padding-top: 20px; direction:ltr; " align="center"></div>');

                $('<button class="button hollow" id="first-button"></button>').text(' « First ').bind('click', function () {
                    currentPage = 0;
                    $table.trigger('repaginate');

                }).appendTo(pager);

                $('<button class="button hollow" id="previous-button"> « Prev </button>').bind('click', function () {
                    if (currentPage > 0)
                        currentPage--;
                    $table.trigger('repaginate');
                }).appendTo(pager);

                var startPager = currentPage > 2 ? currentPage - 2 : 0;
                var endPager = startPager > 0 ? currentPage + 3 : 5;
                if (endPager > pages) {
                    endPager = pages;
                    startPager = pages - 5;
                    if (startPager < 0)
                        startPager = 0;
                }

                for (var page = startPager; page < endPager; page++) {
                    $('<button id="pager-button" class="button tiny"><span id="pg' + page + '" class="' + (page == currentPage ? 'pg-selected' : 'pg-normal') + '"></span></button>').text(page + 1).bind('click', {
                        newPage: page
                    }, function (event) {
                        currentPage = event.data['newPage'];
                        $table.trigger('repaginate');
                    }).appendTo(pager);
                }

                $('<button class="button hollow" id="next-button"> Next » </button>').bind('click', function () {
                    if (currentPage < pages - 1)
                        currentPage++;
                    $table.trigger('repaginate');
                    console.log('Viewing ' +currentPage);
                }).appendTo(pager);
                $('<button class="button hollow" id="last-button"> Last » </button>').bind('click', function () {
                    currentPage = pages - 1;
                    $table.trigger('repaginate');
                    var rowCount = $('#table tbody tr').length;
                    var numOfVisibleRows = $('tbody tr:visible').length;
                    var diff = currentPage * 25;
                    var remainingRows = allRows - diff;
                    console.log('Viewing ' +currentPage);
                    console.log(diff + 'sliced' +remainingRows + ' on the last page');
                }).appendTo(pager);

                if (!$table.next().hasClass("pager"))
                    pager.insertAfter($table);
            }
            $table.find(
                'tbody tr:not(:has(th))').hide().slice(currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage).show();

        });

        $table.trigger('repaginate');

    });

Glad you came back with what you've tried until now based off the comment I posted. I couldn't work on it yesterday as I got too busy with work. Anyway, got it working now.

I made a few changes to it and also added certain additional features (First, Prev, Next, Last) and seems to be working okay now.

HTML added:

<div id="toggle-buttons">
<button class="button" data-id="first">
          First Page
        </button>
<button class="button disabled" data-id="previous">
          Prev Page
        </button>
<button class="button" data-id="next">
          Next Page
        </button>
<button class="button" data-id="last">
          Last Page
        </button>            
<button class="button" id="all-button">
          Show All
</button>
</div>

Javascript to paginate the table based on the button click:

const totalBodies = $('tbody').length;
var counter = 0; // set counter initially to 0

d3.selectAll('#toggle-buttons button').on('click', function () {

if(d3.select(this).classed('disabled'))
    return ;

switch(d3.select(this).attr('data-id')) {
    case 'first':
        counter = 0;
    d3.select(this.parentNode).select('[data-id="previous"]').classed('disabled', true);
    d3.select(this.parentNode).select('[data-id="next"]').classed('disabled', false);
            break;
  case 'last':
        counter = totalBodies-1;
    d3.select(this.parentNode).select('[data-id="next"]').classed('disabled', true);        
    d3.select(this.parentNode).select('[data-id="previous"]').classed('disabled', false);        
    break;
  case 'next':
            counter++;
    if(counter === totalBodies-1) {
        d3.select(this).classed('disabled', true);
    }
        d3.select(this.parentNode).select('[data-id="previous"]').classed('disabled', false);         
    break;
  case 'previous':
            counter--;
    if(!counter) {
        d3.select(this).classed('disabled', true);
    }
        d3.select(this.parentNode).select('[data-id="next"]').classed('disabled', false);         
            break;
}
redraw(counter);
});

Explanation (pretty much understandable I suppose):

  1. Switch case to determine the button click based on which the counter will change. Important Toggling the disabled class on the buttons.
  2. redraw(counter) on every button click (unless it has a disabled class)

Here's a fork of your fiddle:

https://jsfiddle.net/shashank2104/noxg31eq/

I'm selecting the first page by default. You can change that according to the requirement and add the disabled class in the initial phase accordingly. Also, I see a table nested within a table which doesn't seem right - maybe tablesorter adds a table tag as well. You can take a look at that.

Hope this helps.

I got some of it myself based off what Shashank posted:

        let tbodiesPerPage = 10;
    const totalBodies = $('tbody').length;

    d3.select("#buttons").datum({
        portion: 0
    });

    // the chain select here pushes the datum onto the up and down buttons also
    d3.select("#buttons").select("#previous-button").on("click", function (d) {
        console.log('next was ', d.portion);
        if (d.portion - tbodiesPerPage >= 0) {
            d.portion -= tbodiesPerPage;
            redraw(d.portion);
        }
    });

    d3.select("#buttons").select("#next-button").on("click", function (d) {
        // let the bodies hit the floor
        console.log('previous was', d.portion);
        if (d.portion < (totalBodies - tbodiesPerPage)) {
            d.portion += tbodiesPerPage;
            redraw(d.portion);
        }
    });

    function redraw(start) {
        d3
            .select("table")
            .selectAll("tr")
            .style("display", function (d, i) {
                return i >= start && i < start + tbodiesPerPage ? null : "none";
            });
    }

    redraw(0);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM