Skip to content

Instantly share code, notes, and snippets.

@carolineschnapp
Last active October 27, 2022 17:59
Show Gist options
  • Star 41 You must be signed in to star a gist
  • Fork 27 You must be signed in to fork a gist
  • Save carolineschnapp/1083007 to your computer and use it in GitHub Desktop.
Save carolineschnapp/1083007 to your computer and use it in GitHub Desktop.
<script>
// (c) Copyright 2016 Caroline Schnapp. All Rights Reserved. Contact: mllegeorgesand@gmail.com
// See https://docs.shopify.com/themes/customization/navigation/link-product-options-in-menus
var Shopify = Shopify || {};
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 %}
</script>
@chefmungus
Copy link

chefmungus commented Jan 22, 2019

@eddyparkinson I implemented both these fixes as well as removing defer="defer" from the vendor.js load and still nothing doing. Preview link below, any help would be much appreciated.

https://8hk4yjvzez82ip57-1011974210.shopifypreview.com - Navigate to the Hats product in the catalog for the best example.

Uncaught TypeError: Shopify.uniq is not a function

Fix 1

From https://ecommerce.shopify.com/c/ecommerce-design/t/how-to-hide-irrelevant-variant-in-debut-theme-385309

var Shopify = Shopify || {};
  
  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 = {};

Fix 2

@codinginfotech says

use "jQuery.unique" in the place of "Shopify.uniq"

@impactcolor
Copy link

I'm getting Cannot read property 'length' of undefined on the loop that goes on
availableOptions = Shopify.optionsMap[key];
any idea how to solve this?

@VickyFi
Copy link

VickyFi commented Jul 4, 2019

Hello, I get this error "availableOptions is undefined". Do you have any suggestions for that?

@klongdesigns
Copy link

Hello, I get this error "availableOptions is undefined". Do you have any suggestions for that?

In my case, the issue was in the selector calls (lines 14, 18, 22, 23, 82 and 88). It originally was:

