Shopify provides a Javascript option selection library to build multiple dropdown boxes for a given product based on the options and variants. However, this product library only generates dropdown boxes and many clients wish to have a more styled dropdown or a totally different method (like a list of swatches).
This library allows you to pass in a template builder, such as Handlebars, to use for the selection generation. This enables you to style or format your markup just as you see fit and let the library handle returning the selected variant. It is written in pure Javascript.
The following steps are simply guidelines. Your process may differ if you use Gulp, Grunt, or others in your standard workflow.
npm i shopify-option-selectors-custom
Now you can do:
import OptionSelectorsCustom from 'shopify-option-selectors-custom';
or
const OptionSelectorsCustom = require('shopify-option-selectors-custom');
Note: Ensure to install Handlebars library as well.
Download dist/option-selectors-custom.min.js
in this repository and upload it to your assets
folder.
Optionally, you can download and include extras/handlebars-helpers.js
which includes some helper methods for your templates.
Lastly, be sure to grab a copy of Handlebars or similar, and add it to your assets
folder.
{% if template == 'product' %}
{{ 'handlebars.js' | asset_url | script_tag }}
{{ 'option_selection.js' | shopify_asset_url | script_tag }}
{{ 'option-selectors-custom.min.js' | asset_url | script_tag }}
{% endif %}
Very basic outline of how to use it, you're free to use any other implementation.
Ensure you have Liquid generating a dropdown box of all variants, example:
<select id="product-select" name="id">
{% for variant in product.variants %}
<option value="{{ variant.id }}">
{{ variant.title }} - {{ variant.price | money }}
</option>
{% endfor %}
</select>
At the bottom of the file (or in a dedicated JS file):
{% include 'option-selector-template' %}
<script>
jQuery(document).ready(function($) {
var variantCallback = function(variant, selector) {
// The variant selected by customer, you can do what you wish with the object
// Update the price, change the images, etc
console.log(variant);
};
new Shopify.OptionSelectorsCustom({
// The select box ID to target
element: '#product-select',
// The product's JSON
product: {{ product | json }},
// Turn on history event (updates history with variant selected and updates URL)
enableHistory: true,
// The class to set active when a customer clicks an item
selectedClass: 'active',
// The callback to fire when a selection is made
// Values passed:
// {Object} variant The variant object
// {Object} e The mouse event which fired the click
callback: variantCallback,
// The template's HTML (Handlebars example)
template: Handlebars.compile($('#option_selector_template').html()),
// The handler for building each selector (template specific, Handlebars example)
templateBuilder: function () {
// `this` is scoped to OptionSelectorsCustom
// Feel free to add in any data you need for your templates
return this.template({
option_id: this.id,
option_name: this.name,
option_values: this.values,
product_id: this.product.id,
});
}
});
});
</script>
Please note, for the object you pass into Shopify.OptionSelectorsCustom
, all parameters are required besides enableHistory
.
This is the template which will be repeated for every option. You have full control of the markup with a small list of requirements:
- Ensure you use
{% raw %}
around the template so the template builder (in this case, Handlebars) and it's variables will not be parsed by Liquid id="selector-{{product_id}}-{{option_id}}"
is required on your parent container for the options[data-options]
container is required, with all direct children being options.- Options under each child (example
span.option
below), must havedata-value="{{this}}"
where the value is equal to the title for the option.
{% raw %}
<script id="option-selector-template" type="text/x-handlebars-template">
<div id="selector-{{product_id}}-{{option_id}}" class="row selector" data-selectbox-name="{{option_name}}">
<div class="label">{{option_name}}</div>
<div class="options">
{{#each option_values}}
<span data-value="{{this}}" class="option"><span class="option-text">{{this}}</span></span>
{{/each}}
</div>
</div>
</script>
{% endraw %}