DanWebb
7/29/2015 - 10:24 AM

Variant option selection with tabs instead of select boxes and no need to initialise it in the product template.

Variant option selection with tabs instead of select boxes and no need to initialise it in the product template.

.variant-selection {
  margin: 10px -10px 10px -10px;
  
  h4 { margin: 0 0 3px 10px; }
  
  span { 
    display: inline-block; 
    border-radius: 20px; 
    border: 1px solid $dark-shade; 
    color: $dark-shade; 
    padding: 8px 10px; 
    font-weight: 300;
    margin: 5px 10px;
    background: #fff;
    cursor: pointer;
  }
  span.active { background: $primary-4; border-color: $primary-4; color: #fff; }
  span.unavailable { background: $light-shade; border-color: $light-shade; color: $mid-shade; cursor: default; }
}
<form action="/cart/add" method="post" enctype="multipart/form-data" id="AddToCartForm" class="form-vertical">

  <!-- Product Price ===================== -->
  <div itemprop="price" class="product-price"> 
    <span id="ProductPrice">{{ current_variant.price | money }}</span>
    
    <!-- on sale -->
    {% if current_variant.compare_at_price > current_variant.price %}
      &nbsp;<span class="h2">was</span> <span class="h2 line-through">{{ current_variant.compare_at_price | money }}</span>
    {% endif %}
  </div>
  <!-- //Product Price -->
  
  <!-- 
    Variant Selection ===================== 

    Generate a set of tags for each of the variant options.
    -- option_list ensures we don't list the same variant option twice.
    -- class="active" represents the currently selected option
    -- class="unavailable" greys out the options preventing it from being selected
    -- when an option is selected each data-option(1/2/3) is checked in the VariantSelect js to
     ensure only compatible matches can be selected
  -->
  {% if product.available %}
  {% for option in product.options %}
  {% assign option_index = forloop.index0 %}
  {% assign option_list = '' %}
  {% assign selected_variant = product.selected_or_first_available_variant %}
  <div class="variant-selection">
    <h4>{{ option }}:</h4>
    {% for variant in product.variants %}
    
      {% if variant.available %}
      {% if option_list contains variant.options[option_index] %}
    	<!-- hidden tags (get swapped out when a relevant option is chosen) -->
    	{% if option_index != 0 %}<span style="display:none;" data-id="{{ variant.id }}" data-index="{{ option_index }}" data-option1="{{ variant.options[0] }}" data-option2="{{ variant.options[1] }}" data-option3="{{ variant.options[2] }}" data-val="{{ variant.options[option_index] }}" data-price="{{ variant.price | money }}">{{ variant.options[option_index] }}</span>{% endif %}
      {% else %}
    	{% assign option_list = option_list | append: variant.options[option_index] | append: ', ' %}
        <span 
          class="{% if variant == selected_variant %}active{% endif %} {% unless variant.options[0] == selected_variant.options[0] %}{% if option_index != 0 %}unavailable{% endif %}{% endunless %}"
          data-id="{{ variant.id }}"
          data-index="{{ option_index }}"
          data-option1="{{ variant.options[0] }}"
          data-option2="{{ variant.options[1] }}"
          data-option3="{{ variant.options[2] }}"
          data-val="{{ variant.options[option_index] }}"
          data-price="{{ variant.price | money }}"
        >{{ variant.options[option_index] }}</span>
      {% endif %}
      {% endif %}
    
    {% endfor %}
  </div>
  {% endfor %}
  {% endif %}
  <!-- //Variant Select -->
  
  <!-- Form Controls ===================== -->
  <label for="Quantity" class="quantity-selector">{{ 'products.product.quantity' | t }}</label>
  <input type="number" id="Quantity" name="quantity" value="1" min="1" class="quantity-selector">
  
  <!-- the current variant id is stored here and submitted with the form -->
  <input type="hidden" data-id="{{ product.selected_or_first_available_variant.id }}" name="id">
  
  <button type="submit" name="add" id="AddToCart" class="btn">
    <span id="AddToCartText">{{ 'products.product.add_to_cart' | t }}</span>
  </button>
  <!-- //Form Controls -->
</form>
  // Product page variant option selection
  var VariantSelect = (function() {
    
    var $variantSelection;
    
    // The selection filters from top to bottom. Only the 2nd and 3rd option 
    // variants can become unavailable
    function setUnavailable(index) {
      var option1 = $variantSelection.find('.active[data-index="0"]').data('option1');
      
      if(index===0) {
        // we need to set the unavailable items in indexes 1 & 2 (remember it's 0 index)
        $variantSelection.find('span[data-index="1"]:not([data-option1="'+option1+'"]), span[data-index="2"]:not([data-option1="'+option1+'"])').addClass('unavailable').removeClass('active');
        $variantSelection.find('span[data-index="1"][data-option1="'+option1+'"], span[data-index="2"][data-option1="'+option1+'"]').removeClass('unavailable');
        
      }
      
      else if(index===1) {
        // we need to set the unavailable items in index 2
        var option2 = $variantSelection.find('.active[data-index="1"]').data('option2');
        $variantSelection.find('span[data-index="2"]:not([data-option1="'+option1+'"][data-option2="'+option2+'"])').addClass('unavailable').removeClass('active');
        $variantSelection.find('span[data-index="2"][data-option1="'+option1+'"][data-option2="'+option2+'"]').removeClass('unavailable');
      }
    }
    
    // make sure we still have an active option in each section and that all options 
    // are showing that should be
    function setActive() {
      $variantSelection.each(function() {
        if(!$(this).find('span.active').length) $(this).find('span:not(.unavailable):first').addClass('active');
          
        // make sure the active item is showing
        if(!$(this).find('span.active:visible').length) {
          var $activeItem = $(this).find('span.active');
          var activeValue = $activeItem.data('val');
          $(this).find('span[data-val="'+activeValue+'"]:visible').hide().after($activeItem);
          $activeItem.show();
        }
        
        // make sure items that are not unavailable which have same name variants that
        // are unavailable are swapped out
        $(this).find('span.unavailable:visible').each(function() {
          var itemText = $(this).data('val');
          var $validItem = $(this).siblings('span[data-val="'+itemText+'"]:not(.unavailable):first');

          if($validItem.length) {
            $(this).hide().after($validItem);
            $validItem.show();
          }
        });
      });
    }
    
    function getActive() {
      if($variantSelection.find('span.active[data-index="2"]').length) return $variantSelection.find('span.active[data-index="2"]');
      if($variantSelection.find('span.active[data-index="1"]').length) return $variantSelection.find('span.active[data-index="1"]');
      if($variantSelection.find('span.active[data-index="0"]').length) return $variantSelection.find('span.active[data-index="0"]');
    }
    
    // the last index will always hold the correct id
    function setVariant() {
      var $active = getActive();

      $('[name="id"]').val($active.data('id'));
      $('#ProductPrice').html($active.data('price'))
    }
    
    function selectItem() {
      if($(this).hasClass('unavailable')) return;
      
      $(this).toggleClass('active').siblings('span').removeClass('active');
      var index = $(this).data('index');
      
      setUnavailable(index);
      setActive();
      setId();
    }
    
    function init() {
      if(!$('.variant-selection').length) return;
      
      $variantSelection = $('.variant-selection');
      
      $variantSelection.find('span').click(selectItem);
      
      setActive();
      setVariant();
    }
    
    return function() {
      $(init);
    };
    
  })();
  
  // initialise it
  VariantSelect();