“Add to Shortlist” feature for WordPress

A tutorial about how to build an 'Add to Shortlist' feature in WordPress using Ajax

View demo    Clone from Github

There are endless options for adding a shopping cart to a website, but sometimes we're not dealing with products for sale and we need something more simplified. This article will take you step-by-step through building a simple shortlist feature in WordPress.

Step One: WordPress Template Files

We'll assume that we are using the default posts in WordPress to represent the items which can be selected. We will also use Bootstrap 3 for styling the appearance of this demo.

Create two pages in the WordPress admin: 'All Items' and 'Shortlist'. Create two WordPress template files called items.php and shortlist.php and then assign each page to their respective template files.

items.php

Add a basic loop to the items.php template file to output all the posts as an unordered list, with each item including three buttons: add, remove and selected. Assuming we're familiar with the WordPress loop, the code inside the while() statement is:

<li id="<?php the_ID(); ?>" class="item">
	<img src="http://placehold.it/180x120/f0f7fd/428bca&text=<?php the_title(); ?>" alt="">						
	<a href="#" class="btn action add" data-action="add"><span class="glyphicon glyphicon-plus-sign"></span> Add</a>
	<a href="#" class="btn action remove" data-action="remove"><span class="glyphicon glyphicon-minus-sign"></span> Remove</a>
	<a href="#" class="btn selected"><span class="glyphicon glyphicon-ok-sign"></span> </a>
</li>

Note the data-action attribute on the add and remove buttons. This will be utilised in another step below.

shortlist.php

The loop in shortlist.php is similar, however we can just include one button: remove. We will add to this template later, once the shortlist functions have been created.

<li id="<?php the_ID(); ?>" class="item">
	<img src="http://placehold.it/180x120/f0f7fd/428bca&text=<?php the_title(); ?>" alt="">						
	<a href="#" class="btn action remove" data-action="remove"><span class="glyphicon glyphicon-minus-sign"></span> Remove</a>
</li>

Step Two: Shortlist Functions

Create an /includes/ directory and the file shortlist-functions.php within. This file will contain our custom functions.

Start the session, if not already started:

function shortlist_start_session() {
    if(!session_id()) {
        session_start();
    }
}
add_action('init', 'shortlist_start_session', 1);

Count the number of items in the session:

function list_count() {
	if(isset($_SESSION['shortlist'])) {
		echo count($_SESSION['shortlist']);
	} else {
		echo '0';
	}	
}

Create the file shortlist.js within a /js/ directory and enqueue it in the theme:

function custom_enqueue_scripts() {
	if (!is_admin()) {
		wp_enqueue_script(
			'shortlist', // handle	
			get_bloginfo('template_directory') . '/js/shortlist.js', // path
			array('jquery'), // dependency
			'1', 	// version
			true // load via wp_footer
		);
	}
}
add_action('wp_enqueue_scripts', 'custom_enqueue_scripts');

Plus, we'll create a debug function which we can use to view the contents of the session array if necessary for debugging.

function debug_shortlist() {
	echo '<br><pre class="entry-content">';
	if(!empty($_SESSION['shortlist'])) {
		print_r($_SESSION['shortlist']);
	} else {
		echo 'Session is empty.';
	}	
	echo '</pre>';
}

Lastly, we need to include the custom functions file in the theme's main functions.php file with the following hook:

if ( ! function_exists('custom_functions') ) {
    function custom_functions() {
	include(get_template_directory() . '/includes/shortlist-functions.php');
    }
}
// runs before 'init' hook
add_action( 'after_setup_theme', 'custom_functions' );

Step Two: Shortlist Actions & Shortlist Total

Inside the /includes/ directory, we create the shortlist-actions.php file, for updating the contents of the session_array. This file will be called via AJAX from a JavaScript file.

//start the session, if not already running
if(!session_id()) {
    session_start();
}
	    
// define a fallback value for an shortlist session
if(!isset($_SESSION['shortlist'])) {
	$_SESSION['shortlist'] = array();
}

Since we're calling this file via AJAX, we need to extract the action and id values from the query string, as follows:

// define variable defaults
$action = null;
$id = 0;

// assign action and id parameters if set
if ( isset( $_GET['action'] ) && !empty( $_GET['action'] ) ) {
	//the action from the URL 
	$action = $_GET['action']; 
} 
if ( isset( $_GET['id'] ) && !empty( $_GET['id'] ) ) {
	//the item id from the URL 
	$id = $_GET['id']; 
} 

According to the $action value; add to, or remove from, the $id from the session using the follwing switch statement:

switch($action) {	

	case "add":
		// check if item is already in array, if not, add
		if(($key = array_search($id, $_SESSION['shortlist'])) === false) {
			array_push( $_SESSION['shortlist'], $id );
		}
	break;
	
	case "remove":
		// search for item by value and remove if found
		if(($key = array_search($id, $_SESSION['shortlist'])) !== false) {
		    unset($_SESSION['shortlist'][$key]);
		}
	break;
	
	case "empty":
		//remove all
		unset($_SESSION['shortlist']); 
	break;

}

Create a third file within the /includes/ directory called shortlist-total.php This file will be only used to update the list counter, and it contains the following function:

//start the session
if(!session_id()) {
    session_start();
}

if(isset($_SESSION['shortlist'])) {
	echo count($_SESSION['shortlist']);
} else {
	echo '0';
}	

Step Three: the JavaScript

The functions below include console.log() messages to help with debugging. This breaks Ajax in older IE, so you can either remove the console.log code or use the log() function by Paul Irish. More details here

Define the path to the /includes/ directory, relative to the WordPress theme. Note the snippet below is for a theme within a directory /shortlist-wordpress/

