Skip to content

Commit

Permalink
Small optimizations to JS loading (Shopify#190)
Browse files Browse the repository at this point in the history
  • Loading branch information
tyleralsbury authored Jul 15, 2021
1 parent ee528d9 commit e52bb68
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 216 deletions.
192 changes: 192 additions & 0 deletions assets/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,195 @@ class DeferredMedia extends HTMLElement {
}

customElements.define('deferred-media', DeferredMedia);

class SliderComponent extends HTMLElement {
constructor() {
super();
this.slider = this.querySelector('ul');
this.sliderItems = this.querySelectorAll('li');
this.pageCount = this.querySelector('.slider-counter--current');
this.pageTotal = this.querySelector('.slider-counter--total');
this.prevButton = this.querySelector('button[name="previous"]');
this.nextButton = this.querySelector('button[name="next"]');

if (!this.slider || !this.nextButton) return;

const resizeObserver = new ResizeObserver(entries => this.initPages());
resizeObserver.observe(this.slider);

this.slider.addEventListener('scroll', this.update.bind(this));
this.prevButton.addEventListener('click', this.onButtonClick.bind(this));
this.nextButton.addEventListener('click', this.onButtonClick.bind(this));
}

initPages() {
if (!this.sliderItems.length === 0) return;
this.slidesPerPage = Math.floor(this.slider.clientWidth / this.sliderItems[0].clientWidth);
this.totalPages = this.sliderItems.length - this.slidesPerPage + 1;
this.update();
}

update() {
if (!this.pageCount || !this.pageTotal) return;
this.currentPage = Math.round(this.slider.scrollLeft / this.sliderItems[0].clientWidth) + 1;

if (this.currentPage === 1) {
this.prevButton.setAttribute('disabled', true);
} else {
this.prevButton.removeAttribute('disabled');
}

if (this.currentPage === this.totalPages) {
this.nextButton.setAttribute('disabled', true);
} else {
this.nextButton.removeAttribute('disabled');
}

this.pageCount.textContent = this.currentPage;
this.pageTotal.textContent = this.totalPages;
}

onButtonClick(event) {
event.preventDefault();
const slideScrollPosition = event.currentTarget.name === 'next' ? this.slider.scrollLeft + this.sliderItems[0].clientWidth : this.slider.scrollLeft - this.sliderItems[0].clientWidth;
this.slider.scrollTo({
left: slideScrollPosition
});
}
}

customElements.define('slider-component', SliderComponent);

class VariantSelects extends HTMLElement {
constructor() {
super();
this.addEventListener('change', this.onVariantChange);
}

onVariantChange() {
this.updateOptions();
this.updateMasterId();
this.toggleAddButton(true, '', false);
this.updatePickupAvailability();

if (!this.currentVariant) {
this.toggleAddButton(true, '', true);
this.setUnavailable();
} else {
this.updateMedia();
this.updateURL();
this.updateVariantInput();
this.renderProductInfo();
}
}

updateOptions() {
this.options = Array.from(this.querySelectorAll('select'), (select) => select.value);
}

updateMasterId() {
this.currentVariant = this.getVariantData().find((variant) => {
return !variant.options.map((option, index) => {
return this.options[index] === option;
}).includes(false);
});
}

updateMedia() {
if (!this.currentVariant || !this.currentVariant?.featured_media) return;
const newMedia = document.querySelector(
`[data-media-id="${this.dataset.section}-${this.currentVariant.featured_media.id}"]`
);
if (!newMedia) return;
const parent = newMedia.parentElement;
parent.prepend(newMedia);
window.setTimeout(() => { parent.scroll(0, 0) });
}

updateURL() {
if (!this.currentVariant) return;
window.history.replaceState({ }, '', `${this.dataset.url}?variant=${this.currentVariant.id}`);
}

updateVariantInput() {
const productForms = document.querySelectorAll(`#product-form-${this.dataset.section}, #product-form-installment`);
productForms.forEach((productForm) => {
const input = productForm.querySelector('input[name="id"]');
input.value = this.currentVariant.id;
input.dispatchEvent(new Event('change', { bubbles: true }));
});
}

updatePickupAvailability() {
const pickUpAvailability = document.querySelector('pickup-availability');
if (!pickUpAvailability) return;

if (this.currentVariant?.available) {
pickUpAvailability.fetchAvailability(this.currentVariant.id);
} else {
pickUpAvailability.removeAttribute('available');
pickUpAvailability.innerHTML = '';
}
}

renderProductInfo() {
fetch(`${this.dataset.url}?variant=${this.currentVariant.id}&section_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 destination = document.getElementById(id);
const source = html.getElementById(id);

if (source && destination) destination.innerHTML = source.innerHTML;

document.getElementById(`price-${this.dataset.section}`)?.classList.remove('visibility-hidden');
this.toggleAddButton(!this.currentVariant.available, window.variantStrings.soldOut);
});
}

toggleAddButton(disable = true, text, modifyClass = true) {
const addButton = document.getElementById(`product-form-${this.dataset.section}`)?.querySelector('[name="add"]');

if (!addButton) return;

if (disable) {
addButton.setAttribute('disabled', true);
if (text) addButton.textContent = text;
} else {
addButton.removeAttribute('disabled');
addButton.textContent = window.variantStrings.addToCart;
}

if (!modifyClass) return;
}

setUnavailable() {
const addButton = document.getElementById(`product-form-${this.dataset.section}`)?.querySelector('[name="add"]');
if (!addButton) return;
addButton.textContent = window.variantStrings.unavailable;
document.getElementById(`price-${this.dataset.section}`)?.classList.add('visibility-hidden');
}

getVariantData() {
this.variantData = this.variantData || JSON.parse(this.querySelector('[type="application/json"]').textContent);
return this.variantData;
}
}

customElements.define('variant-selects', VariantSelects);

class VariantRadios extends VariantSelects {
constructor() {
super();
}

updateOptions() {
const fieldsets = Array.from(this.querySelectorAll('fieldset'));
this.options = fieldsets.map((fieldset) => {
return Array.from(fieldset.querySelectorAll('input')).find((radio) => radio.checked).value;
});
}
}

customElements.define('variant-radios', VariantRadios);
57 changes: 0 additions & 57 deletions assets/slider.js

This file was deleted.

133 changes: 0 additions & 133 deletions assets/variants.js

This file was deleted.

3 changes: 0 additions & 3 deletions layout/theme.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,5 @@
unavailable: `{{ 'products.product.unavailable' | t }}`,
}
</script>

<script src="{{ 'slider.js' | asset_url }}" defer="defer"></script>
<script src="{{ 'variants.js' | asset_url }}" defer="defer"></script>
</body>
</html>
Loading

0 comments on commit e52bb68

Please sign in to comment.