Skip to content

Instantly share code, notes, and snippets.

@kyleboehlen
Created December 22, 2021 19:36
Show Gist options
  • Save kyleboehlen/fd4ce2f4d24a16b74b1e4a30c914f32d to your computer and use it in GitHub Desktop.
Save kyleboehlen/fd4ce2f4d24a16b74b1e4a30c914f32d to your computer and use it in GitHub Desktop.
Shopify Hide Unavailable Variants
<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
*/
/* Modified by Mark and Kyle Boehlen Dec 2021 https://www.kyleboehlen.com
- Added variables, moved call to product.liquid.
- Added switch (variable) to show sold out
- Added secound for loop to add in sold out variants into the selectors at the end
- Added reference to jquery in product.liquid
*/
// Set up some variables that identify the selection, in this theme it is located in product-form.liquid line 98
var selectionobject = '.form__input--select' // '.single-option-selector'
var select0 = selectionobject + ':eq(0)'
var select1 = selectionobject + ':eq(1)'
var select2 = selectionobject + ':eq(2)'
// Set up a variable as a switch for showing sold out items
var showsoldout = true
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(select0);
break;
case 1:
var key = jQuery(select0).val();
var selector = jQuery(select1);
break;
case 2:
var key = jQuery(select0).val();
key += ' / ' + jQuery(select1).val();
var selector = jQuery(select2);
}
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.
// Start with available variants
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]);
}
}
}
// If we are showing the sold out items - then we will add them in a second loop so that the selectors get instock options first
if (showsoldout) {
for (var s=0; s<product.variants.length; s++) {
var variant = product.variants[s];
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]);
}
}
}
} // End if soldout
// 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(select0).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(select1).change(function() {
if (product.options.length === 3) Shopify.updateOptionsInSelector(2);
return true;
});
};
// added - making the script fire on page load
Shopify.linkOptionSelectors({{ product | json}});
{% 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(select0);
selector.trigger('change');
{% if product.options.size == 1 %}
{% for variant in product.variants %}
{% unless variant.available %}
jQuery(select0).filter(function() { return jQuery(this).text().trim() === {{ variant.title | json }}; }).remove();
{% endunless %}
{% endfor %}
jQuery(select0).trigger('change');
{% endif %}
});
</script>
@n3R0d0T
Copy link

n3R0d0T commented Jan 14, 2022

Hi,

How can I make this work in Venture theme? Thank you

@MrBillium
Copy link

Which theme was this tested on ? It is not mentioned in the comments and I have yet to find one with the hook you describe on line 21
Thx

@kyleboehlen
Copy link
Author

@MrBillium I don't remember, but that class can be changed to whatever the class is in your theme.

@MrBillium
Copy link

MrBillium commented Feb 7, 2022 via email

@webmgrscandia
Copy link

Any update on this? I'm using dawn theme 3.0

@FaizanAli96
Copy link

I am very new to this but i followed the instructions exactly as posted and it is still offering options and showing them as unavailable. I am using the Masonry theme if that matters. Thank you in advance for your assistance.

@FaizanAli96
Copy link

// linked-options.liquid file in my theme:

<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 */ /* Modified by Mark and Kyle Boehlen Dec 2021 https://www.kyleboehlen.com - Added variables, moved call to product.liquid. - Added switch (variable) to show sold out - Added secound for loop to add in sold out variants into the selectors at the end - Added reference to jquery in product.liquid */ // Set up some variables that identify the selection, in this theme it is located in product-form.liquid line 98 var selectionobject = '.form__input--select' // '.single-option-selector' var select0 = selectionobject + ':eq(0)' var select1 = selectionobject + ':eq(1)' var select2 = selectionobject + ':eq(2)' // Set up a variable as a switch for showing sold out items var showsoldout = true 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(select0); break; case 1: var key = jQuery(select0).val(); var selector = jQuery(select1); break; case 2: var key = jQuery(select0).val(); key += ' / ' + jQuery(select1).val(); var selector = jQuery(select2); } var initialValue = selector.val(); selector.empty(); var availableOptions = Shopify.optionsMap[key]; for (var i=0; i').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. // Start with available variants 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]); } } } // If we are showing the sold out items - then we will add them in a second loop so that the selectors get instock options first if (showsoldout) { for (var s=0; s 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]); } } } } // End if soldout // 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(select0).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(select1).change(function() { if (product.options.length === 3) Shopify.updateOptionsInSelector(2); return true; }); }; // added - making the script fire on page load Shopify.linkOptionSelectors({{ product | json}}); {% 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(select0); selector.trigger('change'); {% if product.options.size == 1 %} {% for variant in product.variants %} {% unless variant.available %} jQuery(select0).filter(function() { return jQuery(this).text().trim() === {{ variant.title | json }}; }).remove(); {% endunless %} {% endfor %} jQuery(select0).trigger('change'); {% endif %} }); </script>

@chadleap
Copy link

chadleap commented Apr 7, 2022

Caroline’s snippet won’t work with 2.0 themes and as far as I can tell, it hasn’t been solidly updated by anyone who has shared the code.

  • This may be the most promising attempt currently, for Dawn theme. However there isn’t much feedback and I haven’t tried it myself yet.
  • Here is a well-explained issue on the official Dawn repo, it’s worthwhile to follow it and add a comment or thumb-up adding your support as it's currently the 2nd most-commented issue of 500+ in the official repo, but don’t dump your shop code there, it’s not a place for personal code support. If this has enough support, maybe Shopify or some developer will solidly solve the issue for everyone. Most themes going forward will be based on Dawn so if there’s a solution on Dawn it’s likely to work on many themes.

@Johneltonsbrain
Copy link

Is this supposed to work with Debut theme?

@ramizibrahimovic
Copy link

{% render 'product-options-linked' %}
Hi all, sorry for the noob question :/
i have the flex theme that does not have product.liquid and would love to try out the snippet. How can that be implemented?
Thanks a lot!
Best, Ramiz

@Pancakes04
Copy link

Any way for this work on 'Ride' 2.0 theme? This theme doesn't have a product.liquid section which is the only reason as to why this isn't working.

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