jQuery Pagination Plugin - Can't click “next”

2019-08-17 13:33发布

问题:

This is regarding the following jQuery Pagination code: https://github.com/gbirke/jquery_pagination/blob/master/src/jquery.pagination.js

Made some customisations to the original code already, with this is a related post regarding the changes made.

Here is the updated code now: https://jsfiddle.net/a6gx5z2o/13/

/**
 * This jQuery plugin displays pagination links inside the selected elements.
 * 
 * This plugin needs at least jQuery 1.4.2
 *
 * @author Gabriel Birke (birke *at* d-scribe *dot* de)
 * @version 2.2
 * @param {int} maxentries Number of entries to paginate
 * @param {Object} opts Several options (see README for documentation)
 * @return {Object} jQuery Object
 */
 (function($){
    /**
     * @class Class for calculating pagination values
     */
    $.PaginationCalculator = function(maxentries, opts) {
        this.maxentries = maxentries;
        this.opts = opts;
    };

    $.extend($.PaginationCalculator.prototype, {
        /**
         * Calculate the maximum number of pages
         * @method
         * @returns {Number}
         */
        numPages:function() {
            return Math.ceil(this.maxentries/this.opts.items_per_page);
        },
        /**
         * Calculate start and end point of pagination links depending on 
         * current_page and num_display_entries.
         * @returns {Array}
         */
        getInterval:function(current_page)  {
            var ne_half = Math.floor(this.opts.num_display_entries/2);
            var np = this.numPages();
            var upper_limit = np - this.opts.num_display_entries;
            var start = current_page > ne_half ? Math.max( Math.min(current_page - ne_half, upper_limit), 0 ) : 0;
            var end = current_page > ne_half?Math.min(current_page+ne_half + (this.opts.num_display_entries % 2), np):Math.min(this.opts.num_display_entries, np);
      return {start:start, end:end};
        }
    });

    // Initialize jQuery object container for pagination renderers
    $.PaginationRenderers = {};

    /**
     * @class Default renderer for rendering pagination links
     */
    $.PaginationRenderers.defaultRenderer = function(maxentries, opts) {
        this.maxentries = maxentries;
        this.opts = opts;
        this.pc = new $.PaginationCalculator(maxentries, opts);
    };
    $.extend($.PaginationRenderers.defaultRenderer.prototype, {
        /**
         * Helper function for generating a single link (or a span tag if it's the current page)
         * @param {Number} page_id The page id for the new item
         * @param {Number} current_page 
         * @param {Object} appendopts Options for the new item: text and classes
         * @returns {jQuery} jQuery object containing the link
         */
        createLink:function(page_id, current_page, appendopts){
            var lnk, np = this.pc.numPages();
            page_id = page_id<0?0:(page_id<np?page_id:np-1); // Normalize page id to sane value
            appendopts = $.extend({text:page_id+1, classes:""}, appendopts||{});
            var weekamount;
            weekamount = Math.ceil(appendopts.text / 7) * 7;
            weekdaysamount = weekamount - 7;
            if(appendopts.text > 7) {
                appendopts.text = appendopts.text - weekdaysamount;
            }
            if(page_id == current_page){
                lnk = $("<span class='current'>" + appendopts.text + "</span>");
            }
            else
            {
                lnk = $("<a>" + appendopts.text + "</a>")
                .attr('href', this.opts.link_to.replace(/__id__/,page_id));
            }
            if(appendopts.classes){ lnk.addClass(appendopts.classes); }
            if(appendopts.rel){ lnk.attr('rel', appendopts.rel); }
            lnk.addClass('page' + page_id);
            lnk.data('page_id', page_id);
            return lnk;
        },
        // Generate a range of numeric links 
        appendRange:function(container, current_page, start, end, opts) {
            var i;
            for(i=start; i<end; i++) {
                this.createLink(i, current_page, opts).appendTo(container);
            }
        },
        getLinks:function(current_page, eventHandler) {
            var begin, end,
                interval = this.pc.getInterval(current_page),
                np = this.pc.numPages(),
                fragment = $("<div class='pagination'></div>");

            // Generate "Previous"-Link

            currentPrevPageCalc = Math.ceil(current_page / 7) * 7;
            currentPrevPage = currentPrevPageCalc - 14;
            fragment.append(this.createLink(currentPrevPage, current_page, {text:this.opts.prev_text, classes:"prev",rel:"prev"}));

            // Generate starting points
            if (interval.start > 0 && this.opts.num_edge_entries > 0)
            {
                end = Math.min(this.opts.num_edge_entries, interval.start);
                this.appendRange(fragment, current_page, 0, end, {classes:'sp'});
                if(this.opts.num_edge_entries < interval.start && this.opts.ellipse_text)
                {
                    $("<span>"+this.opts.ellipse_text+"</span>").appendTo(fragment);
                }
            }
            // Generate interval links
            this.appendRange(fragment, current_page, interval.start, interval.end);
            // Generate ending points
            if (interval.end < np && this.opts.num_edge_entries > 0)
            {
                if(np-this.opts.num_edge_entries > interval.end && this.opts.ellipse_text)
                {
                    $("<span>"+this.opts.ellipse_text+"</span>").appendTo(fragment);
                }
                begin = Math.max(np-this.opts.num_edge_entries, interval.end);
                this.appendRange(fragment, current_page, begin, np, {classes:'ep'});

            }
            // Generate "Next"-Link
            currentNextPage = Math.ceil(current_page / 7) * 7;
            fragment.append(this.createLink(currentNextPage, current_page, {text:this.opts.next_text, classes:"next",rel:"next"}));

            $('a', fragment).click(eventHandler);
            return fragment;
        }
    });

    // Extend jQuery
    $.fn.pagination = function(maxentries, opts){

        opts = $.extend({
            items_per_page:7,
            num_display_entries:35,
            current_page:1,
            num_edge_entries:0,
            link_to:"#",
            prev_text:"<<",
            next_text:">>",
            ellipse_text:"...",
            prev_show_always:true,
            next_show_always:true,
            renderer:"defaultRenderer",
            show_if_single_page:true,
            load_first_page:true,
            callback:function(){return false;}
        },opts||{});

        var containers = this,
            renderer, links, current_page;

        /**
         * This is the event handling function for the pagination links. 
         * @param {int} page_id The new page number
         */
        function paginationClickHandler(evt){
            var links, 
                new_current_page = $(evt.target).data('page_id'),
                continuePropagation = selectPage(new_current_page);
            if (!continuePropagation) {
                evt.stopPropagation();
            }
            return continuePropagation;
        }

        /**
         * This is a utility function for the internal event handlers. 
         * It sets the new current page on the pagination container objects, 
         * generates a new HTMl fragment for the pagination links and calls
         * the callback function.
         */
        function selectPage(new_current_page) {
            // update the link display of a all containers
            containers.data('current_page', new_current_page);
            links = renderer.getLinks(new_current_page, paginationClickHandler);
            containers.empty();
            links.appendTo(containers);
            // call the callback and propagate the event if it does not return false
            var continuePropagation = opts.callback(new_current_page, containers);
            return continuePropagation;
        }

        // -----------------------------------
        // Initialize containers
        // -----------------------------------
        current_page = parseInt(opts.current_page, 10);
        containers.data('current_page', current_page);
        // Create a sane value for maxentries and items_per_page
        maxentries = (!maxentries || maxentries < 0)?1:maxentries;
        opts.items_per_page = (!opts.items_per_page || opts.items_per_page < 0)?1:opts.items_per_page;

        if(!$.PaginationRenderers[opts.renderer])
        {
            throw new ReferenceError("Pagination renderer '" + opts.renderer + "' was not found in jQuery.PaginationRenderers object.");
        }
        renderer = new $.PaginationRenderers[opts.renderer](maxentries, opts);

        // Attach control events to the DOM elements
        var pc = new $.PaginationCalculator(maxentries, opts);
        var np = pc.numPages();
        containers.off('setPage').on('setPage', {numPages:np}, function(evt, page_id) { 
                if(page_id >= 0 && page_id < evt.data.numPages) {
                    selectPage(page_id);
                }
        });
        containers.off('prevPage').on('prevPage', function(evt){
                var current_page = $(this).data('current_page');
                if (current_page > 0) {
                    selectPage(current_page - 1);
                }
                return false;
        });
        containers.off('nextPage').on('nextPage', {numPages:np}, function(evt){
                var current_page = $(this).data('current_page');
                if(current_page < evt.data.numPages - 1) {
                    selectPage(current_page + 1);
                }
                return false;
        });
        containers.off('currentPage').on('currentPage', function(){
                var current_page = $(this).data('current_page');
                selectPage(current_page);
                return false;
        });

        // When all initialisation is done, draw the links
        links = renderer.getLinks(current_page, paginationClickHandler);
        containers.empty();
        if(np > 1 || opts.show_if_single_page) {
            links.appendTo(containers);
        }
        // call callback function
        if(opts.load_first_page) {
            opts.callback(current_page, containers);
        }
    }; // End of $.fn.pagination block

})(jQuery);


