Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Linked options helper methods for Shopify. See this: http://docs.shopify.com/manual/configuration/store-customization/advanced-navigation/linked-product-options - Updated to work with sectioned themes (tested with District)
<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: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>
@akenger

This comment has been minimized.

Copy link

@akenger akenger commented Feb 26, 2017

Hey Jonathan,

I'm trying to use this and for the life of me can't figure out why it seems like jQuery is not available when this loads. I get "Can't find variable $" on this line var $addToCartForm = $('form[action="/cart/add"]');

Any ideas? I imagine maybe it's a race condition of some kind b/c if I put a debugger on it, I can run that selector.

I'm using the debut theme...

@JordanNZ

This comment has been minimized.

Copy link

@JordanNZ JordanNZ commented Mar 30, 2017

Is there anyway that we can have it load up the default variants value with out having to select another to fire the script?

@AnnaBanana124

This comment has been minimized.

Copy link

@AnnaBanana124 AnnaBanana124 commented May 10, 2017

Thank you, this amazingly worked for me! However, when I switch options, the default variant is automatically listed as unavailable but will show as available if i click another variant and then click back to the default. I can live with this but I figured I'd let you know how it's working for me. I am using the Colors theme. Thanks again!!

@HandHugs

This comment has been minimized.

Copy link

@HandHugs HandHugs commented May 18, 2017

Im getting an error:
Uncaught TypeError: Cannot read property 'length' of undefined
at Object.Shopify.updateOptionsInSelector
at Object.Shopify.linkOptionSelectors
at MutationObserver.

They seem to have to do with the section of code:
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); }

Any ideas on how I can get this to work?


@hnashenda

This comment has been minimized.

Copy link

@hnashenda hnashenda commented May 27, 2017

@HandHugs... that problem is related to your dropdown selection class. It is probably different from that in the code.
Solution : replace all instances of "single-option-selector__radio" with the class from your select dropdown linked list.
eg:
my class for the select drop down list is "single-option-selector__radio"
THus
var selector = jQuery('.single-option-selector:eq(0)');
I changed to
var selector = jQuery('.single-option-selector__radio:eq(0)');

@ybyalik

This comment has been minimized.

Copy link

@ybyalik ybyalik commented Jul 21, 2017

Hi, maybe someone can help. This works for me but only after I refresh the product page. I'm using Shopify Turbo theme from OOTS
Any ideas?

@namurray

This comment has been minimized.

Copy link

@namurray namurray commented Jul 25, 2017

@jonathanmoore thanks for this, i made one change, i removed .removeAttr('checked') from line 55 because, in this one theme built off Debut that I've looked at anyway, after initial page load I had to check another available variant twice to apply the checked status (which has a style associated to it). This change seems to have taken care of it, can you foresee any issues?

one problem i am still having (might be related to @AnnaBanana124 's): On first load of an available variant (eg Color Pink / Size 2), an option that is unavailable for another (eg no size 4 for Black, but there is a 4 for Pink) is hidden (size 4). If I click a new color this is resolved but obviously it's an issue for first page load.

I'll work on it but maybe you'll find a solution before me.

@maxg23

This comment has been minimized.

Copy link

@maxg23 maxg23 commented Aug 14, 2017

im trying to run this script with the Envy Theme, doesnt seem to work. where do i paste the {% include 'linked-options' %} . Help please!

@ajensen27

This comment has been minimized.

Copy link

@ajensen27 ajensen27 commented Aug 22, 2017

What JordanNZ said: Is there anyway that we can have it load up the default variants value with out having to select another to fire the script?

Is there?

@tracynicholas

This comment has been minimized.

Copy link

@tracynicholas tracynicholas commented Aug 23, 2017

@jonathanmoore This is fantastic, thank you so much!
Is there any way I can add some code in that still shows the unavailable options, but has them greyed out?

Thanks, Tracy

@AlejandroRomeo

This comment has been minimized.

Copy link

@AlejandroRomeo AlejandroRomeo commented Oct 1, 2017

Thank you!!!!!!!!!
Solved my problem

@AlejandroRomeo

This comment has been minimized.

Copy link

@AlejandroRomeo AlejandroRomeo commented Nov 3, 2017

Hi @jonathanmoore, I encountered a bug after further developing a website.

The scripts works just fine when using "dropdown menus" on product page. However, when implementing "product swatch", an error occurs. The available variants show up fine for every click on the first variable selector, but when clicking to choose on the 2nd variable for some options, the selection doesn't apply.
screenshot_1
Did any one encounter this same problem? I'm using Showtime Theme (sectioned theme)
I did manage to figure out that the ocurrence of this bug depends on the order of the variants on the product page.
For example:
image
I replicated the error on this test store if anyone wants to help me, it would be much appreciated.
url: https://livewellargentina.myshopify.com/products/copy-of-100-whey-protein?variant=4588430655520
pass: loowha
Thanks

@voskresla

This comment has been minimized.

Copy link

@voskresla voskresla commented Nov 22, 2017

Quickly change it for Brooklyn Theme. Test only for my shop.
gist

@jonathanmoore

This comment has been minimized.

Copy link
Owner Author

@jonathanmoore jonathanmoore commented Jan 10, 2018

Hey @AlejandroRomeo - This specific snippet was modified to ensure it works with our District premium Shopify theme. If you're working with another theme or a custom built one, you might want to try referencing Shopify's guide or working with a developer.

@tracynicholas

This comment has been minimized.

Copy link

@tracynicholas tracynicholas commented Jan 22, 2018

Hi @jonathanmoore, this function seems to have stopped working over the last couple of days (not sure exactly when). We haven't updated our website / code in any way.
Have there been any updates that you know of that occurred over the weekend?
Thanks in advance, Tracy

@rjpt-339

This comment has been minimized.

Copy link

@rjpt-339 rjpt-339 commented Mar 10, 2018

Hello Every One,
Tons of information there. @jonathanmoore Sir i was looking to implement this on Venture Free Theme. I tried the above code but not working. But i tried it on Supply Theme it worked.
Can any one help me setup Linked-Options over Venture Theme.
Regards
Arslan Akram

@hanihsdd

This comment has been minimized.

Copy link

@hanihsdd hanihsdd commented Mar 14, 2018

Hello,

Thanks for the script.

For those of you who are having issues with the default variant not loading and requiring the user to select a variant before the script fires:

This is what worked for me on the Shopify Debut Theme.

Find the following code:
Shopify.linkOptionSelectors({{ product | json }});

It is located in the following if statement

{% if product.available and product.options.size > 1 %}
...
Shopify.linkOptionSelectors({{ product | json }});
...
{% endif %}

Simply move it directly outside of the if statement:

{% if product.available and product.options.size > 1 %}
...
// Line removed from here
...
{% endif %}

// Line added here
Shopify.linkOptionSelectors({{ product | json }});
@elucan

This comment has been minimized.

Copy link

@elucan elucan commented Apr 3, 2018

Anyone been successful with getting this to work on the Symmetry theme?

@nutriprash

This comment has been minimized.

Copy link

@nutriprash nutriprash commented Sep 12, 2018

The code by Jonathan works perfectly on my Debut theme without any change in code.

@nutriprash

This comment has been minimized.

Copy link

@nutriprash nutriprash commented Sep 12, 2018

Thanks Hanihsdd & Jonathan, It worked for my Debut Theme

@thousandzero

This comment has been minimized.

Copy link

@thousandzero thousandzero commented Sep 18, 2018

Hey Please help me. Everything tried but not working for me.

  jQuery(function($) {
    {% if product.available and product.variants.size > 1 %}
    new Shopify.OptionSelectors('product-selectors', {
      product: {{ product | json }},
      onVariantSelected: selectCallback, 
      enableHistoryState: true
    });   

    {% comment %}
    Use color swatch and linked options (copyright by @carolineschnapp)
    {% endcomment %}
    {% if section.settings.use_color_swatch and product.available and product.options.size > 1 %}
    Shopify.linkOptionSelectors({{ product | json }});
    {% endif %} 

    {% if section.settings.use_color_swatch != true %}
    jQuery('.single-option-selector').selectize();
    jQuery('.selectize-input input').attr("disabled","disabled");
    {% endif %}

    {% endif %}
@nutriprash

This comment has been minimized.

Copy link

@nutriprash nutriprash commented Oct 1, 2018

Hello,
I made the changes in Jonathans code recomended by hanihsdd (without first checking how Jonathans code works), in my debut theme, which caused most of the products to show as "out of stock". On reversing Hanihsdd's code change, my problem got resolved. So you don't need to make any changes in Jonathans code.

Thanks again

@baisho

This comment has been minimized.

Copy link

@baisho baisho commented Dec 5, 2018

Hey, I tried to use @jonathanmoore's solution in Debut theme, and the functionality works perfectly, but unfortunately the loading time has been increased big time. So big that a lot of times the page just crashes. Before applying Jonathan's code it hadn't happen. Do you have any suggestions why it occurs and how to solve it? Thank you! :)

@baisho

This comment has been minimized.

Copy link

@baisho baisho commented Dec 7, 2018

Now it seems the page loads okay, it crashes significantly less then 2 days ago. BUT! Jonathan's code works only for the first loading of the page. If I go to another product it stops working. Do you have an idea why it can happen? I am using the Debut theme. Thank you for all of your answers!

@MohdQasim00

This comment has been minimized.

Copy link

@MohdQasim00 MohdQasim00 commented Feb 23, 2019

@baisho I am encountering the same issue which is the product page never load and crashes. have you successfully been able to solve that issue? Thanks.

@numzie

This comment has been minimized.

Copy link

@numzie numzie commented May 13, 2019

Script seems to work with my Venture theme.

Has anyone come up with a way to DISABLE/GREY OUT unavailable/sold out items instead of removing them?

@IliasHad

This comment has been minimized.

Copy link

@IliasHad IliasHad commented Sep 24, 2019

If it's doesn't work, you can try to add this
Find the following code:
Shopify.linkOptionSelectors({{ product | json }});

It is located in the following if statement

{% if product.available and product.options.size > 1 %}
...
Shopify.linkOptionSelectors({{ product | json }});
...
{% endif %}

And add this this after var Shopify = Shopify || {};

Array.from(document.querySelectorAll(".single-option-selector")).forEach(el => el.addEventListner("change", () => Shopify.linkOptionSelectors({{ product | json }})))

@numzie

This comment has been minimized.

Copy link

@numzie numzie commented Sep 24, 2019

If it's doesn't work, you can try to add this

I assume this is a general solution to try to get it work, right?

This was not a solution to disable/grey out the unavailable/sold out items instead of removing them, is that correctly understood?

@IliasHad

This comment has been minimized.

Copy link

@IliasHad IliasHad commented Sep 24, 2019

This was not a solution to disable/grey out the unavailable/sold out items instead of removing them, is that correctly understood?

Yes, this is a general solution to get it to work on some Shopify themes

@jonathanmoore

This comment has been minimized.

Copy link
Owner Author

@jonathanmoore jonathanmoore commented Sep 25, 2019

@numzie With this approach, nothing is removed we were just adding CSS classes and HTML attributes to style the disabled or sold out options differently.

@numzie

This comment has been minimized.

Copy link

@numzie numzie commented Sep 26, 2019

@jonathanmoore

Yes, sorry.

I were just looking for something that would in stead disable or grey it out.

Another thing: it actually acts a bit weird on my shop when a product work variants is sold out and still online on store (all options are removed but the drop-down is still there):

https://www.authentic.dk/collections/vejrhoj/products/vejrhoj-nora-m-valgfri-rem?variant=13842384650292

I don't know if anything can be done to this and if we can disable/grey out stead (which would actually solve the empty looking drop-down I guess)

@Drugsbio

This comment has been minimized.

Copy link

@Drugsbio Drugsbio commented Dec 5, 2019

This variantes aren't work(( i have brooklin theme. And my variantes looks like that. Can you help pls?) Need the same as for you, guys - hide unavalible variantes
example

Ok, this code work but only with drop-done style(( what about button variantes style?))

@Drugsbio

This comment has been minimized.

Copy link

@Drugsbio Drugsbio commented Dec 6, 2019

@jonathanmoore ,Excuse me, but can you help me a little bit?)) upper comment

@jonathanmoore

This comment has been minimized.

Copy link
Owner Author

@jonathanmoore jonathanmoore commented Dec 6, 2019

@Drugsbio - This isn't anything that I can help support on other themes. The intention of publishing this was to provide a solution for our premium theme District for shops using older versions of our theme. Now the current versions of our themes have this feature directly built-in.

Since this, you're trying to work with Shopify's Brooklyn theme you could try reaching out to Shopify support. However, that guide from Shopify specifically says it does not work with Brooklyn.
image

@Drugsbio

This comment has been minimized.

Copy link

@Drugsbio Drugsbio commented Dec 7, 2019

@jonathanmoore i didnt understand that you code was only for District, excuse me))
But you code is working on Brooklyn theme! but only for drop-down type(

in this picture says only about "non-sectioned versions of Brooklyn. I have section version.

Shopify support even didn't want do anything. They just bla-bla-bla but they can not help.
I think if your code is working for brooklyn theme "drop-down" type probably you may a little bit change and it will be work with "button" type.
One more time sorry)

