Creating your Own Swatch Selector

Having worked retail before and speaking from experience, the word blue can mean a whole assortment of anything remotely blue. Turquoise, Cyan, Light Blue, Dark Blue, even Purple could be considered “blue” in a buyer’s vocabulary. On an online shop, these little ambiguities should be avoided in order to prevent client dissatisfaction (because a slight mismatch in colour assumption could mean the difference between a sale, and extra expenses processing that return!).

A fairly common way for retailers to show their product colours is through the usage of colour or image swatches. Many e-commerce systems output their colour selections (or attributes as we call it) as a drop down list by default.
Inspecting the element, we’ll usually see something like this:

<select name="colour" class="attribute-colour" id="colour-selector">
    <option value="red">Red</option>
    <option value="blue">Blue</option>
    <option value="yellow">Yellow</option>
</select>

With this in mind, there are two main routes we can use to replace this with a swatch selector:

  1. We can either alter the output of the <select> by trying to pick apart of the code responsible for outputting the <select>
  2. or

  3. We can build a dummy swatch selector using jQuery that lies on top of the <select> and mimics its selections.

I prefer to use the latter approach. My reasoning? The values and the way the system accepts values from our elements are already set in place, and all we’re doing is applying an additional layer on top of it. We avoid altering the way the attribute works, and also avoid accidentally removing functionality that depends on our <select> selector.

Related: Editing Frontend Product Configurable Attributes in Magento

What do we need to do?

Before writing any code, I like mapping out and writing out the requirements for what I’m about to create. Additionally, if you can explain this code to someone in plain English (We call it the rubber ducky approach where you explain your logic to a rubber ducky in order to better understand it yourself) then extra brownie points for you!
Let’s map out the steps. (This will all be done in jQuery)

Steps:

  1. Hide our <select> tag
  2. Parse <select> for <option>s
  3. Create swatches from the <select>
  4. Wire up events to ensure our new selector synchronizes with the <select>
  5. Skin the selector

Hide our <select> tag

For all intents and purposes, we can simply use:

$('select').hide();

Note: For Magento, validation.js tends to ignore validating elements with display set to none. There are many ways to hide an element without setting its display to none. I choose to give an element either a fixed or absolute position, far off to the left of the window e.g. position: absolute; left: -99999px;

Parse <select> for <option>s

This is an optional step. I like having a list object that I can use in case I want to output an entirely different set of html for the selector. What this step involves is creating a key/value array for the <select> element. If you don’t want to make an extra function, you can take some of this logic and directly apply it to the next step below.

//Takes a select object and parses it for its values and options
function parseSelect(selectObj) {
    var select = $(selectObj);
    
    var listObj = new Object;
    listObj.classNames = 'swatch-list ' + select.attr('class');
    listObj.originalSelect = select; //Store our select object reference here
    
    listObj.items = new Array();
    select.children('option').each(function () {
        var _element = $(this);
        //Discard any empty values
        if (_element.val() != "") {
            var item = {
                'label': _element.text(),
                'id': _element.val(),
            }
            listObj.items.push(item);
        }
    });
    return listObj;
}

The function parseSelect returns an object with the following properties:

classNames: A string of class names including “swatch-list” derived from the select object
originalSelect: A reference to our original select element. We will uses this later to synchronize our swatches with this reference.
items: an array of options where “label” is the option’s text and the “id” is the element’s value.

Let’s run parseSelect() on our example:

<select name="colour" class="attribute-colour" id="colour-selector">
    <option value="red">Red</option>
    <option value="blue">Blue</option>
    <option value="yellow">Yellow</option>
</select>

We should now have following list object:

listObj.classNames = “swatch-list attribute-color”
listObj.originalSelect = reference to <select name=”colour”>
listObj.items =
1) label: “Red”, id:red
2) label: “Blue”, id:blue
3) label: “Yellow”, id:yellow

Theoretically, we should be able to use this list to create any kind of list (including custom drop downs, or custom swatches for our example)
Let’s call this variable “list”.

Create swatches from the <select>

How you choose to create the list is entirely up to you.
What I did was create a very simple function that creates the html for the swatches.

//Creates all the html needed and returns an element
function createSwatches(list) {
    var html;
    var div = $(document.createElement('div')).addClass('swatch-wrapper');
    var element = $(document.createElement('ul'));
    element.addClass(list.classNames);
    element.data('original-select', list.originalSelect);

    for (var i = 0; i < list.items.length; i++) {
        var list_item = document.createElement('li');
        $(list_item).append(document.createElement('a'));
        $(list_item).data('id', list.items[i]['id']);
        $(list_item).attr('title', list.items[i]['label']);
        $(list_item).children('a').text(list.items[i]['label']);
        element.append(list_item);

        div.append(element);
        html = div;
    }
    return html;
}