jQuery(".single-option-selector:eq

But when I looked at my code, the actual selector was missing the "single-" part, so the updated code ended up being:

jQuery(".option-selector:eq

&TLDR;

In the linked-options.liquid file, find and replace all instances of ".single-option-selector" with ".option-selector"

@muneeb434
Copy link

muneeb434 commented Oct 8, 2019

I have also applied the same script but its not working. I am using 'FLEX' theme by Out of Sandbox.

Actually, I have three option as below:

  1. Color
  2. Contrast Color
  3. Size

linked-option script only hide the color option variants its not hiding the size and contrast color variants.


Code:

`<script>
// (c) Copyright 2016 Caroline Schnapp. All Rights Reserved. Contact: mllegeorgesand@gmail.com
// See https://docs.shopify.com/themes/customization/navigation/link-product-options-in-menus

// Modified by Jonathan Moore (Style Hatch) https://github.com/jonathanmoore
/*
Updated to work with sectioned themes
- Added required methods from the deprecated options_selection.js
- Triggers an initial variant change
- Hides sold out variants with only one option
*/
window.addEventListener('DOMContentLoaded', function() {
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__radio:eq(0)');
    break;
  case 1:
    var key = jQuery('.single-option-selector__radio:eq(0)').val();
    var selector = jQuery('.single-option-selector__radio:eq(1)');
    break;
  case 2:
    var key = jQuery('.single-option-selector__radio:eq(0)').val();  
    key += ' / ' + jQuery('.single-option-selector__radio:eq(1)').val();
    var selector = jQuery('.single-option-selector__radio: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').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__radio: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__radio: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() {

    observer.disconnect();
  });  
  observer.observe($addToCartForm[0], config);
}

{% endif %}
Shopify.linkOptionSelectors({{ product | json }});

var selector = jQuery('.single-option-selector__radio:eq(0)');
selector.trigger('change');
{% if product.options.size == 1 %}
{% for variant in product.variants %}
{% unless variant.available %}
jQuery('.single-option-selector__radio option').filter(function() { return jQuery(this).text().trim() === {{ variant.title | json }}; }).remove();
{% endunless %}
{% endfor %}
jQuery('.single-option-selector__radio').trigger('change');
{% endif %}
});
</script>`

Any one have any idea how to fix this?

@Drugsbio
Copy link

Drugsbio commented Dec 9, 2019

@muneeb434 Hello dude, your code is working for Brooklyn theme, thanks. But what if i want use "button" type, not "drop-down"? May be you know what to change in this code?

@murthy-softscripts
Copy link

Excellent one.. trying hard for whole day.. and i found it at last

@samineb
Copy link

samineb commented Feb 5, 2020

Was anyone able to implement this feature to the quickview cart option?

@williamgrivera
Copy link

williamgrivera commented Feb 10, 2020

The code if you want to disable variants instead of hiding them. It also appends 'Sold Out'. Should look like this https://tppr.me/jjhlY

<script>
// (c) Copyright 2016 Caroline Schnapp. All Rights Reserved. Contact: mllegeorgesand@gmail.com
// See https://docs.shopify.com/themes/customization/navigation/link-product-options-in-menus

var Shopify = Shopify || {};

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();
  
  var availableOptions = Shopify.optionsMap[key];
  selector.find('option').each(function () {
    var $this = jQuery(this);
    var _value = $this.val();
    var html = $this.html();
    if (jQuery.inArray(_value, availableOptions) !== -1) {
      $this.removeAttr('disabled', 'disabled').html(_value);
    } else {
      $this.removeAttr('checked').attr('disabled', 'disabled').html( _value + " - Sold Out" );
      selector.find('option:not(:disabled)').first().click();
    }
  });
  
  if (selector.find(':checked').length < 1) {
    selector.find('option:not(:disabled)').first().click();
  }

  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 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 %}
  
</script>

@samineb
Copy link

samineb commented Feb 12, 2020

Is it possible to have this work for select elements instead of radio buttons?

@williamgrivera
Copy link

It works for select elements.

@cfxd
Copy link

cfxd commented May 31, 2020

@willimagrivera has a good idea but I had to make some corrections to get that code to work.

Here's what works for me:

First, make sure when you call the product form you add product JSON, here's how I placed that in the data-product-json attribute:

{%- assign product_data_json = product | json -%}
{% form 'product', product, novalidate: 'novalidate', data-product-json: product_data_json %}

Then in my utilities.js I just do this:

var Shopify = Shopify || {};

Shopify.optionsMap = {};

Shopify.arrayIncludes = function (t, e) {
  for (var o = 0; o < t.length; o++) {
    if (t[o] == e) return !0;
  }
  return !1;
};

Shopify.uniq = function (t) {
  for (var e = [], o = 0; o < t.length; o++) {
    Shopify.arrayIncludes(e, t[o]) || e.push(t[o]);
  }
  return e;
};

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);
  // }
  selector.find('option').each(function () {
    var $this = jQuery(this);
    var _value = $this.val();
    if (jQuery.inArray(_value, availableOptions) !== -1) {
      $this.removeAttr('disabled', 'disabled').html(_value);
    } else {
      $this.removeAttr('checked').attr('disabled', 'disabled');
      selector.find('option:not(:disabled)').first().click();
    }
  });
  
  if (selector.find(':checked').length < 1) {
    selector.find('option:not(:disabled)').first().click();
  }

  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;
  });  
};
 
var $addToCartForm = $('form[action="/cart/add"]');
var product        = $addToCartForm.length > 0 ? JSON.parse($addToCartForm.attr('data-product-json')) : false;

if (product && product.available && product.options.length > 1) {
  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);
      observer.disconnect();
    });  
    observer.observe($addToCartForm[0], config);
  }

  Shopify.linkOptionSelectors(product);
}

@uumoss
Copy link

uumoss commented Aug 11, 2020

I've tried trouble shooting using the comments on here to either grey out or even just show out of stock options while also having the linked options work, but I haven't had any luck. I can get one to work or the other, but never at the same time.

I am working with the Out of the Sandbox theme Mobilia. Has anyone been able to make it work with that theme?

@sebfie
Copy link

sebfie commented Dec 4, 2020

I changed also the way to select form from :

$('form[action="/cart/add"]')

to

