A space to discuss online store customization, theme development, and Liquid templating.
So recently I was looking to add recommended product upsells to my cart, and after writing a few lines of code I got stuck. I managed to get the upsells to show, as well as finding a script that adds the item directly to the cart drawer:
{%- assign current_variant = product.selected_or_first_available_variant -%}
<span id="color"></span>
<button id="color" type="button" {% unless current_variant.available %}disabled="disabled"{% endunless %} onclick="add_to_cart_grid({{current_variant.id}}, 1)" class="btn btn--full">
{% if current_variant.available %}
Shop Now
{% else %}
Out of Stock
{% endif %}
</button>
<script>
function add_to_cart_grid(variantid,qty){
var id = variantid;
var q = qty;
var ajax = {
type: "POST",
url: "/cart/add.js",
data: "quantity=" + q + "&id=" + id,
dataType: "json",
success: function (n) {
var cart = new theme.CartDrawer
cart.init()
cart.open();
},
error: function (n, c) {
console.log('fail');
}
};
jQuery.ajax(ajax)
}
</script>
But the issue with this is that you cannot change the variant, so I also added this piece of code:
<select id="product-select" name="id" onchange="update_current_variant()">
{% for variant in product.variants %}
<option value="{{ variant.id }}"
{% if variant == product.selected_or_first_available_variant %}selected="selected"{% endif %}>
{{ variant.title }} - {{ variant.price | money }}
</option>
{% endfor %}
</select>
And it works great, the select shows all the variants, when I use inspector I can see all the variant id's, but the only problem is I cannot get the current_variant to update according to the option selected in the dropdown. The current variant always stays as the first available variant id, so I'm looking for some help on how to write a script that will update the current variant for me, and replace it according to the variant id thats attached to the selected option from the dropdown. Any help is appreciated, thanks a lot :))
Solved! Go to the solution
This is an accepted solution.
So after some more brainstorming I figured out the solution. Here is the process that you have to take, or at least the one I took to make this work:
1. Add a select dropdown, with all your variants as options, and attach variant.id to those options
2. Add an input before your variant
3. Write a script that will make sure the variant.id of the select option will be the value of the input
4. Write a script that will make your buttons on click {{current_variant.id}} match the inputs value
Some things worth mentioning:
- If you're displaying multiple products you have to add a forloop.index to your select dropdown, product wrapper, input, and button.
Below I attached how my cart drawer code looked like in the end:
<div class="drawer__scrollable">
<div data-products class="appear-animation appear-delay-2"></div>
{% if request.locale.iso_code == 'nl' %}
<div class="product-recommendations" data-url="{{ routes.product_recommendations_url }}?section_id={{ section.id }}&product_id={{ product.id }}&limit=5&intent=related">
{%- if recommendations.performed? and recommendations.products_count > 0 -%}
<h2 class="recommended-heading">Maak je bestelling af</h2>
{% endif %}
<div id="cart-upsell_swiper" class="swiper">
<div class="swiper-wrapper">
{%- for product in recommendations.products -%}
<div class="swiper-slide upsell-slide-{{forloop.index}}">
<a href="{{ product.url }}">
<img
class="recommended_img"
src="{{ product.featured_image | image_url: width: 200, height: 300 }}"
alt="{{ product.featured_image.alt }}" />
<p class="recommended_title">{{ product.title }}</p>
<div class="recommended-price_wrapper">
{% if product.compare_at_price %}
<p class="recommended-compare_price">{{ product.compare_at_price | money }}</p>
{% endif %}
<p class="recommended_price">{{ product.price | money }}</p>
</div>
</a>
<div class="bottom-upsell_wrapper" data-inline="true">
<!-- Modified dropdown menu code -->
<select id="product-select-{{forloop.index}}" class="recommended-select" name="id">
<option value="" disabled selected>MAAT</option>
{% for variant in product.variants %}
{% if variant.available %}
<option value="{{ variant.id }}">
{{ variant.title }}
</option>
{% endif %}
{% endfor %}
</select>
<input type="hidden" name="id" id="color-{{forloop.index}}" value="{{ product.variants.first.id }}" />
{% assign current_variant = product.selected_or_first_available_variant %}
<button data-add-to-cart type="button" id="cart-ub-{{forloop.index}}" {% unless current_variant.available %}disabled="disabled" style="background: #878787;"{% endunless %} onclick="add_to_cart_grid({{current_variant.id}}, 1)" class="upsell-rerun btn btn--full recommended-button">
{% if current_variant.available %}
TOEVOEGEN
{% else %}
UITVERKOCHT
{% endif %}
</button>
</div>
</div>
{%- endfor -%}
</div>
</div>
</div>
{% endif %}
{% if settings.cart_notes_enable %}
<div class="appear-animation appear-delay-3">
<label for="CartNoteDrawer">{{ 'cart.general.note' | t }}</label>
<textarea
name="note"
class="input-full cart-notes"
id="CartNoteDrawer">{{ cart.note }}</textarea>
</div>
{% endif %}
</div>
As well as the scripts:
- Script that matches option's variant.id with the input value:
<script>
window.addEventListener("load", (event) => {
setTimeout(() => {
var select = document.getElementById('product-select-1'),
color = document.getElementById('color-1');
select.addEventListener('change', function() {
var selected = this.options[this.selectedIndex].value;
color.value = selected;
});
}, "1000");
});
</script>
- Script that matches the input value with the button's onclick {{current_variant.id}}
<script>
window.addEventListener("load", (event) => {
setTimeout(() => {
// Get a reference to the dropdown menu and button
const dropdown = document.getElementById('product-select-1');
const button = document.getElementById('cart-ub-1');
// Retrieve the initial value of the hidden input field
const hiddenInput = document.getElementById('color-1');
let currentVariantId = hiddenInput.value;
// Listen for changes in the dropdown menu
dropdown.addEventListener('change', () => {
// Retrieve the value of the hidden input field
const variantId = hiddenInput.value;
// Update the current variant ID
currentVariantId = variantId;
// Update the onclick function of the button
button.setAttribute('onclick', `add_to_cart_grid(${currentVariantId}, 1)`);
});
// Update the button onclick function when it is clicked
button.addEventListener('click', () => {
// Call the add_to_cart_grid function with the current variant ID
add_to_cart_grid(currentVariantId, 1);
});
}, "1000");
});
</script>
- Add to cart drawer script
<script>
function add_to_cart_grid(variantid, qty) {
var id = variantid;
var q = qty;
var ajax = {
type: "POST",
url: "/cart/add.js",
data: "quantity=" + q + "&id=" + id,
dataType: "json",
success: function (n) {
var cart = new theme.CartDrawer
cart.init()
cart.open();
// Call the updateProgressBar function after the item has been added
updateProgressBar();
},
error: function (n, c) {
console.log('fail');
}
};
jQuery.ajax(ajax)
}
</script>
As you can tell the first 2 scripts have a lot of variables, and classes which end with a 1. The reason for this is, since I'm using forloop.index I couldn't figure out how to implement that into the script and make it work, so for my 5 products I'm displaying I just repeated the script 5 times, changing all the 1's into 2's, into 3's, etc. Hopefully this helps someone in the future 🙂
This is an accepted solution.
So after some more brainstorming I figured out the solution. Here is the process that you have to take, or at least the one I took to make this work:
1. Add a select dropdown, with all your variants as options, and attach variant.id to those options
2. Add an input before your variant
3. Write a script that will make sure the variant.id of the select option will be the value of the input
4. Write a script that will make your buttons on click {{current_variant.id}} match the inputs value
Some things worth mentioning:
- If you're displaying multiple products you have to add a forloop.index to your select dropdown, product wrapper, input, and button.
Below I attached how my cart drawer code looked like in the end:
<div class="drawer__scrollable">
<div data-products class="appear-animation appear-delay-2"></div>
{% if request.locale.iso_code == 'nl' %}
<div class="product-recommendations" data-url="{{ routes.product_recommendations_url }}?section_id={{ section.id }}&product_id={{ product.id }}&limit=5&intent=related">
{%- if recommendations.performed? and recommendations.products_count > 0 -%}
<h2 class="recommended-heading">Maak je bestelling af</h2>
{% endif %}
<div id="cart-upsell_swiper" class="swiper">
<div class="swiper-wrapper">
{%- for product in recommendations.products -%}
<div class="swiper-slide upsell-slide-{{forloop.index}}">
<a href="{{ product.url }}">
<img
class="recommended_img"
src="{{ product.featured_image | image_url: width: 200, height: 300 }}"
alt="{{ product.featured_image.alt }}" />
<p class="recommended_title">{{ product.title }}</p>
<div class="recommended-price_wrapper">
{% if product.compare_at_price %}
<p class="recommended-compare_price">{{ product.compare_at_price | money }}</p>
{% endif %}
<p class="recommended_price">{{ product.price | money }}</p>
</div>
</a>
<div class="bottom-upsell_wrapper" data-inline="true">
<!-- Modified dropdown menu code -->
<select id="product-select-{{forloop.index}}" class="recommended-select" name="id">
<option value="" disabled selected>MAAT</option>
{% for variant in product.variants %}
{% if variant.available %}
<option value="{{ variant.id }}">
{{ variant.title }}
</option>
{% endif %}
{% endfor %}
</select>
<input type="hidden" name="id" id="color-{{forloop.index}}" value="{{ product.variants.first.id }}" />
{% assign current_variant = product.selected_or_first_available_variant %}
<button data-add-to-cart type="button" id="cart-ub-{{forloop.index}}" {% unless current_variant.available %}disabled="disabled" style="background: #878787;"{% endunless %} onclick="add_to_cart_grid({{current_variant.id}}, 1)" class="upsell-rerun btn btn--full recommended-button">
{% if current_variant.available %}
TOEVOEGEN
{% else %}
UITVERKOCHT
{% endif %}
</button>
</div>
</div>
{%- endfor -%}
</div>
</div>
</div>
{% endif %}
{% if settings.cart_notes_enable %}
<div class="appear-animation appear-delay-3">
<label for="CartNoteDrawer">{{ 'cart.general.note' | t }}</label>
<textarea
name="note"
class="input-full cart-notes"
id="CartNoteDrawer">{{ cart.note }}</textarea>
</div>
{% endif %}
</div>
As well as the scripts:
- Script that matches option's variant.id with the input value:
<script>
window.addEventListener("load", (event) => {
setTimeout(() => {
var select = document.getElementById('product-select-1'),
color = document.getElementById('color-1');
select.addEventListener('change', function() {
var selected = this.options[this.selectedIndex].value;
color.value = selected;
});
}, "1000");
});
</script>
- Script that matches the input value with the button's onclick {{current_variant.id}}
<script>
window.addEventListener("load", (event) => {
setTimeout(() => {
// Get a reference to the dropdown menu and button
const dropdown = document.getElementById('product-select-1');
const button = document.getElementById('cart-ub-1');
// Retrieve the initial value of the hidden input field
const hiddenInput = document.getElementById('color-1');
let currentVariantId = hiddenInput.value;
// Listen for changes in the dropdown menu
dropdown.addEventListener('change', () => {
// Retrieve the value of the hidden input field
const variantId = hiddenInput.value;
// Update the current variant ID
currentVariantId = variantId;
// Update the onclick function of the button
button.setAttribute('onclick', `add_to_cart_grid(${currentVariantId}, 1)`);
});
// Update the button onclick function when it is clicked
button.addEventListener('click', () => {
// Call the add_to_cart_grid function with the current variant ID
add_to_cart_grid(currentVariantId, 1);
});
}, "1000");
});
</script>
- Add to cart drawer script
<script>
function add_to_cart_grid(variantid, qty) {
var id = variantid;
var q = qty;
var ajax = {
type: "POST",
url: "/cart/add.js",
data: "quantity=" + q + "&id=" + id,
dataType: "json",
success: function (n) {
var cart = new theme.CartDrawer
cart.init()
cart.open();
// Call the updateProgressBar function after the item has been added
updateProgressBar();
},
error: function (n, c) {
console.log('fail');
}
};
jQuery.ajax(ajax)
}
</script>
As you can tell the first 2 scripts have a lot of variables, and classes which end with a 1. The reason for this is, since I'm using forloop.index I couldn't figure out how to implement that into the script and make it work, so for my 5 products I'm displaying I just repeated the script 5 times, changing all the 1's into 2's, into 3's, etc. Hopefully this helps someone in the future 🙂
Hi,...Can you share entire cart-drawer.liquid ? I am looking for the same solution but could not follow exactly steps you outlined...Thanks in Advance
Seems like way too much work. OMG