The designer guide to product customization in Shopify
From Shopify Wiki
Level: Easy
Are you a shop-owner? If so, you probably should look at The shop-owner guide to product customization in Shopify instead. Same JavaScript library (Customizr), but I don't commit the sin of dishing out “too much information” over there. Also, that other article contains a tutorial. This one doesn't.
What's the Customizr?
The Customizr is a module pattern based object namespaced Shopify.Customizr. It was built by Caroline Schnapp to fulfill this mission:
Allow you to drop any additional form fields in your add to cart forms and witness those extra product attributes magically carried through checkout as part of the order. Snazzy, n'est-ce-pas? It's also 727 lines of JavaScript code with the comments. 16Kb minified.
Customizr uses the old tried and true method of saving product attributes in a cookie until checkout — yes, Hunkybill, I use only one cookie with a JSON object inside of it, in 99% of cases there's plenty of room in there to fit in an order's attributes ☺. At checkout, the cookie is read and its data is submitted as cart attributes.
The hardest is not so much to accomplish the above, although it took me many days to figure all of this out in 2009.
No, the hardest parts are:
- scanning the add to cart form fields intelligently and ignore whatever form fields are in there by default. No need to use a particular name or class or id on your additional form fields. Note that file upload fields are ignored because you cannot store a file in a cookie nor submit one to Shopify's back-end with cart attributes. Use transloadit for your file upload needs — or wufoo, or jotform — and please embed your file upload form on your Thank you page, it really belongs there. (Strictly-speaking, the default form fields are far from being ignored as Customizr needs to know how many items are added, and which variant ID is submitted as well, to establish the connection between carted items and the customization applied to them.)
- validating the form field intelligently: rely on the browser when it supports the HTML5 Form API, and validates on its own otherwise.
- being compatible with themes that use Ajax to add items to the cart, and not every theme works the same for that. Some attach an onclick event-handler to the add to cart button, some attach an onsubmit handler to the form itself. Some add handlers right in the HTML, some use unobtrusive JavaScript.
- scanning the cart form on the cart page and re-build it while using the HTML and CSS (and JavaScript) already in use in it. That was the toughest part for me. Instead of building my own table with my own markup, or instead of relying on a jQuery template — more work for you — I use what's there already. I clone lines, hide old ones. Whether the cart form is a table or an unordered list, I make do. I look for money amounts (unit price and line item totals), I look for line item titles, I re-calculate, append text, remove handlers add new ones, clone qty inputs and attach handlers to those, and re-massaged thoroughly to present the new “line items” with the same look / feel / behavior as present on the original “un-tampered with” page. Then I test and test again. With one theme then another. Then I add multiple currencies support into the mix, refactor and test again. I have never done more DOM scripting and event handlers twinkerinng in my whole life.
The Customizr accomplishes all of the above and more.
Although the Customizr is very smart on its own, it does let you control it with some optional configuration parameters. If anything should go wrong, it also comes with a debugger that lets you inspect a pretty-formatted version of your attributes cookie.
The Customizr uses 2 plugins:
- json2.js by the awesome Douglas Crockford
- the old but tried and true cookie plugin by Klaus Hartl
The above plugins are minified inside jquery.customzr.min.js at the top with headers preserved.
Customizr should not be used to “save up” on SKU usage but only to circumvent Shopify's limits. See here for a run-down on when it makes sense to use Customizr: The shop-owner guide to product customization in Shopify .
It would be sad to use Customizr in lieu of getting more SKUs in one's plan. A Shopify variant comes with a price, compare at price and inventory. If your client comes to you to use Customizr because he wants to stay on the Basic plan (with less SKUs), send him off to sales@shopify.com where our sales people will accomodate his or her special needs at a reasonable additional cost. It's possible that one shall need more SKUs if, say, one sells T-shirts that come in many sizes and many colors, yet not be the kind of business able to afford the Business plan right off the bat. Common sense prevails here. Contact a sales representative. We do put together custom plans. This is not a sales pitch. This is not sales speaking. This is a designer speaking. --Caroline Schnapp 17:09, 26 August 2011 (UTC)
Contents |
Download Customizr
Time to upload jquery.customizr.min.js to your theme assets.
Go to this github page and download the carolineschnapp/customizr package.
Unzip the carolineschnapp/customizr package.
In your shop admin, go to Themes → Template Editor. On the Template Editor page, in the left-hand side pane, scroll down to the Theme Assets section and upload the file jquery.customizr.min.js by clicking on the Choose File button.
Link to the file in theme.liquid using this code;
{{ 'jquery.customizr.min.js' | asset_url | script_tag }}
For an easy install guide refer to The shop-owner guide to product customization in Shopify.
Methods and configuration
The Customizr library comes with two methods: attach and show. There are also 3 additional methods used only by the debugger, but we will get to those later.
The Shopify.Customizr.attach() method is to be used on the product page, collection page, index page — wherever you have add to cart forms with additional form fields in them. It is perfectly safe to call that method on every single page of the website. --Caroline Schnapp 04:44, 28 January 2012 (UTC)
The Shopify.Customizr.show() method is to be used on the cart page.
Shopify.Customizr.attach()
Here's how you use the attach() method:
Shopify.Customizr.attach();
You can call Shopify.Customizr.attach() with an optional configuration object like so:
Shopify.Customizr.attach( {
selector: null,
checkboxOnValue: 'yes',
requiredText: 'This field is required',
requiredSelect: 'Please select an option',
requiredCheckbox: 'This checkbox is required',
useNativeValidation: null
} );
Calling the method as done above is the same as doing this:
Shopify.Customizr.attach();
... as I have listed all the optional parameters set to their default values.
We do need to talk about each of those parameters.
selector
When set to null, the Customizr will scan the add to cart form and pick what makes sense. This is “auto-pilot” mode. Try it.
It may help you to see how Customizr scans your form:
If you would rather take over, specify a selector that jQuery can understand.
The selector can be quite complex. Or simple:
selector: '[name^="attributes"]',
checkboxOnValue
If your checkboxes don't have a specified value, then checkboxOnValue is what's used as value.
Here's how you could handle a checkbox:
<input id="with-blankie" type="checkbox" value="Yes" /> <label for="with-blankie">I want a blankie with this</label>
The above would result in this line item title if one buys an Awesome sofa:
Awesome sofa / I want a blankie with this: Yes.
If no value is specified in the HTML:
<input id="with-blankie" type="checkbox" /> <label for="with-blankie">I want a blankie with this</label>
... then that'd give you:
Awesome sofa / I want a blankie with this: Yes, madame.
That is, if you had used the following:
checkboxOnValue: 'Yes, madame',
Notes:
- If the checkbox is un-checked — and NOT required with a required attribute or required class — then it's not saved nor submitted to the back-end with the order.
- The default value is 'yes' for this configuration parameter. (To avoid you scrolling up and checking.)
requiredText
If a text field is required, here's what happens when the form is submitted and it is blank:
- If HTML5 validation is en vigueur, then the browser will give you some feedback auto-styled. (In Chrome that looks like a tooltip).
- If HTML5 validation is not supported, then Customizr will show the text requiredText next to that field. It will also wrap the field in a span with class 'error'.
Notes:
- If several form fields are in error, the focus is automatically placed on the first field in error when one submits the form. To help one fix one's mistakes.
- The default value is 'This field is required' for this configuration parameter.
requiredSelect
If a select element is required, and its value is an empty string, then validation kicks in and shows requiredSelect next to the dropdown. It will also wrap the field in a span with class 'error'.
Use an empty-value "Please make a selection" to force your customer to look at available options and make an informed decision. Or not.
<label for="favorite-meal">Favorite meal</label> <select id="favorite-meal"> <option value="">Pick your favorite meal</option> <option value="Breakfast or brunch">Breakfast or brunch</option> <option value="Lunch">Lunch</option> <option value="Souper">Souper</option> </select>
If the select is not required, then it's not submitted if it has an empty string value.
Note:
- If many form fields are in error, the focus is automatically placed on the first field when one submits the form. To help one fix his mistakes.
- The default value is 'Please select an option' for this configuration parameter.
requiredCheckbox
If a checkbox is required (for some “I agree with...” purpose), and it's unchecked upon submission, then validation kicks in and shows the requiredCheckbox text next to the checkbox. Customizr will also wrap the field in a span with class 'error'.
The default value is 'This checkbox is required' for this configuration parameter.
useNativeValidation
If set to null, you let Customizr decide if HTML5 validation can be used or not.
You can force Customizr to NOT use native validation by setting useNativeValidation to false.
If you set this value to true and if Customizr decides that native validation still can't be used in the context, it will wisely ignore your command.
The default value is null for this configuration parameter.
Shopify.Customizr.show()
Now, here's how you use the show() method:
{% if template == 'cart' %}
Shopify.Customizr.show({{ cart | json }}, {{ shop.money_format | json }}, '{{ shop.currency }}');
{% endif %}
You can call Shopify.Customizr.show() with an optional configuration object like so:
Shopify.Customizr.show({{ cart | json }}, {{ shop.money_format | json }}, '{{ shop.currency }}', {
showLabelOnCartPage: true,
labelFormatOnCartPage: '%s: ',
lineItemFormatInTemplatesPlainText: '%qx %t for %p each',
attributeSeparator: ' / '
});
Calling the method as done above is the same as doing this:
Shopify.Customizr.show({{ cart | json }}, {{ shop.money_format | json }}, '{{ shop.currency }}');
... as I have listed all the optional parameters set to their default values.
We do need to talk about each of those parameters.
showLabelOnCartPage
When set to false, the Customizr will not display any label in the new “line item” title. In a way, that's consistent with what Shopify does with option label and value for true line item titles.
Default value is true though. In too many cases, the label is needed, especially when dealing with text fields.
When set to true or not specified, the Customizr will display both label and value, and it will use the following format...
labelFormatOnCartPage
This is the formatting used when the label is shown alongside value. The %s placeholder is replaced with the label. The use of a placeholder here allows you to place something on either side of the label. Should you feel creative:
labelFormatOnCartPage: '<span class=\"attribute-label\" style=\"padding-right:5px\">%s</span> ',
The above would result in this line item title HTML if one buys an Awesome sofa:
Awesome sofa / <span class="attribute-label" style="padding-right:5px">Label</span> Value
The default value is '%s: ' for this configuration parameter.
lineItemFormatInTemplatesPlainText
lineItemFormatInTemplatesPlainText is a template. It is used to render the new “line item” information in the Order Confirmation email.
Default value is '%qx %t for %p each' for this parameter.
%q, %t and %p are placholders. They will get replaced with the quantity, full line item title and formatted price, respectively.
attributeSeparator
This is what is used to separate each label-value pair in the new “line item” title.
Default value is ' / '.
How does one mark a form field as required?
Use the HTML5 'required' attribute or use the class 'required' on your form field. It makes no difference which method of signaling Customizr you use. Go with what you prefer.
How does one specify the label?
The Customizr will look for a label in the following places and order:
1. It will look for the value of a data-label attribute set on your form field element. If it cannot find such attribute...
2. It will then look for a label element which for attribute matches the id attribute of your form field. The content of that label will be used. If the Customizr cannot find a label this way, then...
3. It will look at a name attribute set on your form field. It will proceed with the following transformation:
- If the name is attributes[last-name], the label will become Last name.
- If the name is last-name, the label will become Last name.
If there's no name attribute, then...
4. 'No label' will become the label.
The Debugger
The Customizr has a built-in debug function called Shopify.Customizr.inspectAttributes() which gives you — as returned value — the content of the cookie 'shopify_product_customization'.
The 'shopify_product_customization' cookie holds the custom attributes of your customized products. That cookie lives and dies along side Shopify's cart cookie. In other words, when the cart dies, the Customizr cookie dies also, ô so conveniently. The cart dies when an order is completed or after 14 days, whichever happens first.
The Shopify.Customizr.inspectAttributes() method also writes the content of the 'shopify_product_customization' cookie to any element on the page that has an id attribute set to attributes-wrapper. We can take advantage of the latter by adding a cute panel to our theme to examine at any time the content of our Customizr cookie.
To add the Customizr debug panel, create a snippet called 'debug-panel.liquid' and copy to it the content of this gist: https://gist.github.com/1273086.
Include your snippet anywhere in theme.liquid:
{% if template == 'cart' or template contains 'product' %}
{% include 'debug-panel' %}
{% endif %}
It doesn't matter where you include the panel because it will be positioned “absolutely” in the document relative to the top-right corner of your screen.
List of compatible themes and a few incompatible ones
The Customizr has been tested and works in the following themes:
It likely works in just any theme.
The Customizr has been tested and does not work in the following themes:
To make the Fresh theme compatible, go into cart.liquid, scroll down to line no 47, and replace this:
<td><a href="{{item.product.url}}">{{ item.product.title | escape }}{% unless item.variant.title contains "Default" %} – {{ item.variant.title | escape }}{% endunless %}</a></td>
With that:
<td><a href="{{item.product.url}}">{{ item.title }}</a></td>
To make the Responsive theme compatible, you must do 4 things — not for the faint at heart:
In product.liquid, replace {% include ‘product-form’ %} with the content of the product-form.liquid snippet. Do this before creating your alternate product template for your customizable product.
Also, make the checkout link in your website header point to your /cart page. Never go to /checkout directly without clicking on the checkout buttons in your cart form. If you go to /checkout directly, the cart attributes won't be submitted. Edit theme.liquid for this.
You will also need to remove the add to cart buttons from your Quick Shop modal boxes, because no custom attributes will be collected from that modal view. Keep the link that goes to the product page there, of course. Edit the snippet product-thumbnail.liquid for this.
Finally, remove buttons in cart.liquid most use the following HTML:
<a href="/cart/change?id={{ item.id }}&quantity=0" title="Remove Item">Remove</a>