// theme directory name
var themeDirName = 'shortlist-wordpress';

// path to ajax file
var homeURL = window.location.protocol + "//" + window.location.host + "/",
    filePath = homeURL + 'wp-content/themes/' + themeDirName + '/includes/';

Append a counter span to the navigation item ending in /shortlist/, and set the value of the counter to that of the data-count attribute on the body element. Note: this value is set in PHP on page load.

var shortlistNavItem = $('.navbar li a[href$="/shortlist/"]'),
    listCount = $('body').data('count');

shortlistNavItem.append('&nbsp;(<span class="shortlist-count">0</span>)');

$('.shortlist-count').html(listCount);

Now we create the getItemTotal function which updates counter dynamically.

function getItemTotal() {
	var	counter = $('.shortlist-count'),
		clearAll = $('.shortlist-clear a');

	$.ajax({
		type: 'GET',
		url: filePath + 'shortlist-total.php',
		success: function(data) {
			counter.text(data);
		},
		error: function() {
			console.log('error with getItemTotal function');
		}			
	});
};

Next we create the shortlistActions function which runs the shortlist-actions.php when an add or remove button is clicked.

function shortlistActions(button) {

	$(button).on('click', function(e) {

		var target 		= $(this),
			item 		= target.closest('.item'),
			itemID 		= item.attr('id'),
			itemAction 	= target.data('action');

		$.ajax({
			type: 'GET',
			url: filePath + 'shortlist-actions.php',
			data: 'action=' + itemAction + '&id=' + itemID,
			success: function() {
				getItemTotal();
				console.log(itemAction + ' item ' + itemID);
			},
			error: function() {
				console.log('error with shortlistActions function');
			}
		});

		if (itemAction === 'remove') {
			item.removeClass('selected');
		} else {
			item.addClass('selected');
		}

		e.preventDefault();
	});

};

Call immediately on any item, except those on the Shortlist page.

shortlistActions( $('.item .action:not(.page-template-shortlist-php .item .action)') );

Next we create a remove only function and call it immediately.

function shortlistPageActions() {

	var shortlistPage 		= $('.page-template-shortlist-php'),
		shortlistPageItem 	= shortlistPage.find('.item'),
		removeItem 			= shortlistPageItem.find('.action');

	removeItem.on('click', function(e) {

		var target = $(this),
			itemID = target.closest('.item').attr('id');

		$.ajax({
			type: 'GET',
			url: filePath + 'shortlist-actions.php',
			data: 'action=remove&id=' + itemID,
			success: function() {
				getItemTotal();
				console.log('removed item ' + itemID);
			},
			error: function() {
				console.log('error with removeItem action');
			}
		});

		target.closest('.item').remove();

		e.preventDefault();
	});

};

// call immediately
shortlistPageActions();

Create a function for clearing all the items from the shortlist session.

function clearAll() {
	
	$('.shortlist-clear a').on('click', function(e) {

		$.ajax({
			type: 'GET',
			url: filePath + 'shortlist-actions.php',
			data: 'action=empty',
			success: function() {
				getItemTotal();
			},
			error: function() {
				console.log('error with clearAll action');
			}
		});

		e.preventDefault();		
	});
} // end 

clearAll();

11 Responses to ““Add to Shortlist” feature for WordPress”

  1. Ian says:

    Hi,

    This is a fantastic tutorial but I notice it doesn’t work in IE8. Whilst I personally think we should be using other or more modern browsers, it still seems to be used and quite popular too.

    Is there any reason why this won’t work in IE8?

    Thanks

  2. Ian says:

    Thanks ever so much for taking the time to reply. I’ve tried adding the shim but sadly it doesn’t work 🙁

    I wonder if the Browser Stack emulator isn’t a true representation of the pants that is IE8!

    I wonder if there is anything else that could solve it?

    Thanks again, your fast response was very much appreciated!

    • Tim says:

      Hi Ian,
      OK, I believe I’ve really fixed the IE8 issue this time. I have now commented out the console.log functions in shortlist.js and tested it again in IE8 and it’s working OK for me.
      At first I thought the absence of the shim was to blame, because the markup makes use of the data- attribute. When I tested this in IE8, it worked OK, but this was misleading because I had already opened the F12 developer console to look for errors. When I tested it again in IE8 following your reply, I noticed the AJAX wasn’t working. So I opened the F12 console and tried again and it worked. WTF? Thanks to Stack Overflow once again for saving the day, I have now learned that console.log breaks AJAX in old versions of IE.
      Thanks for your feedback to help me figure this out.
      Tim.

  3. Ian says:

    Thanks again Tim

    Any chance you can update the GitHub version so I can ensure I’ve commented out the right parts

    Cheers!

  4. Mike says:

    This looks great, is there a way to save the shortlist to a cookie?

  5. Paul says:

    This looks great, is their a way to add the shortlist to a contact form that can be emailed?

    • Tim says:

      Hi Paul,
      Yes; this is exactly what I did on the viviensmodels.com.au/sydney website. Once items are added to the shortlist, the “Shortlist” page has an option for “Email”. I used Gravity Forms to handle the email side of things, and the gform_replace_merge_tags filter to create a custom merge tag for outputting the shortlist selections into the confirmation email. This involved creating a function that ran a post query for the item IDs and then generating the relevant HTML to pass into the Gravity Forms filter and thus the email.
      Tim.

  6. Robert says:

    Is it possible to do shortlist and show author? Currently I am showing all authors and single author page. I want to build Shortlist feature for each author.

  7. darer says:

    I would like display the “shortlisted items on the front page itself .Like a fixed division or something.can you please help me out

Leave a Comment