Skip to content

Instantly share code, notes, and snippets.

@tobiasdalhof
Last active October 24, 2022 03:00
Show Gist options
  • Save tobiasdalhof/ca0484abc8c1e8cee26d8c375f1caef2 to your computer and use it in GitHub Desktop.
Save tobiasdalhof/ca0484abc8c1e8cee26d8c375f1caef2 to your computer and use it in GitHub Desktop.
Shopify variant listener
/**
* This class contains general helper methods.
*/
class Helper {
/**
* @param {String} name
* @returns {String|null}
*/
static getQueryParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
const regex = new RegExp('[\\?&]' + name + '=([^&#]*)')
const results = regex.exec(location.search)
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '))
}
}
/**
* This class contains Shopify related helper methods.
*/
class ShopifyHelper {
/**
* @returns {String|null}
*/
static getSelectedVariantId() {
// Try to get variant id from form element with name id
const addToCartForm = document.querySelector('form[action^="/cart/add"]')
if (addToCartForm) {
const input = addToCartForm.querySelector('[name="id"]')
const submitButton = addToCartForm.querySelector('[type="submit"]')
// If the submit button is disabled, the input value is most likely wrong
// because it got autofilled on initial page load with the first available product variant id.
if (
(!submitButton || submitButton.disabled !== true) &&
input &&
input.value
) {
return input.value
}
}
// Try to get variant id from "variant" query parameter.
const queryParameter = Helper.getQueryParameter('variant')
if (queryParameter) {
return queryParameter
}
// Try to get variant id from ShopifyAnalytics.meta object
if (
self.ShopifyAnalytics &&
self.ShopifyAnalytics.meta &&
self.ShopifyAnalytics.meta.selectedVariantId
) {
return self.ShopifyAnalytics.meta.selectedVariantId
}
// No variant id found
return null
}
/**
* @returns {Array<Element>}
*/
static getAddToCartFormElements() {
const foundElements = []
const elementTypes = ['select', 'input']
for (let elementType of elementTypes) {
let elements = document.querySelectorAll(`form[action^="/cart/add"] ${elementType}`)
for (let element of elements) {
foundElements.push(element)
}
}
return foundElements
}
/**
* @param {Function} eventHandler
*/
static addVariantListeners(eventHandler) {
for (let element of ShopifyHelper.getAddToCartFormElements()) {
element.addEventListener('change', eventHandler)
}
}
/**
* @param {Function} eventHandler
*/
static removeVariantListeners(eventHandler) {
for (let element of ShopifyHelper.getAddToCartFormElements()) {
element.removeEventListener('change', eventHandler)
}
}
}
const initApp = () => {
let selectedVariantId = ShopifyHelper.getSelectedVariantId()
console.log('selected variant id is', selectedVariantId)
const variantHandler = (event) => {
const variantId = ShopifyHelper.getSelectedVariantId()
if (selectedVariantId !== variantId) {
selectedVariantId = variantId
console.log('selected variant id changed to', selectedVariantId)
}
}
ShopifyHelper.addVariantListeners(variantHandler)
}
if (document.readyState !== 'loading') {
setTimeout(initApp, 0)
} else {
document.addEventListener('DOMContentLoaded', initApp)
}
@patrickbolle
Copy link

I've noticed that putting the query handler above everything else works better off for most themes, might be a good option here.

Thanks for making this available, super helpful.

@tobiasdalhof
Copy link
Author

I've noticed that putting the query handler above everything else works better off for most themes, might be a good option here.

Thanks @patrickbolle
Why is that? I'm not using it in production yet but the thought behind getting the variant id from the add to cart form element with name id first was that id is required by the cart api.

@patrickbolle
Copy link

patrickbolle commented Jun 6, 2019 via email

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