views:

490

answers:

4

In my online store, I want to implement an interface like Amazon's for selecting product size and color. I.e., instead of radio buttons for the different sizes / colors, the user should see a bunch of little boxes, each containing either a swatch of color or the name of a size. When the user clicks a box, the border should change to indicate it's selected.

I'm using jQuery already, so I thought that the jQuery selectable widget would be a natural way to implement this. Unfortunately, it doesn't appear to give you control over the maximum number of items the user can select, and therefore I couldn't limit users to selecting online one color / size in the UI.

What's the best way to implement this functionality in a jQuery-friendly way (if possible)?

+4  A: 

It is possible to have jQuery selectable prevent selection, although unfortunately it is not very intuitive:

$('#selectable').selectable({
   selected: function(event, ui) {
       var total = $(ui.selected).siblings('li.ui-selected').length + 1;
       if(total > 1) {
           $(ui.selected).removeClass('ui-selected');
       }
   }
});

That will stop anything past 1 selection being made on a selectable widget.

The rest is just customizing it for your specific needs.

EDIT: I got a little bit bored so I implemented some more so you could get an idea.

Check out a working demo. There is a lot of repetition for things done to sizes and colors that you can probably get rid of but I wanted to keep them separate because I'd imagine things could change between the two. Anyways, that should give you a pretty good start.

Paolo Bergantino
A: 

If your goal is to allow only one item to be selected, this will do it:

$(function() {
    $("#selectable").selectable({
     // this will prevent selecting a second item (for dragging\clicking)
     selecting: function(event,ui) {
      if ($(ui.selecting).siblings("li.ui-selecting").length > 0)
       $(ui.selecting).removeClass("ui-selecting");
     },
     // this will prevent selecting a second item (for CTRL+Click)
     selected: function(event,ui) {
      if ($(ui.selected).siblings("li.ui-selected").length > 0)
       $(ui.selected).removeClass("ui-selected");
     },
     // this will prevent un-selecting items (one item must be selected)
     unselected: function(event,ui) {
      if ($(ui.unselected).siblings("li.ui-selecting").length == 0)
       $(ui.unselected).addClass("ui-selected");
     }
    })
});

This code will allow a single item to be selected, and it will prevent selecting another item whether by mouse-dragging or using CTRL+click. It will also prevent un-selecting items. It has very similar behavior to what Amazon have.

You can use the stop event to perform changes on the page after an item is selected (similar to the examples that comes with JQuery UI).

Aziz
A: 

Man, that Amazon interface is ugly. I'd be tempted to try something more along the lines of:

  • Simple Color Picker (naturally, you would not use the hex representation of the color in the picker)
  • Color Picker (although this is probably a little more control than they would need over the color)
  • Flat Color Picker (almost as ugly as the Amazon one)

Sizes should be easy enough. I'd probably do little pictures of shirts of different sizes though, sort of like here:

(that's the best example of picking shirt size that I could find, most places are pretty awful)

altCognito
+3  A: 

Here is how to do this properly using progressive enchancement. The idea is simple, turn a decently marked up HTML form into nicer color and size swatch buttons for users that have JavaScript.

The benefits of this method are many, you can easily switch, or remove, components (JS/CSS) and have the form still work. It will work for users that don't hvae javascript, and if we're careful, it will be accessible via keyboard and screen readers. It also requires no extra code on submit, and will be a lot easier to use with a form validator.

Most of the work is done with CSS with only minimal JavaScript to add the finishing touches.

Full working example example can be found on jsbin. It includes both color and size selections. I have only included the colors here as there is only a slight difference in the CSS between the them.

The HTML

First off, markup the form components. Here are the basics, you may obviously want to add things, like a wrapper around the whole widget, or use other classnames.

<label>Color</label>
<ul class="radio color">
  <li class="red">
    <input type="radio" name="color" id="color_red" value="red" />
    <label for="color_red">Red</label>
  </li>
  <li class="green">
    <input type="radio" name="color" id="color_green" value="green" />
    <label for="color_green">Green</label>
  </li>
  <!-- ...and so on... -->
</ul>

This may seem a lot for "basics", but there are several things going on here. The whole UL container is flagged as "radio" so everything within can be targeted appropriately with CSS ("ul.radio label").

Additionally I add a class corresponding to the input's name, and a class on each LI echoing the value to allow CSS to add appropriate styles ("ul.color li.red"). The rest is pretty much your standard form markup.

The JavaScript

The JavaScript is very simple. All we do is detect which radio input is currently selected and flag the container LI with a "selected" class, thus allowing CSS to highlight it appropriately.

jQuery(function($){
  // notify CSS that JS is present
  $( 'body' ).addClass( 'js-active' );
  // for every radio control...
  $( 'ul.radio' )
    // redo every change
    .bind('click', function () {
      // refresh "current classes"
      $( 'li', this ).removeClass( 'selected' )
        .filter( ':has(:checked)' ).addClass( 'selected' );
    })
    // trigger initial run
    .trigger( 'click' );  
});

Please note: This "trick" of triggering click to enforce the initial run may be unsafe if you have click handlers higher (typically document.body), I use this method for brevity.

The CSS

Here is where the real magic happens. The idea is that we can tuck the input controls out of view and style the labels as color buttons. So long as the for attributes on the labels correctly point to id's on the inputs then clicking the labels will suffice to select the control correctly.

We need to target things only within ".js-active" so that users that don't have JavaScript but still have CSS will see the radio input elements. Some sort of ".radio-js" class could alternatively be added on the UL wrapper to get the same thing done.

Firstly, float the buttons and give them a more button-like appearance:

/* attributes are lined up horizontally */
ul.color li,
ul.size li {
  border : 1px solid #aaa;
  margin : 0 4px 0 0;
  padding : 2px;
  float : left;
  overflow : hidden;
  position : relative;
}

We put position:relative and overflow:hidden on each LI so we can shift the input element out of view. This will keep it accessible, but we just no longer have to look at it:

/* if JS: remove inputs from sight */
.js-active ul.color input,
.js-active ul.size input {
  position : absolute;
  left : -999em;
}

Next up is turning the labels into buttons:

/* if JS: turn color labels into buttons */
.js-active ul.color label {
  cursor : pointer;
  display : block;
  height : 18px;
  width : 18px;
  text-indent : -999em; /* hide text */
}
/* color buttons in color -- allowed even if no JS */
ul.color li.red label   { background : red;   }
ul.color li.green label { background : green; }
ul.color li.blue label  { background : blue;  }

Finally, set some styles for our highlighted / selected item.

/* if JS: current selected highlight */
.js-active ul.color .selected,
.js-active ul.size .selected {
  border : 2px solid #000;
  padding : 1px;
}

For completeness: Getting the select values if you want to add more effects is demonstrated elsewhere on SO.

Borgar
Extremely clear and comprehensive -- thanks!
Horace Loeb
Begrudgingly.. Much better answer.
Paolo Bergantino
Just re-read this -- I can't get over how fucking incredible this answer is!
Horace Loeb