function handlePaginationClick(new_page_index, pagination_container) {
    console.log(new_page_index);
}


$.PaginationCalculator.prototype.getInterval = function (current_page) {
    var num_display_entries = this.opts.num_display_entries;
  var start = Math.floor(current_page / num_display_entries) * num_display_entries;
  var end = Math.min(start + num_display_entries, this.numPages());

  return { start: start, end: end };
};


$("#News-Pagination").pagination(150, {
        items_per_page:7,
        num_display_entries: 7,
        callback:handlePaginationClick
});

What it does is has groups of 1-7 pages, when you click next, it goes to the next set of pages (labeled 1 - 7).

It all works fine, other than when you are on the first page of that group (1). When you are on page 1, you are unable to click the next button.

Does anyone have a suggestions on how this could be fixed?

Much appreciated!

回答1:

It is a bit much to dump the entire plugin code into your question and expect someone to search through it and try to find all of the modifications you have made. I am certainly not prepared to do it.

However, I want to point out a few problems with your approach:

First, you should not be modifying the plugin source directly. In my original answer to you, I made sure to leave the plugin code intact and explicitly overwrote the $.PaginationCalculator.prototype.getInterval implementation outside of the plugin code. Anyone picking-up your code is not going to know that this plugin code has been modified and will be very confused when the behavior does not comply with the documentation.

