Product Comparison Tool
Client: “Now that we have all of our product data loaded into the new site, I want a tool where users can select multiple products and compare them side by side.”
Gulp….“Let me think about that one for a bit…”
Not an unreasonable request, right? As a consumer I’ve often used such tools to quickly compare multiple products I was interested in purchasing. Comparison tables can quickly help you find differences and similarities that would be tough to see bouncing around different product-specific pages.
But how to pull this off on an ExpressionEngine based site?
As is so often the case with EE, what looks difficult at first blush turns out to not be too difficult. Let me show you the front end of what I came up with so you can better tell if it’s close to what you need:
Overview
- The drop-downs are dynamically populated from channel:entries tags so if our client adds or deletes products he won’t have to worry about replicating those edits for the drop-downs.
- While the drop-downs display entry titles, you are really choosing entry_ids.
- When you click “Compare” a bit of javascript grabs the entry_id’s from the 3 drop-downs, appends them onto the URL and reloads the template.
- Additional channel:entries loops then see those entry_id’s and use them to retrieve the content for the chosen entry.
- Each column is actually its own HTML table, floated using CSS to create the side-by-side view.
Main releases/compare Template
Let’s work through the main template (available as a download):
<!doctype html>
<html>
<head>
<title>Compare Releases</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script type="text/javascript">
<!-- Begin
site = "http://localhost:8888/index.php/releases/compare/";
function combineMenus(frm, product_1, product_2, product_3) {
with (frm) {
str = product_1.options[product_1.selectedIndex].value;
str += product_2.options[product_2.selectedIndex].value;
str += product_3.options[product_3.selectedIndex].value;
url = site + str;
window.location.href = url;
}
}
// End -->
</script>
This is the javascript that assembles the expected page URL including the 3 entry_id’s from the selected products. If you use this code make sure to change the site= parameter to the URL to your comparison template.
<style type="text/css">
table.compare {width:170px; float: left;}
td {text-align: left; padding: 2px; height: 28px;}
td.label {text-align:right; padding: 5px 4px 0px 0px; }
th {height: 80px;}
form#compare_form {padding: 0px 0px 0px 100px;}
select {width: 167px;}
</style>
</head>
Just some basic CSS mainly to get the result tables to line up by floating. I went this route because it made exception-handling much easier (what if user selects an option only from the first and third boxes, etc).
<body>
<h1>Compare Record Releases</h1>
<form name="compare_releases" id="compare_form" method="post" action="http://localhost:8888/index.php/releases/compare/">
<input type="button" class="compare_form" value="Compare" onclick="combineMenus(this.form, this.form.product_1, this.form.product_2, this.form.product_3)" />
<select name="product_1">
<option value="">Choose Release</option>
{exp:channel:entries channel="releases"" dynamic="no" disable="trackbacks|categories|member_data|pagination|custom_fields"}
<option value ="{entry_id}/" {if segment_3==entry_id}selected="selected"{/if} >{title}</option>
{/exp:channel:entries}
</select>
<select name="product_2">
<option value="">Choose Release</option>
{exp:channel:entries channel="releases"" dynamic="no" disable="trackbacks|categories|member_data|pagination|custom_fields"}
<option value ="{entry_id}/" {if segment_4==entry_id}selected="selected"{/if} >{title}</option>
{/exp:channel:entries}
</select>
<select name="product_3">
<option value="">Choose Release</option>
{exp:channel:entries channel="releases"" dynamic="no" disable="trackbacks|categories|member_data|pagination|custom_fields"}
<option value ="{entry_id}/" {if segment_5==entry_id}selected="selected"{/if} >{title}</option>
{/exp:channel:entries}
</select>
</form>
These are the dynamically-generated drop-downs. It may or may not make sense to chunk these out into a reusable embedded template or snippet, but since they are pretty simple I just left them this way. All we need are the default fields from the channel so our disable parameter can disable everything else for best performance.
{if segment_3 !="" OR segment_4 !="" OR segment_5 !=""}
<table class="compare">
<thead><tr><th> </th></tr></thead>
<tbody>
<tr><td class="label">Title</td></tr>
<tr><td class="label"># of Tracks</td></tr>
<tr><td class="label">Formats</td></tr>
</tbody>
</table>
{/if}
This conditional displays the comparison table labels if the template has been loaded with entry_id’s showing in the URL.
{if segment_3 !=""}
{embed="embeds/comparison_column" the_segment="{segment_3}"}
{/if}
{if segment_4 !=""}
{embed="embeds/comparison_column" the_segment="{segment_4}"}
{/if}
{if segment_5 !=""}
{embed="embeds/comparison_column" the_segment="{segment_5}"}
{/if}
</body>
</html>
These conditionals pull in the embedded template that displays a column. Since the columns need to be identical and only need to look at a different segment for the right entry_id to pull content for, I created an embedded template and just pass the segment variable as an embed variable. While the comparison column in this tutorial is simple, the odds are that on a production site it would be much more complex and you might as well only create that table once.
The embeds/comparsion_column Template
{exp:channel:entries weblog="releases" limit="1" entry_id="{embed:the_segment}" disable="trackbacks|categories|member_data|pagination" dynamic="no"}
<table class="compare" border="1">
<thead>
<tr><th><img src="{releases_album_thumb}" title="{title}" alt="{title}" /></th></tr>
</thead>
<tbody>
<tr><td><a href="{url_title_path='releases/detail'}">{title}</a></td></tr>
<tr><td>{releases_number_tracks}</td></tr>
<tr><td>
{releases_formats}
{if item=="MP3"}<img src="{site_url}images/interface/release_itunes.png" alt="MP3" />{/if}
{if item=="CD"}<img src="{site_url}images/interface/release_cd.png" alt="CD" />{/if}
{if item=="LP"}<img src="{site_url}images/interface/release_lp.png" alt="LP" />{/if}
{/releases_formats}
</td></tr>
</tbody>
</table>
{/exp:channel:entries}
Pretty straightforward here, we just use the passed-in variable to determine what URL segment to grab the entry_id from.
Wrapping Up
So there you have it - with a small bit of javascript together with native ExpressionEngine tools like segment variables, conditionals, embedded templates, and embed variables, you can pretty quickly pull entries from a channel and display them in a side-by-side comparison table for happy users.
Category Navigation






by Shane
Date: Saturday, February 4th, 2012
Comment: #1
This is awesome Mike. Thank you for sharing.