Liquid, JavaScript, themes, sales channels
Everything is working but swatch selections are not updating once "Add to Cart" is clicked (whatever the originally selected swatch was is being selected. Please advise!
product.liquid
<div data-section-id="{{ section.id }}" data-section-type="product" data-enable-history-state="true"> <form action="/cart/add" method="post" enctype="multipart/form-data"> {%- assign current_variant = product.selected_or_first_available_variant -%} {%- assign featured_image = current_variant.featured_image | default: product.featured_image -%} {% capture productTitle %}{% include 'product-title' %}{% endcapture %} {% capture productPrice %}{% include 'product-price' %}{% endcapture %} {%- capture image_alt_text -%} {{ productTitle }} - {{ shop.name }} {%- endcapture -%} {% if product.images.size > 1 %} <ul class="productThumbs"> {% for image in product.images %} <li> <a href="{{ image.src | img_url: '480x480' }}" data-thumbnail-id="{{ image.id }}" data-product-single-thumbnail> <img class="product-single__thumbnail-image" src="{{ image.src | img_url: '50x' }}" alt="{{ image_alt_text }}"> </a> </li> {% endfor %} </ul> {% endif %} <div class="mobileOnly"> <div class="vendor">{{ product.vendor }}</div> <h1 class="title">{{ productTitle }}</h1> </div> <div class="mainImage"> {% assign counter = 0 %} {% for image in product.images %} {% capture wrapper_class %} {%- unless featured_image == image -%} hide {%- endunless -%} {% endcapture %} {% capture image_attributes %} data-product-featured-image data-large="{{ image | img_url: '2048x2048' }}" {% endcapture %} <div class="img-zoom-container {{ wrapper_class }}"> {% include 'responsive-image' with image: image, max_width: 2048, max_height: 2048, image_attributes: image_attributes, image_class: 'image-zoom', wrapper_class: wrapper_class, wrapper_attributes: "data-product-image-wrapper", image_alt: image_alt_text %} <div class="zoomedContainer secret"><div id="zoomed-Image-{{ image.id }}-{{ counter }}" class="img-zoom-result"></div></div> <span class="imageInstructions">Roll over image to zoom in</span> </div> {% assign counter = counter | plus: 1 %} {% endfor %} </div> <div class="quickInfo"> <h1 class="title desktopOnly">{{ productTitle }}</h1> <div class="vendor desktopOnly">by {{ product.vendor }}</div> {{ productPrice }} <div class="rte"> <div class="description desktopOnly"> {{ product.description }} </div> {% include 'product-variants' %} </div> </div> <div class="actionsBoxContainer"> {% if section.settings.show_share_buttons %} <div class="socialSharingBox desktopOnly"> <span class="shareButtonInstructions">Share</span> {% include 'social-sharing', share_title: productTitle, share_permalink: product.url, share_image: product %} </div> {% endif %} <div class="actionsBox"> {{ productPrice }} {% if product.available %} <div class="stock inStock">In Stock.</div> {% else %} <div class="stock noStock">Out of Stock.</div> {% endif %} <div class="shipsFrom desktopOnly"> Ships from and sold by {{ shop.name }}. </div> <div class="quantity visually-hidden"> <label for="Quantity">{{ 'products.product.quantity' | t }}</label> <input type="number" id="Quantity" name="quantity" value="1" min="1"> </div> <button type="submit" name="add" class="button" data-add-to-cart {% unless current_variant.available %}disabled="disabled"{% endunless %} {% unless current_variant.price != 0 %}disabled="disabled"{% endunless %}> <span data-add-to-cart-text> {% if current_variant.available %} <span class="icon">{% include 'icon-cart' %}</span> <span class="text">{{ 'products.product.add_to_cart' | t }}</span> {% else %} <span class="soldOut">{{ 'products.product.sold_out' | t }}</span> {% endif %} </span> </button> <div class="shipsFrom mobileOnly"> Ships from and sold by {{ shop.name }}. </div> {% include 'deliver-to' %} </div> <h2 class="about mobileOnly">About this item</h2> <div class="moreInfo mobileOnly"> <div class="description">{{ product.description }}</div> {% include 'product-variants' %} </div> {% if section.settings.show_share_buttons %} <div class="socialSharingBox mobileOnly"> <span class="shareButtonInstructions">Share</span> {% include 'social-sharing', share_title: productTitle, share_permalink: product.url, share_image: product %} </div> {% endif %} {% unless product == empty %} <script type="application/json" data-product-json> {{ product | json }} </script> {% endunless %} </form> </div> {% schema %} { "name": "Product pages", "settings": [ { "type": "checkbox", "id": "show_share_buttons", "label": "Show social sharing buttons", "default": true } ] } {% endschema %} {% javascript %} $( document ).ready(function() { function imageZooming() { var windowWidth = $(window).width(); if (windowWidth >= 1300) { $('.imageInstructions').show(); $('.img-zoom-container').on({ mouseenter: function () { $(this).children('.responsive-image__wrapper').children('.img-zoom-lens').removeClass('secret'); $(this).children('.zoomedContainer').removeClass('secret'); $('.imageInstructions').html('Click image to open expanded view'); }, mouseleave: function () { $(this).children('.responsive-image__wrapper').children('.img-zoom-lens').addClass('secret'); $(this).children('.zoomedContainer').addClass('secret'); $('.imageInstructions').html('Roll over image to zoom in'); } }); $('.img-zoom-container img.image-zoom').each(function() { var zoomedId = "zoomed-" + this.id; imageZoom(this.id, zoomedId); }); } else { $('.imageInstructions').hide(); } } $(window).resize(function(){ imageZooming(); }); imageZooming(); }); {% endjavascript %}
product-variants.liquid
<div class="variants"> {% unless product.has_only_default_variant %} {% for option in product.options_with_values %} {%- capture optionValuesSize -%}{{ option.values | size }}{%- endcapture -%} <div class="selector-wrapper js swatch option{% if optionValuesSize == '1' %} oneOption{% endif %}"> {%- assign i = forloop.index0 -%} <h5 class="name">{{ option.name }}</h5> <div class="values"> {% for value in option.values %} <input class="swatch-element" id="SingleOptionSelector-{{ i }}-{{ value | handle }}" data-single-option-selector data-index="option{{ option.position }}" type="radio" name="SingleOptionSelector-{{ i }}" value="{{ value | escape }}"{% if option.selected_value == value %} checked{% endif %} {% unless current_variant.available %}disabled="disabled"{% endunless %} > <label for="SingleOptionSelector-{{ i }}-{{ value | handle }}" {% if optionValuesSize == '1' %}class="onlyOption"{% endif %}><span>{{ value }}</span></label> {% endfor %} </div> </div> {% endfor %} {% endunless %} {% if current_variant.sku != blank %} <div class="option sku"> <h5 class="name">Item #</h5> <span class="value">{{ current_variant.sku }}</span> </div> {% endif %} <select name="id" class="no-js" data-product-select> {% for variant in product.variants %} <option {% if variant == current_variant %}selected="selected"{% endif %} {% unless variant.available %}disabled="disabled"{% endunless %} value="{{ variant.id }}"> {{ variant.title }} </option> {% endfor %} </select> </div>
product.js
/** * Product Template Script * ------------------------------------------------------------------------------ * A file that contains scripts highly couple code to the Product template. * * @namespace product */ import $ from 'jquery'; import Variants from '@shopify/theme-variants'; import {formatMoney} from '@shopify/theme-currency'; import {register} from '@shopify/theme-sections'; const selectors = { addToCart: '[data-add-to-cart]', addToCartText: '[data-add-to-cart-text]', comparePrice: '[data-compare-price]', comparePriceText: '[data-compare-text]', originalSelectorId: '[data-product-select]', priceWrapper: '[data-price-wrapper]', productImageWrapper: '[data-product-image-wrapper]', productFeaturedImage: '[data-product-featured-image]', productJson: '[data-product-json]', productPrice: '[data-product-price]', productThumbs: '[data-product-single-thumbnail]', singleOptionSelector: '[data-single-option-selector]', }; const cssClasses = { activeThumbnail: 'active-thumbnail', hide: 'hide', }; const keyboardKeys = { ENTER: 13, }; /** * Product section constructor. Runs on page load as well as Theme Editor * `section:load` events. * @param {string} container - selector for the section container DOM element */ register('product', { onLoad() { this.$container = $(this.container); this.namespace = `.${this.id}`; // Stop parsing if we don't have the product json script tag when loading // section in the Theme Editor if (!$(selectors.productJson, this.$container).html()) { return; } this.productSingleObject = JSON.parse( $(selectors.productJson, this.$container).html(), ); const options = { $container: this.$container, enableHistoryState: this.$container.data('enable-history-state') || false, singleOptionSelector: selectors.singleOptionSelector, originalSelectorId: selectors.originalSelectorId, product: this.productSingleObject, }; this.settings = {}; this.variants = new Variants(options); this.$featuredImage = $(selectors.productFeaturedImage, this.$container); this.$container.on( `variantChange${this.namespace}`, this.updateAddToCartState.bind(this), ); this.$container.on( `variantPriceChange${this.namespace}`, this.updateProductPrices.bind(this), ); if (this.$featuredImage.length > 0) { this.$container.on( `variantImageChange${this.namespace}`, this.updateImages.bind(this), ); } $( ".swatch input.swatch-element" ).click(function() { // console.log(this); // $(".swatch input.swatch-element").each(function() { // $(this).prop('checked', false); // }); // var checkbox = ".swatch input#" + this.id; // $(checkbox).prop('checked', !$(checkbox).prop('checked')); $(".swatch label.selectedOnLoad").each(function() { $(this).removeClass("selectedOnLoad"); }); }); this.highlightSelectedVariantOnLoad(); this.initImageSwitch(); }, highlightSelectedVariantOnLoad() { $(".swatch input.swatch-element:checked").each(function() { $(".swatch label[for='" + this.id + "']").addClass("selectedOnLoad"); }); }, initImageSwitch() { const $productThumbs = $(selectors.productThumbs, this.$container); if (!$productThumbs.length) { return; } $productThumbs .on('click', (evt) => { evt.preventDefault(); const imageId = $(evt.currentTarget).data('thumbnail-id'); this.switchImage(imageId); this.setActiveThumbnail(imageId); }) .on('keyup', this.handleImageFocus.bind(this)); }, handleImageFocus(evt) { if (evt.keyCode !== keyboardKeys.ENTER) { return; } this.$featuredImage.filter(':visible').focus(); }, setActiveThumbnail(imageId) { let newImageId = imageId; // If "imageId" is not defined in the function parameter, find it by the current product image if (typeof newImageId === 'undefined') { newImageId = $( `${selectors.productImageWrapper}:not('.${cssClasses.hide}')`, ).data('image-id'); } const $thumbnail = $( `${selectors.productThumbs}[data-thumbnail-id='${newImageId}']`, ); $(selectors.productThumbs) .removeClass(cssClasses.activeThumbnail) .removeAttr('aria-current'); $thumbnail.addClass(cssClasses.activeThumbnail); $thumbnail.attr('aria-current', true); }, switchImage(imageId) { const $newImage = $( `${selectors.productImageWrapper}[data-image-id='${imageId}']`, this.$container, ); const $otherImages = $( `${selectors.productImageWrapper}:not([data-image-id='${imageId}'])`, this.$container, ); $newImage.removeClass(cssClasses.hide); $otherImages.addClass(cssClasses.hide); }, /** * Updates the DOM state of the add to cart button * * @param {boolean} enabled - Decides whether cart is enabled or disabled * @param {string} text - Updates the text notification content of the cart */ updateAddToCartState(evt) { const variant = evt.variant; if (variant) { $(selectors.priceWrapper, this.$container).removeClass(cssClasses.hide); } else { $(selectors.addToCart, this.$container).prop('disabled', true); $(selectors.priceWrapper, this.$container).addClass(cssClasses.hide); return; } if (variant.available) { $(selectors.addToCart, this.$container).prop('disabled', false); } else { $(selectors.addToCart, this.$container).prop('disabled', true); } if (variant.price != 0) { $(selectors.addToCart, this.$container).prop('disabled', false); } else { $(selectors.addToCart, this.$container).prop('disabled', true); } }, updateImages(evt) { const variant = evt.variant; const imageId = variant.featured_image.id; this.switchImage(imageId); this.setActiveThumbnail(imageId); }, /** * Updates the DOM with specified prices * * @param {string} productPrice - The current price of the product * @param {string} comparePrice - The original price of the product */ updateProductPrices(evt) { const variant = evt.variant; const $comparePrice = $(selectors.comparePrice, this.$container); const $compareEls = $comparePrice.add( selectors.comparePriceText, this.$container, ); const freeText = $('.freeText').html(); if (variant.price == 0) { $(selectors.productPrice, this.$container).html( freeText, ); } else { $(selectors.productPrice, this.$container).html( formatMoney(variant.price, theme.moneyFormat), ); } if (variant.compare_at_price > variant.price) { $comparePrice.html( formatMoney(variant.compare_at_price, theme.moneyFormat), ); $compareEls.removeClass(cssClasses.hide); } else { $comparePrice.html(''); $compareEls.addClass(cssClasses.hide); } }, /** * Event callback for Theme Editor `section:unload` event */ onUnload() { this.$container.off(this.namespace); }, });
This is URGENT! Any help ASAP would be hugely appreciated. Compensation can be provided if necessary.
Hello! have you got the solution?
Hi, I have the same problem. Have you managed to solve it?
Are you ready to take your business to the next level? Look no further than the latest ...
By SarahF_Shopify Apr 15, 2024We’re keeping the ball rolling to make sure you’re always ahead of the game. So buckle ...
By JasonH Apr 8, 2024Portrait of Stephen positioned next to an image of planet Earth, with the Stephen's World ...
By JasonH Mar 18, 2024