Secondly, you should not be hard-coding 7s all over the place. This plugin code was intentionally designed to support different configurations. By hard-coding in 7, you have ensured that this code will break for anyone who initializes the plugin with a num_display_entries that is not 7.

Thirdly (and similar to the second point), you should not be modifying the default options within the plugin code. The plugin design allows you to pass, for example, your own values for prev_text and next_text at initialization.

Although I would not attempt to debug your modifications, I did take a look at original plugin source to see if your new requirement could be met. I believe I was able to meet your requirement by changing just a single line of code. I simply changed the default link text that is set in the $.PaginationRenderers.defaultRenderer.prototype.createLink method. Again, in order to preserve the plugin source, I would recommend explicitly overwriting the method you want to modify. I have copied the method and have added a comment regarding the line of code I have changed:

$.PaginationRenderers.defaultRenderer.prototype.createLink = function(page_id, current_page, appendopts) {
    var lnk, np = this.pc.numPages();
    page_id = page_id<0?0:(page_id<np?page_id:np-1); // Normalize page id to sane value
    appendopts = $.extend({
        //text: page_id+1,
        // Note: This is the only line of code I have changed from the original.
        // It ensures that the link text is only ever within a range of 1 - num_display_entries
        text: (page_id % this.opts.num_display_entries) + 1,
        classes: ""
    }, appendopts||{});
    if(page_id == current_page){
        lnk = $("<span class='current'>" + appendopts.text + "</span>");
    }
    else
    {
        lnk = $("<a>" + appendopts.text + "</a>")
            .attr('href', this.opts.link_to.replace(/__id__/,page_id));
    }
    if(appendopts.classes){ lnk.addClass(appendopts.classes); }
    if(appendopts.rel){ lnk.attr('rel', appendopts.rel); }
    lnk.data('page_id', page_id);
    return lnk;
};

I have created a JS Fiddle demo for reference.