Benjk
Benjk

Reputation: 57

shopify how do you create a product color swatch

Does anyone know how to this? Adding a color swatch and it changing the dropdown menu and I guess by changing the dropdown it will change the variant itself but not sure how to do it.

https://giphy.com/gifs/SUXFrP069WJNU8D2Iw/html5

Code I have tried:

   $('.color-swatch').on('change', function() {

  var value = $(this).val();

    $('.single-option-selector__radio').find("option:selected").val(value).change();

});

    $('.color-swatch').on('change', function() {

      var optionIndex = jQuery(this).closest('.dbtle-swatches').attr('data-option-index');
      var optionValue = jQuery(this).val();

      var test = jQuery('form')
      .closest('option')
      .val(optionValue)
      .trigger('change');

    });
    <div class="dbtle-color-swatches">
{% assign option_index = 0 %}
{% for option in product.options_with_values %}
{% if option.name == "Color" %}
{% assign option_index = forloop.index0 %}

  <label class="swatch-label">{{option.name}}</label>
  <div class="dbtle-swatches" data-option-index="{{ option_index }}">
    {% for value in option.values %}
    <input id="swatch-{{ option_index }}-{{ value | handle }}"
           class="color-swatch" 
           type="radio" 
           name="color" 
           value="{{ value | escape }}" 
           data-class=""
           data-index="{{ option_index }}"
           data-section-id="{{ section.id }}"
           {% if option.selected_value == value %} checked{% endif %}/>

    <div data-value="{{ value | escape }}" class="swatch">
      <div class="tooltip">{{ value }}</div>
      <label for="swatch-{{ option_index }}-{{ value | handle }}" style="background-color: {{ value | split: " " | last | handle }}">{{value}}</label>
    </div>
    {% endfor %}
  </div>  
  {% endif %}
  {% endfor %}
</div>

Upvotes: 0

Views: 1314

Answers (1)

drip
drip

Reputation: 12933

Ok first you will need the main select, that is what we will be submitting behind the scene.

<select class="main-select" name="id" style="display: none;">
  {% for variant in product.variants %}
    <option 
      value="{{variant.id}}"
      data-option-1="{{variant.option1}}"
      data-option-2="{{variant.option2}}"
      data-option-3="{{variant.option3}}"
    >{{ variant.title }}</option>
  {% endfor %}
</select>

You should hide it doesn't need to be visible for the user. Please take notice of the lines that use data-option-* we will use this to target the hidden select afterwards.

After that you need to create the logic for the swatches/select for the color/sizes.

{% comment %} Create the swatch and select logic {% endcomment %}
<div class="options">
  {% for option in product.options_with_values %}
    <h5>{{option.name}}</h5>
    {% comment %} We check if it's color or not {% endcomment %}
    {% if option.name == 'Color' %}
      <ul>
        {% for value in optioin.values %}
          <li>
            <input type="radio" id="radio-{{option.name | handle}}-{{value | handle}}" name="radio-{{option.name | handle}}" value="{{value}}" data-option="{{option.position}}">
            <label for="radio-{{option.name | handle}}-{{value | handle}}">{{value}}</label>
          </li>
        {% endfor %}
      </ul>
    {% else %}
      <select>
        {% for value in option.values %}
          <option value="{{value}}" data-option="{{option.position}}">{{value}}</option>
        {% endfor %}
      </select><!-- /# -->
    {% endif %}
  {% endfor %}
</div><!-- /.options -->

Once again see the data-option="{{option.position}}" it's important here, since we will use that.

After that you need your JS logic.

$('.options').find('select, input[type="radio"]').on('change', function(){
  let target = '';
  $('.options').find('select option:selected, input[type="radio"]:checked').each(function(){
    const $this = $(this);
    const dataPosition = $this.data('option');
    const thisValue = $this.val();
    target += `[data-option-${dataPosition}="${thisValue}"]`
  })
  if($(target).length === 1){
    $('.main-select').val($(target).val());
  }
})

This is the minimum code required, there is no logic for disabled inputs, prices etc.. you will need to write those if you like to support it as well.

To clarify we target the select and radio buttons we create in the <div class="options"> element from the object product.options_with_values. So we do it like so -> $('.options').find('select, input[type="radio"]').on('change'

After that we create an empty variable that will hold our end target element let target = '';

Then inside the change event we target each selected/checked option/radio button like so $('.options').find('select option:selected, input[type="radio"]:checked') and loop them while targeting the option position and value in order to populate the target string. Like so

target += `[data-option-${dataPosition}="${thisValue}"]`

So if you select a single option you will get something like this [data-option-1="Red"], if you select a color and size it will become [data-option-1="Red"][data-option-2="XS"].

We use this element to find an existing option from the main select we create in the beginning and check if there a single one like so if($(target).length === 1). We check this since when you select a single swatch there will be multiply options and we need to have only a single option, for that we must be sure that there is no more than 1 and you select both a color and a size.

After that you just change the main select like so $('.main-select').val($(target).val());;

Have in mind that this is overly simplified version, there will be additional steps for sold out variants, price changing and so on.

To summarize here is the whole code:

{% comment %} This is the main select we will submit behind the scene {% endcomment %}
<select class="main-select" name="id" style="display: none;">
  {% for variant in product.variants %}
    <option 
      value="{{variant.id}}"
      data-option-1="{{variant.option1}}"
      data-option-2="{{variant.option2}}"
      data-option-3="{{variant.option3}}"
    >{{ variant.title }}</option>
  {% endfor %}
</select><!-- /# -->

{% comment %} Create the swatch and select logic {% endcomment %}
<div class="options">
  {% for option in product.options_with_values %}
    <h5>{{option.name}}</h5>
    {% comment %} We check if it's color or not {% endcomment %}
    {% if option.name == 'Color' %}
      <ul>
        {% for value in optioin.values %}
          <li>
            <input type="radio" id="radio-{{option.name | handle}}-{{value | handle}}" name="radio-{{option.name | handle}}" value="{{value}}" data-option="{{option.position}}">
            <label for="radio-{{option.name | handle}}-{{value | handle}}">{{value}}</label>
          </li>
        {% endfor %}
      </ul>
    {% else %}
      <select>
        {% for value in option.values %}
          <option value="{{value}}" data-option="{{option.position}}">{{value}}</option>
        {% endfor %}
      </select><!-- /# -->
    {% endif %}
  {% endfor %}
</div><!-- /.options -->

{% comment %} JS logic {% endcomment %}
<script>
$('.options').find('select, input[type="radio"]').on('change', function(){
  let target = '';
  $('.options').find('select option:selected, input[type="radio"]:checked').each(function(){
    const $this = $(this);
    const dataPosition = $this.data('option');
    const thisValue = $this.val();
    target += `[data-option-${dataPosition}="${thisValue}"]`
  })
  if($(target).length === 1){
    $('.main-select').val($(target).val());
  }
})
</script>

And that's pretty much the just of it.

Good luck!

Upvotes: 2

Related Questions