$('form[action*="/cart/add"]')

Because when you are on a locale (/en), it does not match, and it should !

@murthy-softscripts
Copy link

murthy-softscripts commented Dec 6, 2020 via email

@sanjay-makwana-avidbrio
Copy link

sanjay-makwana-avidbrio commented Jan 27, 2021

@ALL

I found the solution and working for me on 27-Jan-2021 with Shopify minimal theme [open here ] (https://belvg.com/blog/shopify-variants.html)

@robhuska
Copy link

robhuska commented Mar 17, 2021

+1 for @Sanjayavidbrio's solution found here: https://belvg.com/blog/shopify-variants.html#step_5! However, if you want to still show products that you do carry, but are out of stock, this will not work out of the box.

My goal was to remove variant combonations that we just do not carry, but if we have a variant that is Out of Stock, we still want to show that. Below is my linked-options.liquid snippet file.

<script>
window.addEventListener('DOMContentLoaded', function() {
  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];

        // 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'); 

});
</script>

The key differences are to remove the if (variant.available) { } located at line 61 (and the closing } on line 80). Then I remove the entire chunk from line 117-124 that deals with products with only one option size.

Hope this help someone else!

@sanjay-makwana-avidbrio
Copy link

sanjay-makwana-avidbrio commented Apr 5, 2021

@robhuska thanks for your solution that was does not work for me.

i want use radio button( variants swatches ) with linked option

the linked option when u change first option that time second option drop-down selected on box but add to cart still Unavailable . select second option add to cart work

image

@aman-developer
Copy link

First you need to convert dropdown into swatches.

There is already tutorial for it.

So go and implement that first and then try to understand what bug or code you need to fix in linked option to make it work.

I explained in my screenshot.

@sanjay-makwana-avidbrio
Copy link

sanjay-makwana-avidbrio commented Apr 5, 2021

@aman-developer thanks for your support.

image

the linked option and variant selection not working

here is my code ` <script>
window.addEventListener('DOMContentLoaded', function() {
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');
      }
    });
    */
    
    $('.variant-swatches :radio').change(function() { {

      var optionIndex = $(this).closest('.variant-swatches').attr('data-option-index');

       jQuery(".variant-swatches[data-option-index='"+optionIndex+"'] .swatch-element").removeClass("active");

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

      jQuery(" .variant-swatches[data-option-index='"+optionIndex+ "'] .swatch-element[data-value='"+optionValue+ "' ] ").addClass("active");

      }})
      /* */
    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>`

@sanjay-makwana-avidbrio
Copy link

@aman-developer can you please post your solution it not working for me

@YahyaYavuz61
Copy link

Hello experts,

this snippet doesnt works for Prestige OS2.0 Theme.
Have anyone a snippet that works for this theme?

Please help me :)

@sanjay-makwana-avidbrio
Copy link

@aman-developer
Copy link

aman-developer commented Jan 17, 2022

Hello @YahyaYavuz61

This tutorial does not work directly for Prestige theme. Not for old version of Prestige theme too.

You will have to update product-form.liquid file first. You will have to call product options in dropdown and have to keep them hide. You have to update Classes assign to Swatches.
Then You will have to update this snippet and then it will work. There are numerous steps to implement linked-options for Prestige theme in shopify.

@YahyaYavuz61
Copy link

@sanjay-makwana-avidbrio which variables i must change? Can you explain please?

@YahyaYavuz61
Copy link

Hello @aman-developer thank you!

Can you do that for me?
if it's chargeable, I can also pay you.

@webmgrscandia
Copy link

Any update on this for Dawn 3.0?

@aman-developer
Copy link

I recently did it for Dawn 3.0 and 4.0 both.

@webmgrscandia
Copy link

I recently did it for Dawn 3.0 and 4.0 both.

Dude, could you help me?

@Tremand89
Copy link

Tremand89 commented Apr 21, 2022

The code sort of works.
It works in cases when there are 10-15 variants with 3 variant options, but when the varaint quantity increases to 30+ it does not work properly.
Is there a way to solve this?

Edit:
With 2options it works perfectly, but when 3rd option is added the functionality breaks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment