skip to Main Content

I’m working on a shoe store website that based on Shopify platform. We want to hide the variants of shoe sizes that are out of stock. After doing some digging I found a Shopify tutorial that tells me to enter a new “snippet” and then edit the theme.liquid file. So I did. This is a single variant case where only shoe sizes vary.

However, when a customer first comes to a product page and clicks a dropdown menu for a shoe size, all of the shoe sizes show up. Unless the customer clicks on the size that is not available and then again clicks on the drop down menu, the menu still displays all of the sizes.

I would like to adjust the code so that the unavailable shoe sizes are eliminated from the beginning. Here is the code snippet:

{% comment %}
Remove sold-out variants.
Only works for products that have one option.
It won’t work with products that have two or three options, like Size and
Color.
See: https://docs.myshopify.io/themes/customization/products/hide-variants-
that-are-sold-out
{% endcomment %}

{% if product.options.size == 1 %}
  <script>
  var $addToCartForm = $('form[action="/cart/add"]');
  if (window.MutationObserver && $addToCartForm.length) {
    if (typeof observer === 'object' && typeof observer.disconnect === 
    'function') {
      observer.disconnect();
    }
    var config = { childList: true, subtree: true };
    var observer = new MutationObserver(function() { 
      {% for variant in product.variants %}
        {% unless variant.available %}
          jQuery('.single-option-selector option').filter(function() { 
          return jQuery(this).text() === {{ variant.title | json }}; 
          }).remove();
        {% endunless %}
      {% endfor %}
      jQuery('.single-option-selector').trigger('change');
      observer.disconnect();
    });  
    observer.observe($addToCartForm[0], config);
  }
  </script>
{% endif %}

5