@numzie

This comment has been minimized.

Copy link

@numzie numzie commented Feb 3, 2020

I'm using an older Venture theme and the above code from Jonathan works as intended.

However, I would really like to just disable (but still show) sold-out variants, but I can't figure out how to do that.

@tracy522

This comment has been minimized.

Copy link

@tracy522 tracy522 commented Feb 28, 2020

I'm hoping someone here is better with javascript than I am! This code works well to hide unavailable options, but the selector doesn't move with the inputs. So, when a button gets hidden the selector for the following button will be offset by one causing it to show no change when selected (but the hidden button would show the highlight if it were visible). The button seems to be operating as it should- selecting the appropriate product/image but the highlight is off. I've tried everything I can think of to address the issue. If I use .remove() it fixes the highlight, but I don't know how to add the removed items back in when they should reappear.

@DanielGGordon

This comment has been minimized.

Copy link

@DanielGGordon DanielGGordon commented Jun 4, 2020

My issue with this script is that it also hides products that are sold out, not just unavailable. This would be especially helpful when you have items that are out of stock but will back in stock soon. Customers can select the sold out product and sign up for alerts on that product. Removing them completely from the list and not distinguishing 'SOLD OUT' from 'UNAVAILABLE' should not be the default behavior. They are two different things. UNAVAILABLE is when the item is simply never carried. There isn't even a SKU for it. Out of stock/sold out is completely different and I really don't know why you would want to hide sold out products.

