loading...

Bootstrap 4 – Enhanced pagination using bootpag

In this section, we will learn both how to use Bootstrap’s default pagination and how to overcome its limitations quickly and with minimal effort. We will first populate the section with a set of sample events, and then we will group these events into pages in an effort to reduce the overall length of the section. To add a set of events to the Events section, replace the <p>Lorem Ipsum</p> markup in the services-events div with the following event placeholder text:

    <h3>My Sample Event #1</h3> 
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Curabitur leo dolor,
    fringilla vel lacus at, auctor finibus ipsum. Lorem ipsum dolor sit
    amet,
    consectetur adipiscing elit. Morbi quis arcu lorem. Vivamus 
    elementum 
    convallis
    enim sagittis tincidunt. Nunc feugiat mollis risus non dictum. 
    Nam commodo nec
    sapien a vestibulum. Duis et tellus cursus, laoreet ante non,
    mollis sem.    
    Nullam vulputate justo nisi, sit amet bibendum ligula varius id.</p>

Repeat this text three times so that we now have three sample events displaying on our page under the Events tab (take a look at figure 5.2). Add top padding and a left-hand margin of 2rem to the parent container to offset the events in an effort to make the section look less crowded:

    #services-events .container { 
        margin-left: 2rem; 
        padding-top: 1rem; 
    }
Figure 5.2: Three sample events displayed one below the other within our Events tab (example02.html)

Hit save and refresh. Voila! This looks pretty good already, so why exactly would we want to display the events on separate pages? Well, as we begin adding more and more events, the events will appear below one another. As such, the Events section will grow indefinitely. Pagination is a clever way of avoiding this while allowing us to maintain the ability to list all the MyPhoto events. If you are loading your data from a server (instead of hard-coding it), pagination will also allow you to lessen the data and processing load by only requesting a small chunk of the data with each page.

Bootstrap offers a visually appealing pagination style (shown in figure 5.3) that can be added to any section of your page by applying the pagination class to an unordered list element. The individual list items within this unordered list should have the page-item class applied to them. Applying this class simply sets the element’s display property to inline. Applying the pagination class sets the display of the unordered list to inline-block and adjusts its margins. As such, in order to display pagination with 10 pages (for this example’s sake, we will carry on using 10 pages from now on), add the following markup after the p element of our third event (note how the active class is used on a list item to denote the currently selected page. The pagination-lg and pagination-sm classes can be used to increase or decrease the size of the pagination control):

    <ul class="pagination"> 
        <li class="page-item"><a class="page-link active" 
        href="#">1</a></li> 
        <li class="page-item"><a class="page-link" href="#">2</a></li> 
        <li class="page-item"><a class="page-link" href="#">3</a></li> 
        <li class="page-item"><a class="page-link" href="#">4</a></li> 
        <li class="page-item"><a class="page-link" href="#">5</a></li> 
        <li class="page-item"><a class="page-link" href="#">6</a></li> 
        <li class="page-item"><a class="page-link" href="#">7</a></li> 
        <li class="page-item"><a class="page-link" href="#">8</a></li> 
        <li class="page-item"><a class="page-link" href="#">9</a></li> 
        <li class="page-item"><a  href="#">10</a></li> 
    </ul>

Pagination in Bootstrap 3

When it comes to pagination, the changes from Bootstrap 3 to Bootstrap 4 are not that drastic. The pagination class remains between the two versions. However, in Bootstrap 3 we did not explicitly need to specify which elements were pagination items and which elements were pagination links. As the page-item and page-link classes have only been introduced with Bootstrap 4, one could previously specify the pagination by simply creating an unordered list and applying the pagination class to it:  <ul class="pagination">      <li><a href="#">1</a></li>      <li><a href="#">2</a></li>      <li><a href="#">3</a></li>   </ul>.

With the addition of the preceding markup, we have already reached the end of Bootstrap’s default pagination capabilities. The implementation of the actual pagination is up to us. Specifically, this will involve the following:

  • Grouping our events into pages
  • Detecting the currently active page
  • Toggling the visibility of the various pages depending on the page that is currently selected

As our event grows beyond 10 pages, we will then be required to manually add both a new page and a new list item to the paginator. While implementing the logic for all, this is not quite rocket science; it would be nice if we did not have to be concerned with re-inventing a solution to such a well-known user interface problem. Indeed, there exist plenty of third-party libraries to help us speed up the development of our event’s pagination.

One of the most popular libraries is jQuery.bootpag, a jQuery plugin that allows you to paginate your data. Unfortunately, bootpag (Version 1.0.7 and earlier) currently does not support Bootstrap 4 out of the box, and will therefore require a little bit of tweaking as such. As with all libraries presented in this chapter, jQuery.bootpag is free to use, and its source code, as well as its licensing information, is available on GitHub at https://github.com/botmonster/jquery-bootpag .