Answers


  1. I’m not sure how your code looks exactly, since the provided one is only showing the MutationObserver function which handles the select when changed.

    Probably you have a product select in your product template where you generate your variants. So you should have something like this in your product.liquid file or in the section that is included there:

    <select name="id" id="product-select">
        {% for variant in product.variants %}
            <option value="{{ variant.id }}">{{ variant.title }}</option>
        {% endfor %}
    </select>
    

    You will have to modify that code so you check if the variable is available. It must become something like this:

    <select name="id" id="product-select">
        {% for variant in product.variants %}
            {% if variant.available %}
                <option value="{{ variant.id }}">{{ variant.title }}</option>
            {% endif %}
        {% endfor %}
    </select>
    

    So this way your select won’t include any of the unavailable variants beforehand.

    Login or Signup to reply.
    1. First, open your theme.liquid file and add below code in this “{% include ‘remove-sold-out’ %}” and click on save button.
    2. Then, In the Snippets directory, click Add a new snippet with name “remove-sold-out”.
    3. Now open new created file and add code from this link and click save.

    In this way, you can remove variants that are sold out.

    Thanks

    Login or Signup to reply.
  2. I have removed the out-of-stock and not available variants for a product that has two, or even three options.

    Just follow the below-mentioned steps and update your Shopify Theme code:

    1. go to product-template.liquid file and paste the following code at the end of the file.
     {% if product.options.size >= 1 %}
      <script>
        var product_variants_available = [
          {%- for variant in product.variants -%}
            {%- if variant.available -%}
              `{{ variant.title }}`,
            {%- endif -%}
          {%- endfor -%}
        ];
        var product_options_count = {{ product.options.size }};
      </script>
    {% endif %}
    1. find out theme.js file and paste the below code.
    function sortMap(map) {
      try {
        map.forEach(function(valueList, key, map) {
          map.get(key).sort();
        });
      } catch(err){
      }
    }
    
    function updateList(option, updatedList) {
      try {
        var selectedValue = $(option).children("option:selected").val();
        var index = -1;
    
        // remove previous list
        $(option).find('option').remove();
    
        //update list with new values
        for (var i=0; i<updatedList.length; i++) {
          $(option).append($('<option>', { 
            value: updatedList[i],
            text : updatedList[i] 
          }));
    
          if (updatedList[i] == selectedValue) {
            index = i;
          }
        }
        //update previous selected value    
        if (index != -1) {
          $(option).val(updatedList[index]).change(); 
        } else if (updatedList[0]){
          $(option).val(updatedList[0]).change();
        }
      } catch(err){
      }
    }
    
    function updateListByMap(options, map) {
      try {
        var selectedValue1 = $(options[0]).children("option:selected").val();
        map.forEach(function(valueList, key, map) {
          if (key == selectedValue1) {
            updateList(options[1], valueList);
          }
        });
        //remove option-2 values on empty map
        if (map.size == 0) {
          $(options[1]).find('option').remove();
        }
      } catch(err){
      }
    }
    
    function updateListByMap1(options, map1) {
      try {
        var selectedValue1 = $(options[0]).children("option:selected").val();
        var selectedValue2 = $(options[1]).children("option:selected").val();
        var variant = selectedValue1+" / "+selectedValue2;
        map1.forEach(function(valueList, key, map) {
          if (key == variant) {
            updateList(options[2], valueList);
          }
        });
        //remove option-3 values on empty map
        if (map1.size == 0) {
          $(options[2]).find('option').remove();
        }
      } catch(err) {
      }
    }
    
    function removeImages(colorSelector) {
      try{
        var colorOptions = $(colorSelector).find('option');
        var imageOptions = $('.product-single__thumbnails-product-template').find('li');
        if (imageOptions.length != colorOptions.length) {
          // hide previous images
          $(imageOptions).hide();
          
          //show availabe variants images
          for (var l=0; l<colorOptions.length; l++) {
            var colorName = $(colorOptions[l]).val();
            $("#"+colorName).show();
          }
          
        }
      } catch(err){
        console.log(err);
      }
    }
    
    $( document ).ready(function() {
      if( typeof product_variants_available != "undefined" && typeof product_options_count != "undefined") {
        var $addToCartForm = $('form[action="/cart/add"]');
        if (window.MutationObserver && $addToCartForm.length) {
          if (typeof observer === 'object' && typeof observer.disconnect === 'function') {
            observer.disconnect();
          }
          var config = { childList: true, subtree: true };
          var observer = new MutationObserver(function() {
            try{
              var options = $('.single-option-selector');
              if (product_options_count == 1 && options[0].length != product_variants_available.length) {
                updateList(options[0], product_variants_available);
                
              } else if (product_options_count == 2 || product_options_count == 3) {
                var map = new Map();
                var map1 = new Map();
                var list = [];
                // initialize map & list with values
                for (var i=0; i<product_variants_available.length; i++) {
                  var variant = product_variants_available[i].split("/");
                  var key = variant[0].trim();
                  var value = variant[1].trim();
                  if(map.has(key)) {
                    if (map.get(key).includes(value) == false) {
                      map.get(key).push(value);
                    }
                  } else {
                    map.set(key, [value]);
                    list.push(key);
                  } 
                }
                //sort map values
                if (product_options_count == 3) {
                  sortMap(map);
                }
                //initialize map1 if there are three options
                if (product_options_count == 3) {
                  // initialize map1 with values
                  for (var i=0; i<product_variants_available.length; i++) {
                    var variant = product_variants_available[i].split("/");
                    var key = variant[0].trim() +" / "+ variant[1].trim();
                    var value = variant[2].trim();
                    if(map1.has(key)) {
                      map1.get(key).push(value);
                    } else {
                      map1.set(key, [value]);
                    }   
                  }
                }
                //update option-1
                updateList(options[0], list);
                //update option-2
                updateListByMap(options, map);
                //update option-3 based on option count
                if (product_options_count == 3) {
                  updateListByMap1(options, map1);
                }
                //add event on option-1 change
                $(options[0]).change(function () {
                  updateListByMap(options, map);
                  if (product_options_count == 3) {
                    updateListByMap1(options, map1);
                  }
                });
                //add event on option-2 change
                if (product_options_count == 3) {
                  $(options[1]).change(function () {
                    updateListByMap1(options, map1);
                  });
                }
              }
              // remove the images of not available variants
              var selectors = $('.selector-wrapper');
              var colorSelectorIndex = -1;
              for (var k=0; k<selectors.length; k++) {
                var label = $(selectors[k]).find("label").text();
                if (label.trim().toLowerCase().includes("color")) {
                    colorSelectorIndex=k;
                }
              }
              if (colorSelectorIndex!=-1) {
                removeImages(options[colorSelectorIndex]);
              }
            } catch(err){
            }
            observer.disconnect();
          });
          observer.observe($addToCartForm[0], config);
          $('.single-option-selector').trigger('change');
        }
      }
    });

    NOTE:

    • This code will remove both the not available and sold out variants
    • This code will work for a product that has one, two, or even three options
    • This code will remove the picture if it’s all variants are not available or out-of-stock
    Login or Signup to reply.
  3. I recently removed sold-out variants for multiple options. Hope it will work for you too.

    1. Create Snippets with the name of "linked-options".

    2. Paste the code below on your linked-options file.

    <script>
    window.addEventListener('DOMContentLoaded', function(event) {
      var Shopify = Shopify || {};
        
      // Required functionality from depricated options_selection.js
      Shopify.arrayIncludes = function(e, t) {
        for (var n = 0; n < e.length; n++)
            if (e[n] == t) return !0;
        return !1
      }, Shopify.uniq = function(e) {
          for (var t = [], n = 0; n < e.length; n++) Shopify.arrayIncludes(t, e[n]) || t.push(e[n]);
          return t
      }
    
      Shopify.optionsMap = {};
    
      Shopify.updateOptionsInSelector = function(selectorIndex) {
          
        switch (selectorIndex) {
          case 0:
            var key = 'root';
            var selector = jQuery('.single-option-selector:eq(0)');
            break;
          case 1:
            var key = jQuery('.single-option-selector:eq(0)').val();
            var selector = jQuery('.single-option-selector:eq(1)');
            break;
          case 2:
            var key = jQuery('.single-option-selector:eq(0)').val();  
            key += ' / ' + jQuery('.single-option-selector:eq(1)').val();
            var selector = jQuery('.single-option-selector:eq(2)');
        }
        
        var initialValue = selector.val();
        selector.empty();    
        var availableOptions = Shopify.optionsMap[key];
        for (var i=0; i<availableOptions.length; i++) {
          var option = availableOptions[i];
          var newOption = jQuery('<option></option>').val(option).html(option);
          selector.append(newOption);
        }
        jQuery('.swatch[data-option-index="' + selectorIndex + '"] .swatch-element').each(function() {
          if (jQuery.inArray($(this).attr('data-value'), availableOptions) !== -1) {
            $(this).removeClass('soldout').show().find(':radio').removeAttr('disabled','disabled').removeAttr('checked');
          }
          else {
            $(this).addClass('soldout').hide().find(':radio').removeAttr('checked').attr('disabled','disabled');
          }
        });
        if (jQuery.inArray(initialValue, availableOptions) !== -1) {
          selector.val(initialValue);
        }
        selector.trigger('change');
        
      };
    
      Shopify.linkOptionSelectors = function(product) {
        // Building our mapping object.
        for (var i=0; i<product.variants.length; i++) {
          var variant = product.variants[i];
          if (variant.available) {
            // Gathering values for the 1st drop-down.
            Shopify.optionsMap['root'] = Shopify.optionsMap['root'] || [];
            Shopify.optionsMap['root'].push(variant.option1);
            Shopify.optionsMap['root'] = Shopify.uniq(Shopify.optionsMap['root']);
            // Gathering values for the 2nd drop-down.
            if (product.options.length > 1) {
              var key = variant.option1;
              Shopify.optionsMap[key] = Shopify.optionsMap[key] || [];
              Shopify.optionsMap[key].push(variant.option2);
              Shopify.optionsMap[key] = Shopify.uniq(Shopify.optionsMap[key]);
            }
            // Gathering values for the 3rd drop-down.
            if (product.options.length === 3) {
              var key = variant.option1 + ' / ' + variant.option2;
              Shopify.optionsMap[key] = Shopify.optionsMap[key] || [];
              Shopify.optionsMap[key].push(variant.option3);
              Shopify.optionsMap[key] = Shopify.uniq(Shopify.optionsMap[key]);
            }
          }
        }
        // Update options right away.
        Shopify.updateOptionsInSelector(0);
        if (product.options.length > 1) Shopify.updateOptionsInSelector(1);
        if (product.options.length === 3) Shopify.updateOptionsInSelector(2);
        // When there is an update in the first dropdown.
        jQuery(".single-option-selector:eq(0)").change(function() {
          Shopify.updateOptionsInSelector(1);
          if (product.options.length === 3) Shopify.updateOptionsInSelector(2);
          return true;
        });
        // When there is an update in the second dropdown.
        jQuery(".single-option-selector:eq(1)").change(function() {
          if (product.options.length === 3) Shopify.updateOptionsInSelector(2);
          return true;
        });  
      };
       
      {% if product.available and product.options.size > 1 %}
        var $addToCartForm = $('form[action="/cart/add"]');
        if (window.MutationObserver && $addToCartForm.length) {
          if (typeof observer === 'object' && typeof observer.disconnect === 'function') {
            observer.disconnect();
          }
          var config = { childList: true, subtree: true };
          var observer = new MutationObserver(function() {      
            Shopify.linkOptionSelectors({{ product | json }});
            observer.disconnect();
          });  
          observer.observe($addToCartForm[0], config);
        }
      {% endif %}
        
      var selector = jQuery('.single-option-selector:eq(0)');
      selector.trigger('change'); 
    
      {% if product.options.size == 1 %}
        {% for variant in product.variants %}
          {% unless variant.available %}
            jQuery('.single-option-selector option').filter(function() { return jQuery(this).text().trim() === {{ variant.title | json }}; }).remove();
          {% endunless %}
        {% endfor %}
        jQuery('.single-option-selector').trigger('change');
      {% endif %}
    });
    </script>
    1. Add the following code to the theme.liquid file before the closing body tag
    {% render 'linked-options' %}

    You can also visit this article that I wrote for reference.

    Login or Signup to reply.
  4. Using javascript when you can use liquid instead is not the best idea.

    Go to your main product grid and try this:

    {%- for product in collection.products -%}
              {%- if product.available -%}
                <li class="grid__item">
                  {% render 'product-card',
                    product_card_product: product,
                    media_size: section.settings.image_ratio,
                    show_secondary_image: section.settings.show_secondary_image,
                    add_image_padding: section.settings.add_image_padding,
                    show_vendor: section.settings.show_vendor,
                    show_image_outline: section.settings.show_image_outline,
                    show_rating: section.settings.show_rating,
                    show_atc: section.settings.show_atc
                  %}
                </li>
              {%- endif -%}{%- endfor -%}
    

    You might need to play around with product.available product.variant.available and so on depending on exactly which ones you want to remove, but I can confirm this code works to not render sold out products and at the same time keep all counters/filters/pagination intact.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search