Skip to content

Instantly share code, notes, and snippets.

@groovenectar
Last active January 2, 2022 04:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save groovenectar/501d7e69304fa6d0ccce to your computer and use it in GitHub Desktop.
Save groovenectar/501d7e69304fa6d0ccce to your computer and use it in GitHub Desktop.
jquery.pagify.js
<!doctype html>
<html>
<title>jquery.pagify.js</title>
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="jquery.pagify.js"></script>
<style type="text/css">
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style>
<script type="text/javascript">
jQuery(function($) {
$('.example').pagify({pager_location: 'before'});
//$('.example').pagify('filter', '.test');
$('.example2').pagify({pager_location: 'both'});
});
</script>
</html>
<body>
<div class="nav"></div>
<ul class="example">
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
<li>List item 4</li>
<li>List item 5</li>
<li>List item 6</li>
<li>List item 7</li>
<li>List item 8</li>
<li>List item 9</li>
<li>List item 10</li>
<li class="test">List item 11</li>
<li>List item 12</li>
<li>List item 13</li>
<li class="test">List item 14</li>
<li>List item 15</li>
<li class="test">List item 16</li>
<li>List item 17</li>
<li>List item 18</li>
<li>List item 19</li>
<li>List item 20</li>
<li class="test">List item 21</li>
<li>List item 22</li>
<li>List item 23</li>
<li class="test">List item 24</li>
<li>List item 25</li>
<li>List item 26</li>
<li class="test">List item 27</li>
<li>List item 28</li>
<li>List item 29</li>
<li>List item 30</li>
<li>List item 31</li>
<li class="test">List item 32</li>
<li>List item 33</li>
<li>List item 34</li>
<li class="test">List item 35</li>
<li>List item 36</li>
<li>List item 37</li>
<li>List item 38</li>
<li class="test">List item 39</li>
<li>List item 40</li>
<li>List item 41</li>
<li>List item 42</li>
<li>List item 43</li>
<li>List item 44</li>
<li>List item 45</li>
<li class="test">List item 46</li>
<li>List item 47</li>
<li>List item 48</li>
<li>List item 49</li>
<li>List item 50</li>
<li>List item 51</li>
<li class="test">List item 52</li>
<li>List item 53</li>
<li>List item 54</li>
<li class="test">List item 55</li>
<li>List item 56</li>
<li>List item 57</li>
<li>List item 58</li>
<li>List item 59</li>
</ul>
<div class="nav"></div>
<ul class="example2">
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
<li>List item 4</li>
<li>List item 5</li>
<li>List item 6</li>
<li>List item 7</li>
<li>List item 8</li>
<li>List item 9</li>
<li>List item 10</li>
<li class="test">List item 11</li>
<li>List item 12</li>
<li>List item 13</li>
<li class="test">List item 14</li>
<li>List item 15</li>
<li class="test">List item 16</li>
<li>List item 17</li>
<li>List item 18</li>
<li>List item 19</li>
<li>List item 20</li>
<li class="test">List item 21</li>
<li>List item 22</li>
<li>List item 23</li>
<li class="test">List item 24</li>
<li>List item 25</li>
<li>List item 26</li>
<li class="test">List item 27</li>
<li>List item 28</li>
<li>List item 29</li>
<li>List item 30</li>
<li>List item 31</li>
<li class="test">List item 32</li>
<li>List item 33</li>
<li>List item 34</li>
<li class="test">List item 35</li>
<li>List item 36</li>
<li>List item 37</li>
<li>List item 38</li>
<li class="test">List item 39</li>
<li>List item 40</li>
<li>List item 41</li>
<li>List item 42</li>
<li>List item 43</li>
<li>List item 44</li>
<li>List item 45</li>
<li class="test">List item 46</li>
<li>List item 47</li>
<li>List item 48</li>
<li>List item 49</li>
<li>List item 50</li>
<li>List item 51</li>
<li class="test">List item 52</li>
<li>List item 53</li>
<li>List item 54</li>
<li class="test">List item 55</li>
<li>List item 56</li>
<li>List item 57</li>
<li>List item 58</li>
<li>List item 59</li>
</ul>
</body>
</html>
/*
Author: Daniel Upshaw
URL: http://danieluphaw.com/
Inspired by https://github.com/jlafitte/jquery-quick-pagination
*/
;(function ($, window, document, undefined) {
"use strict";
var truncation_pattern = {
prefer_current_side: function(page_count, current_page, nav_page_limit, min_start, min_end) {
var min_adjacent_before = 1;
var min_adjacent_after = 1;
var limit_includes_truncated = true;
// Can we calculate whether there will be one or two truncations beforehand?
// (total out values) + (minimum adjacent values) + (number of possible truncations) + (the current item)
var min_for_truncation = min_start + min_end + min_adjacent_before + min_adjacent_after + (limit_includes_truncated ? 2 : 0) + 1;
var truncate = nav_page_limit > min_for_truncation && page_count > nav_page_limit;
if (!truncate) {
return false;
}
var pages = [];
var half_of_limit = Math.floor(nav_page_limit / 2);
var page_num = 1;
var truncate_left = current_page - (half_of_limit - min_start) > min_start + 1;
var truncate_right = current_page + (half_of_limit - min_end) < page_count - min_end;
for (var nav_page = 0; nav_page < nav_page_limit; nav_page++) {
pages.push(page_num);
if (nav_page == min_start - 1 && truncate_left) {
pages.push(false);
nav_page++;
if (truncate_right) {
page_num = current_page - (min_start + 1);
} else {
page_num = page_count - (nav_page_limit - min_start - 1);
}
}
if (nav_page == nav_page_limit - min_end - 2 && truncate_right) {
pages.push(false);
nav_page++;
page_num = page_count - min_end;
}
page_num++;
}
return pages;
}
};
var defaults = {
per_page: 10, // How many items you want to show per page.
save_state: true, // Whether or not to store the current page in URL #hash
container_class: 'pagify', // The main/root class name, also prepended to other utility class names
current_page_class: 'pagify-page-current', // Class name of the active page items
nav_loop: true, // E.g., wrap back around when clicking Next, if on the last item
nav_location: 'before', // Possible values are "before,after,both,[jQuery Object]"
nav_next_html: '<span aria-label="Next">&raquo;</span>', // Set to false to disable
nav_prev_html: '<span aria-label="Previous">&laquo;</span>', // Set to false to disable
active_class: 'pagify-active', // Class name of the active page li in the navigation list
disabled_class: 'pagify-disabled', // Class name of the active page li in the navigation list
truncate_after: 20, // Set to false to always show all page numbers
truncate_html: '&hellip;', // Only applicable if max_nav_pages is set
truncate: function() { // Only applicable if max_nav_pages is set
var min_start = 2;
var min_end = 2;
return truncation_pattern.prefer_current_side(this.page_count, this.current_page, this.options.truncate_after, min_start, min_end);
},
start_page: 1, // Starting page... normally you would probably leave this alone.
item_filter: '*', // .filter() to run on items to determine what is included
goto: function() {
// aria-haspopup="true"
},
init: function() {
},
nav_rendered: function(nav) {
/*
if (!this.results_message) {
this.container.before($('<p>').addClass('pagify-results-message pull-left'));
this.results_message = this.container.prev('.pagify-results-message');
}
*/
},
page_changed: function(page, page_items) {
/*
var prev_page_count = (page - 1) * this.options.per_page;
var current_page_count = this.items.filter(':visible').length;
var lower_bound = prev_page_count + 1;
var upper_bound = prev_page_count + current_page_count;
this.results_message.html('Displaying results ' + lower_bound + ' - ' + upper_bound + ' of ' + this.items.length);
*/
},
removed: function() {
/*
this.results_message && this.results_message.remove();
*/
},
// You can also specify a unique ID string to set it manually
uid: function() {
return this.util.uid(this.element);
},
debug: true // Whether to show debug console messages
};
$('<style type="text/css">.' + defaults.container_class + '-page:not(.' + defaults.current_page_class + ') { display: none; }</style>').appendTo('head');
function Plugin(element, options) {
this.element = $(element);
this.options = $.extend({}, defaults, options);
this._defaults = defaults;
this.init();
}
$.extend(Plugin.prototype, {
init: function () {
this.options.start_page = this._page_from_hash() || this.options.start_page;
this._render(this.options.start_page);
this.options.init.call(this);
return this;
},
_render: function(page) {
this.container = $();
this._insert_containers();
return this.page(page);
},
_utility_class_name: function(class_name) {
return this.options.container_class + '-' + class_name;
},
filter: function(filter) {
this._remove_pages();
this._process_pages(filter);
var start_page = 1;
this.page(start_page);
return this;
},
_first_page_number: function() {
return this.items.length ? 1 : null;
},
first: function () {
this.page(this._first_page_number());
},
_last_page_number: function() {
return this.page_count ? this.page_count : null;
},
last: function () {
this.page(this._last_page_number());
},
_prev_page_number: function() {
if (this.current_page - 1) {
return this.current_page - 1;
} else {
return this.options.nav_loop ? this.page_count : null;
}
},
prev_page: function() {
if (!this._prev_page_number()) {
return false;
}
this.page(this._prev_page_number());
},
_next_page_number: function() {
if (this.current_page % this.page_count) {
return parseInt(this.current_page) + 1;
} else {
return this.options.nav_loop ? 1 : null;
}
},
next_page: function() {
if (!this._next_page_number()) {
return false;
}
this.page(this._next_page_number());
},
page: function(page) {
if (typeof page === 'undefined') {
return this.current_page;
} else {
this.current_page = page;
}
// If we haven't loaded the items yet
if (typeof this.items === 'undefined') {
this._process_pages();
}
if (page > this.page_count) {
this.options.debug && console.log('Could not get page ' + page + ' out of ' + this.page_count);
return;
}
this.items.filter('.' + this.options.current_page_class).removeClass(this.options.current_page_class);
var page_items = this.items.filter('.' + this._page_class_name(page));
page_items.addClass(this.options.current_page_class);
this._reload_nav();
if (this.options.save_state) {
this._page_to_hash(this.current_page);
}
this.options.page_changed.call(this, page, page_items);
return this;
},
_process_pages: function(filter) {
if (filter) {
this.options.item_filter = filter;
}
this.items = this.element.children().filter(this.options.item_filter);
var page_count = 1;
var $plugin = this;
$plugin.items.each(function (i) {
if (i >= (page_count * $plugin.options.per_page)) {
page_count++;
}
$(this).addClass($plugin._page_class_name() + ' ' + $plugin._page_class_name(page_count));
});
this.page_count = page_count;
return this;
},
_build_nav_prev_next: function(type) {
var $plugin = this;
var onclick;
var html;
var page_num;
switch (type) {
case 'next' :
html = this.options.nav_next_html;
page_num = this._next_page_number();
onclick = this.next_page;
break;
case 'prev' :
html = this.options.nav_prev_html;
page_num = this._prev_page_number();
onclick = this.prev_page;
break;
}
if (!html) {
return false;
}
if (html) {
var container = $('<li>');
var link = $('<a>')
.attr('rel', type)
.attr('href', this.nav_page_href(page_num))
.html(html);
link.on('click', function(e) {
e.preventDefault();
onclick.call($plugin);
});
container.append(link)
}
return container;
},
_build_nav_pages: function() {
var $plugin = this;
var nav_pages = $();
var truncated;
var index_min;
var index_max;
if (this.options.truncate_after) {
var pages = this.options.truncate.call(this);
}
if (pages) {
truncated = true;
index_min = 0;
index_max = pages.length - 1;
} else {
truncated = false;
index_min = 1;
index_max = this.page_count;
}
for (var i = index_min; i <= index_max; i++) {
var li = $('<li>');
// If not truncated, or truncated and page is not disabled (false)
if (!truncated || pages[i] !== false) {
var page_num = truncated ? pages[i] : i;
var nav_page = li.val(page_num);
// Just use the <li> for the click event. It contains the page number in its value.
nav_page.on('click', function (e) {
e.preventDefault();
var page = $(this).val();
$plugin.page(page);
});
// Add a anchor tag for good measure, and for things like middle click to new tab (loads the href).
var nav_page_a = $('<a>').attr('href', this.nav_page_href(page_num)).html(page_num);
nav_page.append(nav_page_a);
nav_pages = nav_pages.add(nav_page);
} else {
var nav_disabled = li.addClass(this.options.disabled_class).append(
$('<a>').html(this.options.truncate_html)
);
nav_pages = nav_pages.add(nav_disabled);
}
}
return nav_pages;
},
_insert_containers: function(nav_location) {
var container = $('<nav>')
.attr('role', 'navigation')
.attr('aria-label', 'Pagination')
.addClass(this.options.container_class);
nav_location = nav_location || this.options.nav_location;
if (nav_location instanceof $) {
container = nav_location.append(container).find('.' + this.options.container_class);
} else {
switch (nav_location) {
case 'both':
this._insert_containers('before');
this._insert_containers('after');
return this;
break;
case 'before':
this.element.before(
container.addClass(this._utility_class_name('before'))
);
break;
case 'after':
this.element.after(
container.addClass(this._utility_class_name('after'))
);
break;
}
}
this.container = this.container.add(container);
return this;
},
_build_nav: function(nav_pages, nav_prev, nav_next) {
var container = $('<ul>').attr('role', 'menubar');
if (nav_prev) {
container.append(nav_prev);
}
container.append(nav_pages);
if (nav_next) {
container.append(nav_next);
}
return container;
},
_render_nav: function() {
this.nav = $();
this.nav_next = $();
this.nav_prev = $();
this.nav_pages = $();
if (this.page_count <= 1) {
return;
}
// If there is one container, append() will reference the item
// If there is more than one container, append() will make clones of the item
// So we'll just always build new elements, and add each individually
var $plugin = this;
this.container.each(function() {
var nav_pages = $plugin._build_nav_pages();
var nav_prev = $plugin._build_nav_prev_next('prev');
var nav_next = $plugin._build_nav_prev_next('next');
var nav = $plugin._build_nav(nav_pages, nav_prev, nav_next);
$(this).append(nav);
nav.data('pages', nav_pages);
$plugin.nav = $plugin.nav.add(nav);
$plugin.nav_prev = $plugin.nav_prev.add(nav_prev);
$plugin.nav_next = $plugin.nav_next.add(nav_next);
$plugin.nav_pages = $plugin.nav_pages.add(nav_pages);
});
this.nav_pages
.attr('tabindex', -1)
.attr('aria-current', 'false')
.filter('[value="' + this.current_page + '"]')
.addClass(this.options.active_class)
.attr('tabindex', 0)
.attr('aria-current', 'true');
if (this.nav_prev) {
var prev_page = this._prev_page_number();
if (prev_page) {
this.nav_prev.removeClass(this.options.disabled_class);
} else {
this.nav_prev.addClass(this.options.disabled_class);
}
this.nav_prev.find('a').attr('href', this.nav_page_href(prev_page));
}
if (this.nav_next) {
var next_page = this._next_page_number();
if (next_page) {
this.nav_next.removeClass(this.options.disabled_class);
} else {
this.nav_next.addClass(this.options.disabled_class);
}
this.nav_next.find('a').attr('href', this.nav_page_href(next_page));
}
this.options.nav_rendered.call(this, this.nav);
return this;
},
_remove_nav: function() {
this.nav.remove();
return this;
},
_reload_nav: function() {
if (this.nav instanceof $) {
this._remove_nav();
}
this._render_nav();
return this;
},
reload: function(start_page) {
start_page = start_page ? start_page : this.current_page;
this.remove();
this._render(start_page);
return this;
},
_remove_pages: function() {
var regex = new RegExp(this._page_class_name() + '(-[0-9]+)?', 'ig');
this.items.removeClass(function (index, css) {
return (css.match(regex) || []).join(' ');
});
this.items.show();
this.page_count = 0;
return this;
},
remove: function() {
this._remove_pages();
this._remove_nav();
this.container.remove();
this.options.removed.call(this);
return this;
},
_page_class_name: function (page_num) {
var class_name = this._utility_class_name('page');
if (page_num) {
return class_name + '-' + page_num;
} else {
return class_name;
}
},
uid: function(uid) {
if (typeof uid === 'undefined') {
// If we haven't initialized the uid yet at all
if (typeof this._uid === 'undefined') {
this._uid = this.options.uid.call(this);
}
return this._uid;
} else {
this._uid = uid;
//return this.reload();
}
},
nav_page_href: function(page) {
return page ? this.util.build_uri_hash(this.uid(), 'page', page) : null;
},
_page_to_hash: function(page) {
return this.util.set_uri_hash_param(this.uid(), 'page', page);
},
_page_from_hash: function() {
var page_from_hash = this.util.get_uri_hash_param(this.uid(), 'page');
return page_from_hash ? parseInt(page_from_hash) : null;
},
nav_container: function() {
return this.container;
},
// Also removes plugin reference from this.element
// Additional functionality below
destroy: function() {
this.remove();
},
util: {
// Generates a reasonably unique ID for the element, used for URL hash
// You can also specify a unique ID string manually into utility._uid
// If available, uses the plugin element's ID attribute
uid: function($elm, update_uid) {
if (typeof update_uid !== 'undefined') {
$elm.data('_uid', update_uid);
return update_uid;
}
if ($elm.data('_uid')) {
return $elm.data('_uid');
}
if ($elm.attr('id')) {
return this.uid($elm, $elm.attr('id'));
}
var elmnode = $elm.context.nodeName;
var elmindex = $elm.index();
var parentnode = $elm.context.parentElement.nodeName;
var parentindex = $($elm.context.parentElement).index();
var uid = elmnode + elmindex + parentnode + parentindex;
// Base64 encode if possible
if (window.btoa) {
uid = btoa(uid);
}
$elm.data('_uid', uid);
return uid;
},
update_uri_hash: function(uid, hash_string) {
hash_string = '#' + hash_string.replace(new RegExp('^#'), '');
window.location.hash = hash_string;
return true;
},
set_uri_hash_param: function(uid, key, value) {
var hash_params = this.build_uri_hash(uid, key, value);
return this.update_uri_hash(uid, hash_params);
},
get_uri_hash_param: function(uid, key) {
// Extract the plugin hash parameters
var hash_query = location.hash.match(new RegExp('&?{' + uid + '[|]([^}]+)}', 'i'));
var params = [];
if (!hash_query || !hash_query[1]) {
return null;
}
hash_query = hash_query[1];
var split = hash_query.split('|');
// Make sure there's at least one other parameter
if (!split.length) {
return null;
}
var matches;
$.each(split, function(i, val) {
if (matches = val.match(/^([\w_-]+)+=?(.*)/i)) {
// matches[1] is the param name, and matches[2] is the value, if present
params[matches[1]] = matches[2] ? matches[2] : true;
}
});
if (key) {
return params[key] ? params[key] : null;
} else {
return params;
}
},
build_uri_hash: function(uid, key, value) {
var hash_params = this.get_uri_hash_param(uid) || [];
if (typeof value === 'undefined' || value === null) {
delete hash_params[key]
}
var hash_query_params = [];
$.each(hash_params, function(key, val) {
var item_query = key;
// If it's a boolean true we don't need to specify a value
if (val !== true) {
item_query += '=' + val
}
return hash_query_params.push(item_query);
});
hash_query_params.push(key + '=' + value);
if (!hash_query_params.length) {
return false;
}
var hash_query = '{' + uid + '|' + hash_query_params.join('|') + '}';
var current_hash = location.hash.replace(new RegExp('^#'), '');
current_hash = current_hash.replace(new RegExp('&?{' + uid + '([^}]+)}', 'i'), '');
if (current_hash) {
hash_query = (current_hash + '&' + hash_query).replace(new RegExp('^&'), '');
}
return '#' + hash_query;
}
}
});
var plugin_name = 'pagify';
$.fn[plugin_name] = function(options) {
var args = arguments;
if (!(this instanceof $)) {
return $.extend(defaults, options);
}
if (options === undefined || typeof options === 'object') {
return this.each(function () {
if (!$.data(this, 'plugin_' + plugin_name)) {
$.data(this, 'plugin_' + plugin_name, new Plugin(this, options));
}
});
} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
var returns;
this.each(function() {
var instance = $.data(this, 'plugin_' + plugin_name);
if (instance instanceof Plugin && typeof instance[options] === 'function') {
returns = instance[options].apply( instance, Array.prototype.slice.call(args, 1));
}
if (options === 'destroy') {
$.data(this, 'plugin_' + plugin_name, null);
}
});
return returns !== undefined ? returns : this;
}
};
}(jQuery, window, document));
.pager-nav {
padding: 0;
}
.pager-nav li {
display: inline-block;
background: #f7f7f7;
}
.pager-nav li a,
.pager-nav::before {
display: inline-block;
color: #333;
text-decoration: none;
padding: 5px 10px;
}
.pager-nav::before {
content: "Page:";
}
.pager-nav li.pager-nav-active a {
color: #fff;
background: #777;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment