Tuesday, 28 February 2017

Roll your own pagination with Bootstrap and MDBootstrap

My previous post about adding MDBootstrap styling to DataTables got me thinking about pagination (that and reading about jQuery Pagination Plugins). As such, I decided to roll my own dynamic pagination. While I got around to it I got to thinking about the bits between the arrows, the filling if you will. I really liked the start/previous mechanism as well as the next/last one. So that within the two ends there's a set of numbers. That's generally fine with a limited set of numbers to page though but what if you've got more than 7, for instance?

I got to thinking about ellipses and how they might do in order to indicate the existence of further numbers past a given set (in my example I'm limiting those to 3). So at the beginning of your paging experience, you can see the first three (with them becoming active as you page through), once you pass a given threshold though you see that there are more behind you than you can see and that you are unable to reach them without going back.

The same mechanism occurs towards the end of your paging experience. I quite like this approach and I've worked up a simple JSFiddle to illustrate the action:

This is the relevant function:

let buildPagination = function(target, start, hits) {
    let pages = Math.ceil(hits / 10);
    let pagesArray = [];
    /*
     * Sort out pages links
     */
    if (pages < 5) {
        for (let i = 0; i < pages; i++) {
            let pageBubble = $("<li></li>", {
                "data-start": i * 10
            });
            (Math.floor((i) * 10) === start) && pageBubble.addClass("active");
            pageBubble.append($("<a></a>", {
                "href": "#",
                "text": i + 1
            }));
            pagesArray.push(pageBubble)
        }
    } else {
        /*
         * Show three pages, and disabled padding if required
         */
        if (start === 0) {
            for (let i = 0; i < 3; i++) {
                let pageBubble = $("<li></li>", {
                    "data-start": i * 10
                });
                (Math.floor((i) * 10) === start) && pageBubble.addClass("active");
                pageBubble.append($("<a></a>", {
                    "href": "#",
                    "text": i + 1
                }));
                pagesArray.push(pageBubble);
            }
            let pageBubble = $("<li></li>", {
                "class": "disabled"
            });
            pageBubble.append($("<a></a>", {
                "href": "#",
                "text": ""
            }));
            pagesArray.push(pageBubble);
        } else if ((start + 10) >= hits) {
            let pageBubble = $("<li></li>", {
                "class": "disabled"
            });
            pageBubble.append($("<a></a>", {
                "href": "#",
                "text": ""
            }));
            pagesArray.push(pageBubble);
            for (let i = pages - 3; i < pages; i++) {
                let pageBubble = $("<li></li>", {
                    "data-start": i * 10
                });
                (Math.floor((i) * 10) === start) && pageBubble.addClass("active");
                pageBubble.append($("<a></a>", {
                    "href": "#",
                    "text": i + 1
                }));
                pagesArray.push(pageBubble)
            }
        } else {
            let pageBubble = $("<li></li>", {
                "class": "disabled"
            });
            pageBubble.append($("<a></a>", {
                "href": "#",
                "text": ""
            }));
            pagesArray.push(pageBubble);
            let current = Math.floor(start / 10);
            for (let i = current - 1; i < current + 2; i++) {
                let pageBubble = $("<li></li>", {
                    "data-start": i * 10
                });
                (i === current) && pageBubble.addClass("active");
                pageBubble.append($("<a></a>", {
                    "href": "#",
                    "text": i + 1
                }));
                pagesArray.push(pageBubble)
            }
            pageBubble = $("<li></li>", {
                "class": "disabled"
            });
            pageBubble.append($("<a></a>", {
                "href": "#",
                "text": ""
            }));
            pagesArray.push(pageBubble);
        }
    }
    /*
     * Left links (Start and previous)
     */
    let first_page = $("<li></li>", {
        "data-start": 0
    });
    (start === 0) && first_page.addClass("disabled");
    first_page.append($("<a></a>", {
        "href": "#!"
    }).append($("<i></i>", {
        "class": "material-icons",
        "text": "first_page"
    })));
    let chevron_left = $("<li></li>", {
        "data-start": ~~start - 10
    });
    (start === 0) && chevron_left.addClass("disabled");
    chevron_left.append($("<a></a>", {
        "href": "#!"
    }).append($("<i></i>", {
        "class": "material-icons",
        "text": "chevron_left"
    })));
    /*
     * Right link (Next and last)
     */
    let chevron_right = $("<li></li>", {
        "data-start": start + 10
    });
    ((start + 10) >= hits) && chevron_right.addClass("disabled");
    chevron_right.append($("<a></a>", {
        "href": "#!"
    }).append($("<i></i>", {
        "class": "material-icons",
        "text": "chevron_right"
    })));
    let last_page = $("<li></li>", {
        "data-start": Math.floor(hits / 10) * 10
    });
    ((start + 10) >= hits) && last_page.addClass("disabled");
    last_page.append($("<a></a>", {
        "href": "#!"
    }).append($("<i></i>", {
        "class": "material-icons",
        "text": "last_page"
    })));
    /*
     * Add out links
     */
    target.empty();
    target.append(first_page);
    target.append(chevron_left);
    target.append(pagesArray);
    target.append(chevron_right);
    target.append(last_page);
};

This did take me far too long to figure out though as the Maths was complicated (at least it was for me), and I'm still not overly sure that it's correct so if anyone want to take a look please do and tell me where I've gone wrong.