@jonathanmoore

@maumonteroj

This comment has been minimized.

Copy link

@maumonteroj maumonteroj commented Jun 5, 2020

Hi everyone first @jonathanmoore thanks for this solution.

I need to make the change of Hanihsdd to made works the code, but I get a issue sometimes.

The issue that I have is that this solution works for me using the Debut theme only sometimes.

The issue is that sometimes when I select the value A in my single-option-selector and I select the value X in my other single-option-selector all works as expected, but if I change the first single-option-selector to B and B don't have X so it changes the value to Y and I lost the price and my buy button change to unavailable

@kishorig

This comment has been minimized.

Copy link

@kishorig kishorig commented Jun 8, 2020

Thank you, this amazingly worked for me! However, when I switch options, the default variant is automatically listed as unavailable but will show as available if i click another variant and then click back to the default.

@impactcolor

This comment has been minimized.

Copy link

@impactcolor impactcolor commented Jun 17, 2020

Developer has stated a couple of times that this code was made for a specific theme and he can't help with development. It would be best if we all understood that and attempted to post solutions instead of asking for blank help. Post your issue with detailed information and we can try to solve the issues. "It doesn't work" is not something can be debugged.

@pk841442

This comment has been minimized.

Copy link

@pk841442 pk841442 commented Jul 2, 2020

Hey @AlejandroRomeo - This specific snippet was modified to ensure it works with our District premium Shopify theme. If you're working with another theme or a custom built one, you might want to try referencing Shopify's guide or working with a developer.

what changes i do for disable or grey out of out of stock variants

@williambsb

This comment has been minimized.

Copy link

@williambsb williambsb commented Sep 2, 2020

Hello,

Thanks for the script.

For those of you who are having issues with the default variant not loading and requiring the user to select a variant before the script fires:

This is what worked for me on the Shopify Debut Theme.

Find the following code:
Shopify.linkOptionSelectors({{ product | json }});

It is located in the following if statement

{% if product.available and product.options.size > 1 %}
...
Shopify.linkOptionSelectors({{ product | json }});
...
{% endif %}

Simply move it directly outside of the if statement:

{% if product.available and product.options.size > 1 %}
...
// Line removed from here
...
{% endif %}

// Line added here
Shopify.linkOptionSelectors({{ product | json }});

Keep working on Debut ... Thanks

@PhilipAV

This comment has been minimized.

Copy link

@PhilipAV PhilipAV commented Oct 30, 2020

None of this is working for me. Is it because I have 3 variants on my product page? Is there any available solution to this problem when 3 variants are involved?

@impactcolor

This comment has been minimized.

Copy link

@impactcolor impactcolor commented Oct 30, 2020

@PhilipAV "None of this is working for me" isn't saying much. What doesn't work? Are you getting an error message? What theme are you using?

@PhilipAV

This comment has been minimized.

Copy link

@PhilipAV PhilipAV commented Oct 30, 2020

Hi, I'm not getting an error and I'm using debut.

@impactcolor

This comment has been minimized.

Copy link

@impactcolor impactcolor commented Oct 30, 2020

With out any details it's difficult to help @PhilipAV

@PhilipAV

This comment has been minimized.

Copy link

@PhilipAV PhilipAV commented Oct 30, 2020

@impactcolor I understand, I've tried many variations of the code provided in this thread and in other threads. My product page has 3 variations but only particular colors are associated with a particular size, but the unavailable colors still remain visible once a size is selected. None of these solutions from these threads has changed anything at all on my site - and I receive no error or any message at all. I'm also using the Debut theme. If there's any advice you or anyone else could provide it would be very appreciated. As far as my original question, do you know if the solutions in this thread work with pages that have 3 variations.

@impactcolor

This comment has been minimized.

Copy link

@impactcolor impactcolor commented Oct 30, 2020

@PhilipAV to the question the answer is yes. I'm using 3 variations on a responsive theme by out of the sandbox and it works.
This solution isn't meant to be an out of the box solution. The author stated that it was made for the District theme. You will have to break it down to see where the issue is arising from. There can be many things that are causing it not to work.

@impactcolor

This comment has been minimized.

Copy link

@impactcolor impactcolor commented Oct 30, 2020

@PhilipAV one way to debug it would be to add some console.logs to the code to see what's being initialized. In example if Shopify.updateOptionsInSelector = function(selectorIndex) { console.log('updateOptions is initizlized'); ....
Then you'll be able to pin point where the issue is arising from. If I remember correctly on the theme I was using it wasn't picking up the options.map but I can't really remember what it was.

@PhilipAV

This comment has been minimized.

Copy link

@PhilipAV PhilipAV commented Oct 30, 2020

@impactcolor Thanks for this, I'll give them a try over the weekend.

@pigmug

This comment has been minimized.

Copy link

@pigmug pigmug commented Nov 26, 2020

My shopify site is a bilingual site (using Langify). I've included your code in my site. The English version works OK. However, the other language (Chinese) works as if there is no script included. Would like to know how to handle bilingual case. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.