Figure 5.3: Bootstrap default pagination (example02.html)

Unsurprisingly, the bootpag NPM package’s name is also bootpag. Go ahead and install it:

    npm install bootpag 

Once the installation is complete, you should see a directory named bootpag under your node_modules directory. Inside bootpag/lib, you should see the following files:

  • jquery.bootpag.js
  • jquery.bootpag.min.js

As always, we want to work with the minified version of our plugin, so go ahead and include jquery.bootpag.min.js within the footer of our page:

<script 
    src="node_modules/bootpag/lib/jquery.bootpag.min.js"></script> 

Before we can start using bootpag, we must understand that the plugin needs containers: one container in which to display the pagination control, and one container to display the content that is to be paginated. In other words, it requires one to divide the area of the Events section among the data that is to be displayed, and one to separate the controls with which the user navigates the data. As a user navigates the data using the pagination control, the content area will be updated with the new content, or, alternatively, the visibility of multiple containers will be toggled.

We will be using the latter approach, that is, we will first divide our events into pages and then use an event listener on the pagination control to toggle the visibility of these various pages. To this end, we must now go ahead and modify our events in the Services section so that each of our events is contained within its own distinct page (that is, using div).

Because our example consists of only three sample events, we will divide the events into two pages. The first page will contain My Sample Event #1 and My Sample Event #2, while the second page will contain My Sample Event #3. We will use a div element to represent an individual page. Each page’s div will consist of a unique id and the word page, followed by the page number. The pagination control will be added after our last event. To do this, add an empty div for holding the pagination below the last of our pages. It should also be assigned a unique id:

   <div >
       <div id="page-1">
          <h3>My Sample Event #1</h3>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
             Curabitur leo dolor, fringilla vel lacus at, auctor     
             finibus ipsum. Lorem ipsum dolor sit amet, consectetur 
             adipiscing elit. Morbi quis arcu lorem. Vivamus elementum 
             convallis enim sagittis tincidunt. Nunc feugiat mollis  
             risus non dictum. Nam commodo nec sapien a vestibulum.   
             Duis et tellus cursus, laoreet ante non, mollis sem.
             Nullam vulputate justo nisi, sit amet bibendum ligula 
             varius id.
          </p>
          <h3>My Sample Event #2</h3>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
             Curabitur leo dolor, fringilla vel lacus at, auctor    
             finibus ipsum. Lorem ipsum dolor sit amet, consectetur   
             adipiscing elit. Morbi quis arcu lorem. Vivamus elementum  
             convallis enim sagittis tincidunt. Nunc feugiat mollis 
             risus non dictum. Nam commodo nec sapien a vestibulum.    
             Duis et tellus cursus, laoreet ante non, mollis sem Nullam 
             vulputate justo nisi, sit amet bibendum ligula varius id.
          </p>
       </div>
     <div id="page-2">
         <h3>My Sample Event #3</h3>
         <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            Curabitur leo dolor, fringilla vel lacus at, auctor finibus 
            ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing 
            elit. Morbi quis arcu lorem. Vivamus elementum convallis 
            enim sagittis tincidunt. Nunc feugiat mollis risus non 
            dictum. Nam commodo nec sapien a vestibulum. Duis et tellus 
            cursus, laoreet ante non, mollis sem.
            Nullam vulputate justo nisi, sit amet bibendum ligula     
            varius id.
         </p>
      </div>
   <div ></div>
</div>

Before we are able to actually use our pagination control, we must inform bootpag of its container. We do so by calling the bootpag function on our element, passing a configuration object as a parameter that contains our desired page count (10 in our case). Insert the following code into the footer of our HTML document:

    $('#services-events-pagination').bootpag({ 
        total: 10 
    }).on("page", function(event, num){});

The bootpag function will render the control to the element with an id equal to services-events-pagination. However, note the on event listener with the page parameter; this is our event listener that will invoke the code contained within the (currently empty) callback as the user uses the pagination control to change pages. However, before we can implement the page – change logic that will toggle the visibility of our individual pages, we must first hide our pages. To this end, we must update our myphoto.css file.

Now, one obvious approach would be to add a style for each one of our individual pages, identifying them by their id. As the number of our events grows, this will seriously bloat our style sheet, as you will be required to add a new CSS rule for each new page. A much neater approach would be to wrap our pages within their own container and then use CSS selectors to hide all the pages (that is, div elements) within this content area. To achieve this, first wrap the pages inside a new container div and assign this container a unique id:

    <div id="services-events-content"> 
        <div id="page-1"> 
            <h3>My Sample Event #1</h3> 
            <p>...</p> 
            <h3>My Sample Event #2</h3> 
            <p>...</p>
        </div>
        <div > 
            <h3>My Sample Event #3</h3> 
            <p>...</p>
        </div>
    </div>

Did you know?

