I wrote a piece of code that works in a way where once I select a variant on the product page, only media with the same alt text as the variant name, will display while the rest of the media is hidden. My only problem is when the product page loads it displays all the media, even if I go directly to the URL with variant ID in it. So a variant has to be selected, for my page to show only matching alt-text media. I'm wondering if there is a way to either change this or make it so that on the website load the first variant gets selected.
Here is the HTML & Liquid code I'm using:
{% unless product.has_only_default_variant %}
<variant-selector data-url="{{ product.url }}" data-section="{{ section.id }}">
{% for option in product.options_with_values %}
<label for="option-{{ section.id }}-{{ forloop.index0 }}">{{ option.name }}</label>
<select name="options[{{ option.name | escape }}]" id="option-{{ section.id }}-{{ forloop.index0 }}">
{% for value in option.values %}
<option value="{{ value | escape }}" {% if option.selected_value == value %} selected="selected" {% endif %}>{{ value }}</option>
{% endfor %}
</select>
<script type="application/json">
{{ product.variants | json }}
</script>
{% endfor %}
</variant-selector>
{% endunless %}
And here is the javascript code:
class VariantSelector extends HTMLElement {
constructor() {
super();
this.addEventListener("change", this.onVariantChange);
}
onVariantChange() {
this.getSelectedOptions();
this.getSelectedVariant();
this.updateMedia(this.currentVariant);
if (this.currentVariant) {
this.updateURL();
this.updateFormID();
this.updatePrice();
this.updateMedia();
}
}
getSelectedOptions() {
this.options = Array.from(this.querySelectorAll('select'), (select) => select.value);
console.log(this.options);
}
getVariantJSON() {
this.variantData = this.variantData || JSON.parse(this.querySelector('[type="application/json"]').textContent);
return this.variantData;
}
getSelectedVariant() {
this.currentVariant = this.getVariantJSON().find(variant => {
const findings = !variant.options.map((option, index) => {
return this.options[index] === option;
}).includes(false);
if (findings) return variant;
});
console.log(this.currentVariant);
}
updateURL() {
if (!this.currentVariant) return;
window.history.replaceState({}, '', `${this.dataset.url}?variant=${this.currentVariant.id}`);
}
updateFormID() {
const form_input = document.querySelector("#product-form").querySelector('input[name="id"]');
form_input.value = this.currentVariant.id;
}
updatePrice() {
fetch(`${this.dataset.url}?variant=${this.currentVariant.id}§ion_id=${this.dataset.section}`)
.then((response) => response.text())
.then((responseText) => {
const id = `price-${this.dataset.section}`;
const html = new DOMParser().parseFromString(responseText, 'text/html');
const oldPrice = document.getElementById(id);
const newPrice = html.getElementById(id);
if (oldPrice && newPrice) oldPrice.innerHTML = newPrice.innerHTML;
});
}
updateMedia() {
if(this.currentVariant.featured_image !=null && this.currentVariant.featured_image.alt != null) {
$('[thumbnail-color]').hide();
var selected_color = this.currentVariant.featured_image.alt;
var thumbnail_selector = '[thumbnail-color="' + selected_color + '"]';
$(thumbnail_selector).show();
} else {
$('[thumbnail-color]').hide();
}
}
}
customElements.define("variant-selector", VariantSelector);
Related
When I click on a list item 'Add' button, I'm using button.onclick to send that item's text to fill out a table on the same html page. However, I keep getting a "Cannot read property 'style' of null" in the console whenever I click an item. This was working fine until recently. Something changed...somewhere, but I can't figure out why it's returning this error suddenly.
Here's the Javascript
function showPage(page) {
document.querySelectorAll('div').forEach(div => {
div.style.display = 'none';
})
document.querySelector(`#${page}`).style.display = 'block';
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('button').forEach(button => {
button.onclick = function() {
showPage(this.dataset.page);
}
});
});
function myFunction(txt) {
var myTxt = txt;
console.log(myTxt)
if (txt.includes('QB')) {
document.getElementById("id_QB").value = myTxt;
}
else if (txt.includes('RB')) {
document.getElementById("id_RB1").value = myTxt;
}
else if (txt.includes('WR')) {
document.getElementById("id_WR").value = myTxt;
}
else if (txt.includes('TE')) {
document.getElementById("id_TE").value = myTxt;
}
else if (txt.includes('K')) {
document.getElementById("id_K").value = myTxt;
}
}
</script>
Here's the html page which is displaying a django template (I've deleted the on-page script tags containing the Javascript above).
{% load static %}
{% block body %}
<form method="POST">
{% csrf_token %}
<table id="playerName">
{{ form.as_table }}
</table>
<input type="submit" value="Submit" >
</form>
<br>
{% for player_data in player_data %}
<li><p>Player ID: {{ player_data.player_id }}: {{ player_data.player_name }}, {{ player_data.team }}, {{ player_data.position }}</p></li> <button onclick="myFunction('{{ player_data.player_name }} {{ player_data.position }}')">Add</button>
{% endfor %}
{% endblock %} ```
This is my views.py
def list_note(request):
note_info = Note.objects.filter(id_teacher__exact=request.user.id).select_related()
actual_date = date.today()
for notes in note_info:
note_date = notes.remind.date()
tmp = actual_date + timedelta(days=3)
note_expired_list = []
if tmp == note_date:
print()
else:
note_expired_list.append(notes)
print(note_expired_list)
note_data = {
"note_details": note_info,
"note_expired_list_details": note_expired_list,
}
return render_to_response('list_note.html', note_data, context_instance=RequestContext(request))
I want use value note_expired_list_details in <scrpit> tag to display this in alert. How do this?
I try use {{ note_expired_list_details}} but in <script> tag not work.
This is a part of my templates (I try finding HTML element by Id in JS)
{% extends "base.html" %}
{% load staticfiles %}
{% block content %}
<div >
{% for details in note_expired_list_details %}
<p>{{ details }}</p>
{% endfor %}
<script>
var x = document.getElementsByTagName("p");
alert("Test\n" + x[1].childNodes.nodeValue + "\n");
</script>
</div>
{% endblock %}
Why not store value of {{ note_expired_list_details }} in a JS variable and then use it however you want?
<script>
var expiredList = '{{ note_expired_list_details }}';
alert(expiredList);
</script>
I've been given a script by my e-commerce provider that will allow me to dynamically change the stock levels based on the product option selected by the user from a simple select element.
While the code seems strait forward, it will not run properly. As I am fairly new to jQuery, I'm assuming this is due to me not properly initializing the script within my html. The code itself is a default code that my e-commerce provider hands out, but does not support it in any way.
Here is the script
<script>
// <![CDATA[
var selectCallback = function(variant, selector) {
if (variant) {
if (variant.available) {
// Selected a valid variant that is available.
$('#add-to-cart').removeClass('disabled').removeAttr('disabled').val('Add to Cart').fadeTo(200,1);
} else {
// Variant is sold out.
$('#add-to-cart').val('Sold Out').addClass('disabled').attr('disabled', 'disabled').fadeTo(200,0.5);
}
// Whether the variant is in stock or not, we can update the price and compare at price.
if ( variant.compare_at_price > variant.price ) {
$('#product_price').html('<span class="product-price on-sale">'+ Shopify.formatMoney(variant.price, "") +'</span>'+' <s class="product-compare-price">'+Shopify.formatMoney(variant.compare_at_price, "")+ '</s>');
} else {
$('#product_price').html('<span class="product-price">'+ Shopify.formatMoney(variant.price, "") + '</span>' );
}
} else {
// variant doesn't exist.
$('#add-to-cart').val('Unavailable').addClass('disabled').attr('disabled', 'disabled').fadeTo(200,0.5);
}
}
// initialize multi selector for product
jQuery(function($) {
new Shopify.OptionSelectors("product-select", { product: , onVariantSelected: selectCallback });
});
// ]]>
</script>
Any ideas on why this might not be working? You can see this script live on my site:
http://www.yandasmusic.com/products/fender-american-standard-stratocaster?variant=1178632565
EDIT:
Upon further inspection, I have found a second piece of code that also calls up the product option selection box. I can tell that this code also serves the function of changing the currently shown product image based on the current option selected:
<script>
var selectCallback = function(variant, selector) {
if (variant && variant.available) {
jQuery('#add-to-cart').removeAttr('disabled').removeClass('disabled'); // remove unavailable class from add-to-cart button, and re-enable button
if(variant.price < variant.compare_at_price){
jQuery('#product_price .price').html('<span class="money">' + Shopify.formatMoney(variant.price, "{{ shop.money_format }}") + '</span><span class="money compare-at-price">' + Shopify.formatMoney(variant.compare_at_price, "{{ shop.money_format }}") + '</span>');
} else {
jQuery('#product_price .price').html('<span class="money">' + Shopify.formatMoney(variant.price, "{{ shop.money_format }}") + '</span>');
}
} else {
jQuery('#add-to-cart').addClass('disabled').attr('disabled', 'disabled'); // set add-to-cart button to unavailable class and disable button
var message = variant ? "Sold Out" : "Unavailable";
jQuery('#product_price .price').text(message);
}
// if (variant && variant.featured_image) {
// var originalImage = $(".zoomWrapper img");
// var newImage = variant.featured_image;
// var element = originalImage[0];
// Shopify.Image.switchImage(newImage, element, function (newImageSizedSrc, newImage, element) {
// $(element).parents('a').attr('href', newImageSizedSrc);
// $(element).attr('src', newImageSizedSrc);
// });
// };
if (variant && variant.featured_image) {
var originalImage = $("#elevatezoom_big");
var newImage = variant.featured_image;
var element = originalImage[0];
Shopify.Image.switchImage(newImage, element, function (newImageSizedSrc, newImage, element) {
$(element).attr('src', newImageSizedSrc);
$("#elevatezoom_gallery a").each(function(){
if ( $(this).attr('data-zoom-image') == newImageSizedSrc ) {
$(this).trigger('click')
};
});
});
};
};
jQuery(document).ready(function($){
new Shopify.OptionSelectors("product-select", { product: {{ product | json }}, onVariantSelected: selectCallback, enableHistoryState: true });
// Add label if only one product option and it isn't 'Title'.
{% if product.options.size == 1 and product.options.first != 'Title' %}
$('.selector-wrapper:eq(0)').prepend('<label>{{ product.options.first }}</label>');
{% endif %}
// Auto-select first available variant on page load.
{% assign found_one_in_stock = false %}
{% for variant in product.variants %}
{% if variant.available and found_one_in_stock == false %}
{% assign found_one_in_stock = true %}
{% for option in product.options %}
$('.single-option-selector:eq({{ forloop.index0 }})').val({{ variant.options[forloop.index0] | json }}).trigger('change');
{% endfor %}
{% endif %}
{% endfor %}
});
</script>
If I run both scripts, the page shows two select boxes for the product options. Is it possible to combine them?
You have a JavaScript error on your page because of this line of code:
new Shopify.OptionSelectors("product-select", { product: , onVariantSelected: selectCallback });
According to this reference document: https://docs.shopify.com/manual/configuration/store-customization/advanced-navigation/linked-product-options, that line of code should actually look like this:
new Shopify.OptionSelectors("product-select", { product: {{ product | json }}, onVariantSelected: selectCallback });
I have a function in twig which selects some values from db and displays a selectbox. I am trying to change the content of the div. The problem is that with innerHTML {{ creates a new line without quote and this is shown like error. It doesnt get the select box because it doesn't have quotes.
$(document).ready(function() {
$type = $("select[name='dtl[USER_TYPE]']");
$type.change(function() {
if ($(this).val() == "AUTOR") {
var content = '{{ mm.select(holdersdata, data.USER_TYPE_OBJECT_KOD, 'dtl[USER_TYPE_OBJECT_KOD]') }}';
document.getElementById("kodi").innerHTML = '"'+ content + '"';
}
});
});
macros.twig
<select data-placeholder="{{ translate('ZGJIDH_NJE') }}" name="{{ name }}" class="form-control input-sm chosen-select">
<option {% if not options.allowNull %}disabled{% endif %} selected value>{{ translate('ZGJIDH_NJE') }}</option>
{% for f in dataset %}
<option value="{{ f[kodField] }}" {% if f[kodField] | trim == selectedVal %}selected{% endif %}>
{% if f[labelField] %}
{{ f[labelField] }} {% if f[kodField] %}— ({{ f[kodField] }}){% endif %}
{% else %}
{{ f[kodField] }}
{% endif %}
</option>
{% endfor %}
</select>
EDIT
this is shown in console. {{ creates a new line without quotes:
var content = "
<select data-placeholder="Zgjidh nje..." name="dtl[USER_TYPE_OBJECT_KOD]" class="form-control input-sm chosen-select">
<option disabled selected value>Zgjidh nje...</option>
</select>
The problem is that your twig syntax is breaking the javascript syntax. See below, just change the 'to "
if ($(this).val() == "AUTOR") {
var content = '{{ mm.select(holdersdata, data.USER_TYPE_OBJECT_KOD, 'dtl[USER_TYPE_OBJECT_KOD]') }}';
document.getElementById("kodi").innerHTML = '"'+ content + '"';
}
Correct code :
if ($(this).val() == "AUTOR") {
var content = "{{ mm.select(holdersdata, data.USER_TYPE_OBJECT_KOD, 'dtl[USER_TYPE_OBJECT_KOD]') }}";
document.getElementById("kodi").innerHTML = '"'+ content + '"';
}
Due to the contents of your output JS is breaking as well. U can solve this by extending twig :
$filter = new Twig_SimpleFilter('escape_for_js', function ($string) {
$needles= array(
"\n",
"\r",
'"',
);
$replaces = array(
'',
'',
'\"',
);
return str_replace($needles, $replaces, $string);
});
Add this filter into twig :
$twig = new Twig_Environment($loader);
$twig->addFilter($filter
Using this filter :
var content = "{{ mm.select(holdersdata, data.USER_TYPE_OBJECT_KOD, 'dtl[USER_TYPE_OBJECT_KOD]') | escape_for_js }}";
More about extending twig here
No need to extend Twig yourself. Just use json_encode:
var content = {{ mm.select(holdersdata, data.USER_TYPE_OBJECT_KOD, 'dtl[USER_TYPE_OBJECT_KOD]') | json_encode }};
Notice the lack of quotes. json_encode will add them for you.
I have a dictionary like:
dest = {2: [u'canada', u'A', 'Ottawa'], 5: [u'Malaysia', u'A', 'KualaLumpur'],...}
Then I tried to retrieve the keys and values from dest in django template using javascript:
function categorizeReports()
{
var a = [];
var b = [];
{% for i,v in dest %}
id = '{{i}}';
console.log('id', id)
values = '{{v}}';
console.log('values',values)
{% for name, type, cat in values %}
if(type=='A' && cat=='Ottawa')
{
a.push(id,name,type,cat)
}
if(type=='A' && cat=='KualaLumpur')
{
b.push(id,name,type,cat)
}
{% endfor %}
{% endfor %}
console.log(a)
console.log(b)
}
But both the Arrays are shown as 'an empty string' as well as id and values are too shown as 'an empty string', Please help!
Missing .items
Change
{% for i,v in dest %}
to
{% for i,v in dest.items %}