Very simple function that takes the list, creates the following html:

<div class="swatch-wrapper">
    <ul class="swatch-list attribute-color">
        <li title="Red"><a>Red</a></li>
        <li title="Blue"><a>Blue</a></li>
        <li title="Yellow"><a>Yellow</a></li>
    </ul>
</div>

But we’re not done yet! Our new unordered list might have the same attributes and labels as our select, but no matter what you click, nothing in the original <select> is going to change. We need to hook these two elements together. Let’s create a function that deals with passing our information from the swatch unordered list back to its original select.

function swatchSelected(list_item) {
    if ($(list_item).data('id') !== undefined) {
        var _select = $(list_item).parents('ul').data('original-select');
        $(_select).val($(list_item).data('id')).change();
        $(list_item).addClass('active').siblings().removeClass('active');
    }
}

What this function does is:

  1. Accepts an element
  2. Checks the data attribute ‘id’ for the element to see if it exists, this is our selected value / swatch
  3. If it exists, find its parent wrapper, in this case our unordered list object and search for its original-select attribute. Remember we added that in our parseSelect()?
  4. Now that we have both our selected value and the reference the original selector, we can use jQuery’s val() function to select option in our hidden <select>
  5. Call our select’s change() function to trigger any events attached to our original selector’s change event
  6. Finally, add the class “active” to our current list item, while simultaneously removing the “active” class from all of the other list items

Note: On Magento, calling .change() itself is not enough to trigger the rest of the events that Magento uses to update prices, etc. If you’re using SimpleConfigurables or a similar extension, make sure you pick up simulate.js (there are several libraries) to trigger a change event on the select

Now we call our functions in order:

<script type="text/javascript">
    var selectObj = document.getElementById('colour-selector');
    $(selectObj).css('position', 'absolute').css('left', '-99999px'); //Remember I don't like hiding elements?
    var listObject = parseSelect(selectObj);
    var swatchSelector = createSwatches(listObject);

    //Add our swatchSelector before the select, essentially replaces the select with our new swatch selector while hiding the old one
    $(selectObj).prepend(swatchSelector);
</script>

Wire up the Event

Now we have our function, we need it called whenever a user clicks on a swatch.
In line 15 of our createSwatches() function, let’s add the following lines:

$(list_item).on('click', function (e) {
    swatchSelected(e.target);
});

There! Now on creation of each list item, an event handler will be attached to it listening for a user’s click. When clicked, the element will call swatchSelected and select our hidden select element for us.

Skinning our new Swatch Selector

We’ve got the content setup for our new swatch selector. However, all we see is an unordered list of labels. Not very “swatchy”.
There are many different ways to apply colours to our swatches, but for the context of this tutorial, I usually generate a list of hex colours associated with each ID.
E.g.

var colours = {red:"#ff0000",blue:"#0000ff",yellow:"#ffff00"};

Then with our swatches, we can apply the style to the list_item based on its id:

...
$(list_item).children('a').text(list.items[i]['label']); //Line 14 for reference
...
//Add the following line
$(list_item).css('background-color', colours[list.items[i]['id']]);
...

And now we should have the following html output!

<div class="swatch-wrapper">
    <ul class="swatch-list attribute-color">
        <li title="Red" style="background-color: #ff0000;"><a>Red</a></li>
        <li title="Blue" style="background-color: #0000ff;"><a>Blue</a></li>
        <li title="Yellow" style="background-color: #ffff00;"><a>Yellow</a></li>
    </ul>
</div>

How you decide to skin the rest is up to you.
Here’s an example of how you can skin the swatches above:

First:

/* Reset our unordered list */
ul.swatch-selector {
    list-style-type: none;
    margin: 0;
    padding: 0;
}

Then we style our actual list items

ul.swatch-selector li {
    display: block;
    float: left; /* Have them laid out horizontally */
    margin-right: 10px; /* Have a slight margin on the right */
    width: 25px;
    height: 25px;
    border: 1px solid transparent; /* This is so our hover effect does not shift the box */
    cursor: pointer; /* Let our users know they can click this! */
}

And style our active list items

ul.swatch-selector li.active {
    border: 1px solid #000;
}

And your swatches are ready!

Future changes

This is of course a very basic swatch selector based only in jQuery. Even so, try and do the following to make it even better:

– Turn it into a jQuery function. Hint: Extend $.fn
– Have the hex colours stored in the backend
– Have multiple html outputs, e.g. have it output divs instead of a ul/ol
– Listen to the original selector’s change() event and update .active on our swatch selector. Hint: Use data attributes to give our original selector a reference to the new one!

And as a final note, there’s a million ways to skin a cat. It’s the same with swatches. If you come up with an even better way to build a fully front-end swatches script, let us know!

Related: Mini Tutorial: Adding Custom Attributes to the Backend Product Grid