There is an easier way to implement the aforementioned code. You can use Bootstrap’s d-none class and toggle it in the callback. Try solving this yourself! Hint: Refer to Chapter 8, Utilities.

Then we update our style sheet so that the individual page div elements held within this new container are hidden by default:

    #services-events-content div
    {
        display: none;
    }

Save and hit refresh. All of our events should now be hidden.

Now all we need to do is implement the logic that makes our individual pages visible as the user navigates. To this end, we complete the currently empty callback function so that it first hides all the pages and only then displays the currently selected page. Hiding all the pages instead of the previous page makes our code much cleaner, as we require no logic to determine the previously selected page; instead, we just use a CSS selector to hide all the  div elements contained within our services-events-content container. The bootpag plugin informs us of the currently selected page number through the second parameter (here named num) passed to our callback function. As such, we can use this page number to construct the id of the div (page) that we wish to make visible:

    $('#services-events-pagination').bootpag({ 
        total: 10 
    }).on("page", function(event, num){ 
        $('#services-events-content div').hide(); 
        var current_page = '#page-' + num; 
        $(current_page).show(); 
    });

Seeing how our style sheet hides all the pages, we should include a statement that makes the first page visible as the user first visits our page. To do this, simply add $('#page-1').show(); to the footer of our document so that our code takes the following structure:

    $('#page-1').show(); 
    $('#services-events-pagination').bootpag({ 
        total: 10 
    }).on("page", function(event, num){  
        // Pagination logic
    });

Take a look at the following screenshot:

Figure 5.4: The display of the bootpag pagination controls is broken for Bootstrap 4, which is because of the changes to the pagination control classes introduced by Bootstrap 4 (example03.html)

Hit save and refresh. While the pagination controls themselves are working, they are not being displayed correctly (refer to figure 5.4). This is due to the previously discussed changes to the pagination controls introduced by Bootstrap 4. Examining jquery.bootpag.js, we can see that the issue lies in constructing the pagination list items on line 130 and onwards. Observe the following code:

return this.each(function(){
var $bootpag, lp, me = $(this),
p = ['
<ul class="', settings.wrapClass, ' bootpag">
'];
if(settings.firstLastUse){
p = p.concat(['
<li data-lp="1" class="', settings.firstClass,
   '"><a href="', href(1), '">', settings.first, '</a></li>
']);
}
if(settings.prev){
p = p.concat(['
<li data-lp="1" ><a href="', href(1), '">', settings.prev, '</a></li>
']);
}

As the pagination items are being created for Bootstrap 3, the problem here lies with the fact that the code generating the items fails to apply the page-item and page-link classes. We can fix this easily by adding two lines of jQuery to the bottom of our HTML document:

    $('[data-lp]').addClass('page-item');
    $('.page-item > a').addClass('page-link');

The first line selects all the elements that have the data-lp attribute (observe how bootpag adds the data-lp attribute to all li pagination elements) and adds the page-item class. Similarly, the second line of code selects the anchor children of any element with the page-item class and adds the page-link class. This effectively turns the bootpag pagination markup into a Bootstrap 4 – compatible pagination markup:

<ul class="pagination bootpag">
    <li data-lp="1" class="prev disabled page-item">
        <a href="javascript:void(0);" >«</a>
    </li>
    ...
</ul>
Figure 5.5: Pagination using our modified version of the bootpag plugin (example03.html)

As you paginate to our second page, you will spot one issue—the last page may, at times, contain only one event (as is the case with My Sample Event #3), and the event descriptions differ in their lengths. Hence, there will be a height difference, which becomes apparent as the user switches pages. As a consequence, the pagination control div will move up and down (refer to figure 5.6). Luckily, the fix for this is straightforward and involves assigning our event-services-content div a fixed height of 15em. Open myphoto.css and add the following:

    #services-events-content { 
        height: 15em; 
    }
Figure 5.6: Note the height difference between the two pages. Having a different number of events per page or listing events with differing descriptions will result in the container growing and shrinking (example03.html)

Now that our events container is of a fixed height, we can be certain that the container will not shrink based on its content. As a result, the pagination control will remain fixed in its position. However, this raises one final issue—long event descriptions. How can we deal with events that contain more text than is permissible by our events container? For example, consider the additional paragraph added to My Sample Event #2 in figure 5.7. As you can see, the pagination control is now rendered above the event description.

Any text exceeding our container’s height is simply cut:

Figure 5.7: Display bug: Long event descriptions result in the pagination control being rendered above the event description; any text exceeding our container’s height is cut

Once again, our fix is a simple one-liner and involves setting the container’s y-axis overflow so that any content within the container is scrollable. Open myphoto.css and update the styling for our services-events-content container so that its overflow-y property is set to scroll:

    #services-events-content { 
        height: 15em; 
        overflow-y: scroll; 
    } 

Save and refresh. Voila!

Comments are closed.

loading...