Skip to contentSkip to author details

casper

A 2-post collection

Integrating Bootstrap Pagination with Ghost

Written by Michael Earls
 blog  bootstrap  programming  ghost  development  casper  pagination

In my previous post, I discussed how to integrate Bootstrap into the default Ghost theme. This post will cover the way that I modified pagination to use Bootstrap's pagination control.

To start, you will need to create a new pagination.hbs file in the partials folder in the root of your theme.

Because of the way that Bootstrap pagination works, I had to implement pagination using client-side JavaScript. The implementation uses a combination of server-side handlebars markup and client-side script.

The variables that we'll use in the pagination are defined in the site-init.js file in /assets/js.

var prev;  
var pages;  
var page;  
var next;  
var pageUrl;  
var pageUrlPrev;  
var pageUrlNext;  
var numbersSurroundingEllipses = 3;  
var useSimplePagination = false;

Here is the pagination.hbs for the customized pagination that I am using on my site:

<script type="text/javascript">  
    // set the values that we'll use in the bootstrap-pagination.js file
    {{!if there is no value for the variable, display a 0}}
    prev = {{#if prev}}{{prev}}{{else}}0{{/if}};
    pages = {{#if pages}}{{pages}}{{else}}0{{/if}};
    page = {{#if page}}{{page}}{{else}}0{{/if}};
    next = {{#if next}}{{next}}{{else}}0{{/if}};
    pageUrl = '{{page_url}}';
    pageUrlPrev = '{{pageUrl prev}}';
    pageUrlNext = '{{pageUrl next}}';
    pageUrlFirst = '{{pageUrl 1}}';
    pageUrlLast = '{{pageUrl pages}}';
</script>
<nav>
  <ul class="pagination bootstrap-pagination">
  </ul>
</nav>

This particular bit of code is a little confusing at first glance because it combines client- and server-side script. Let's break it down.

Since this code will appear each time the pagination control is displayed, we don't want to define the variables here. That's why they were defined in the init script file. So, each time the code runs on the page, it will set the value of the variables needed to create the pagination control.

Notice the if statements. Since these values may be empty, we can't just use them "raw" to define the values of our JavaScript variables. This will lead to a JavaScript error because essentially, an empty value would render the following code:

prev = ;

This is obviously incorrect. The {{#if prev}}{{prev}}{{else}}0{{/if}}; code will check to see if we have the prev variable defined. If so, we'll simply print it out. If not, we'll provide the number 0. It then repeats this process for the remaining initialization variables.

After the initialization code, we just create a nav control with the ul defined. It is empty when it is defined. In order to fill it with page navigation controls, we will use jQuery code when the document is ready.

The main default.hbs file includes a link to the pagination script. Here is the code that runs when the page starts up:

$(document).ready(function() {  
    // see if there are any previous pages
    // if so, append them to the pagination ul
    if (prev > 0) {
        $('ul.bootstrap-pagination').append('<li class="prev"><a href="' + pageUrlFirst + '" title="Go to first page" aria-label="First"><span aria-hidden="true"><i class="fa fa-angle-double-left"></i></i></span></a></li>');
        $('ul.bootstrap-pagination').append('<li class="prev"><a href="' + pageUrlPrev + '" title="Go to previous page" aria-label="Previous"><span aria-hidden="true"><i class="fa fa-angle-left"></i></span></a></li>');
    }

    if (useSimplePagination) {
        doSimplePagination();
    }
    else {
        doComplexPagination();
    }

    // if we have pages after this one, display the 'next' buttons
    if (next > 0) {
        $('.bootstrap-pagination').append('<li class="nxt"><a href="' + pageUrlNext + '" title="Go to next page" aria-label="Next"><span aria-hidden="true"><i class="fa fa-angle-right"></i></span></a></li>');
        $('ul.bootstrap-pagination').append('<li class="nxt"><a href="' + pageUrlLast + '" title="Go to last page" aria-label="Last"><span aria-hidden="true"><i class="fa fa-angle-double-right"></i></i></span></a></li>');
    }
});

As you can see, this code will add the navigate to first and navigate to previous buttons if we are not on the first page. It does this by using jQuery selectors to find any ul's on the page that use the .bootstrap-pagination class and appends the li controls that have the proper links.

Then, it checks to see if it should perform simple or complex pagination. Simple pagination merely shows "Page X of Y". Complex pagination shows the page numbers with an ellipses in the middle.

Simple pagination is performed by the following function:

function doSimplePagination() {  
    $('.bootstrap-pagination').append('<li><a href="' + pageUrl + 'page/"' + page + '" aria-label="Page ' + page + ' of ' + pages + '">Page ' + page + ' of ' + pages + '</a></li>');
}

This shows the user what page they're on.

Complex pagination shows a set number of page numbers on either side of an ellipses. The number of page numbers to show is determined by the variable numbersSurroundingEllipses. This value is set in the init script to a default value of 3.

It is possible to override this number on the Settings->Code Injection screen on your site's dashboard. If the value is set to -1, then the pagination control will display all page numbers.

The code to determine which pages to display on the control is a bit complicated. In short, it will show the number of pages as defined by the numbersSurroundingEllipses variables and the page directly before and after the current page so that you can navigate between pages. When you get within a certain number of pages from the end (calculated based on the pages - (numbersSurroundingEllipses * 2) calculation), the ellipses is not shown and a link to the page in the "middle" is displayed.

Refer to the doComplexPagination() function in the bootstrap-pagination.js file.

The very last thing we do when creating the pagination control is set the current page as active:

$('li.page' + page).addClass('active');

This provides you with Bootstrap pagination on your Ghost blog.

Here is a screenshot of the pagination control:

Simple Pagination:
Simple Pagination

Complex Pagination:
Complex Pagination

Note: This implementation requires a link to FontAwesome fonts (used in the left and right buttons).

To make this work, make sure you remove (or alter) any CSS in your theme's screen.css file that might define properties on the nav element or the pagination class. I had a terrible time getting this working at first because the default theme heavily customizes these elements and has class names that clash with Bootstrap.

Integrating Bootstrap with the Casper Ghost Theme

Written by Michael Earls
 blog  bootstrap  css  programming  ghost  theme  casper

I have been working on my blog lately to customize the theme and provide some features that I wanted. I started with the default Ghost theme (Casper) and modified it to suit my needs.

I have shared my theme (I named it "Ghost Cerkit Theme") on GitHub in a new repository:

Ghost Cerkit Theme

I added a few features, like Google custom Search and Disqus comments. I also added a sidebar that shows Author information for the author of the first post on the site. This is for blogs that have a single author that want to show their information on all pages (except the Author page, where it is suppressed).

To use the theme, there are a few customization options that you'll have to change. The hardest one is probably the Google custom search. Instructions for how to create a custom search engine can be found on the Ghost website. In order for your custom search to work, you will need to put your search engine id in the hidden cx form field on the search form.

I had to make significant changes to the screen.css file in order for Bootstrap to work properly. There are a lot of settings in the default theme that interfere with Bootstrap. I believe the best way to see what changed would be to view the diffs for the css file (and some of the core .hbs templates).

In addition to the custom search form, I also added the ability for the user to change the current site theme. Since the custom theme utilizes Bootswatch themes, I was able to add a selection box containing all of the Bootswatch themes. When the user picks a new theme, the site changes to that theme and stores its name in a cookie on the user's browser. Whenever the user visits a page on the site in the future, the chosen theme is used instead of the default theme. There is also a reset button to return the site to the original theme as defined by the site owner. It is possible to hide the theme selector. Just add the following code to the header:

showThemeSelector = false;

I also replaced the Casper pagination control with a Bootstrap pagination control that shows more detail. However, in order for this to work, I had to utilize JavaScript. This might affect SEO a bit, but with the theme retaining the meta tags for navigation, I don't think there will be a major impact.

I will be writing up additional blog entries about how I implemented the Bootstrap pagination for Ghost as well as an entry about the theme selector.

The theme still needs some polish to remove all of the CSS that interferes with Bootstrap. I worked as hard as I could to remove any customizations that are specific to my personal site, but there are still some things that need to be done in order for it to work completely.

I retained the MIT license on the original Casper theme so that there are no constraints on usage. I thought about licensing it under GPL 2, but adding a more restrictive sublicense seemed inappropriate in this case. If you use the code and feel inclined to give me attribution, that would be great. Just link to my website (https://cerkit.com).