Add Quickview button

Add Quickview button

How to see the code

1. Please log in/create an account first. This is to ensure that all purchases are connected to an account and can be viewed on different devices.

2. Purchase the code using the "purchase box" on the specific page of the desired code.

3. After the purchase, please refresh the page.

Compatability: Dawn 2.0 to Shopify 3.0

Add a quick view button with no app or external library. We will utilize Shopify's section rending API in this code. 

 Check DEMO store here 💻. Password: made4uo

To start:

1. Go to Admin Shopify store > Themes > Actions > Edit Code

2. In Section folder, create a new section, name it "quick-view", then replace the default code with the code below.

 {{ 'quick-view.css' | asset_url | stylesheet_tag }}
<product-viewer>
<div class="hidden">
<div id="buttonViewer">
<div class="buttonViewer" style="background: {{section.settings.button_background}}; color: {{section.settings.button_font_color}}; font-size: clamp(12px, 1vw, 20px);">
<span class="desktopbutton">
<span>{{section.settings.button_label}}</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
<style type="text/css">
.st--0{fill:none;}
.st--1{fill: {{section.settings.button_font_color}};}
</style>
<g id="icon-eye">
<path class="st--0" d="M19.86,8.36c-5.96,0.26-11.2,3.82-15.25,8.02c-1.03,1.11-2,2.31-2.98,3.51c-0.33,0.41-0.31,0.96-0.01,1.42
c0.21,0.31,0.41,0.63,0.66,0.9c10.1,13.8,25.85,13.6,35.76-0.3C40.34,16.95,24.74,7.44,19.86,8.36z"/>
<path class="st--1" d="M19.86,8.36c8.91,0.49,17.14,7.75,18.5,12.53c0.05,0.19-0.01,0.47-0.13,0.63
C28.31,35.31,12.34,35.96,2.27,22.2c-0.24-0.26-0.45-0.58-0.66-0.9c-0.3-0.45-0.32-1.01,0.01-1.42c0.98-1.19,1.95-2.4,2.98-3.51
C8.65,12.18,13.9,8.62,19.86,8.36z M20.06,11.35c-8.99-0.25-8.86,18.67-0.1,18.52C28.97,30.08,28.9,11.12,20.06,11.35z
M26.06,29.26c4.06-1.67,7.57-4.94,10.49-8.36c-1.8-3.77-6.61-7.15-10.51-8.97C29.7,17.74,29.68,23.5,26.06,29.26z M13.99,11.95
c-0.03-0.04-0.03-0.03-0.06-0.07c-3.85,2.02-8.6,5.13-10.43,9.01c2.96,3.35,6.39,6.72,10.52,8.4
C10.36,23.49,10.37,17.72,13.99,11.95z"/>
<path class="st--0" d="M20.06,11.35c8.84-0.23,8.91,18.73-0.1,18.52C11.2,30.02,11.07,11.1,20.06,11.35z M21.23,15.55
c-4.52-2.07-6.82,6.52-3.77,9.19c3.95,4.36,8.16-3.43,6.07-6.51C21.77,20.95,19.51,17.29,21.23,15.55z"/>
<path class="st--0" d="M26.06,29.26c3.62-5.76,3.64-11.51-0.02-17.34c3.89,1.82,8.7,5.21,10.51,8.97
C33.64,24.32,30.12,27.58,26.06,29.26z"/>
<path class="st--0" d="M13.98,11.96c-3.61,5.77-3.62,11.53,0.04,17.33c-4.13-1.68-7.56-5.05-10.52-8.4
c1.83-3.88,6.57-6.98,10.43-9.01C13.95,11.91,13.96,11.93,13.98,11.96z"/>
<path class="st--1" d="M21.23,15.55c-1.72,1.75,0.54,5.39,2.3,2.68c2.1,3.09-2.11,10.87-6.07,6.51
C14.41,22.07,16.71,13.48,21.23,15.55z"/>
</g>
</svg>
</span><span class="mobilebutton">
<svg viewBox="0 0 40 40" x="121" y="112">
<use xlink:href="#icon-eye" />
</svg>
</span>
</div>
</div>
</div>
<div class="viewerBackground {% if template == 'page' %} view {% endif %}">
<div class="productViewContainer color-{{ section.settings.color_scheme }}">
<div class="quickViewButtonContainer">
<div class="navigatorWrapper">
<button type="button"
class="prevNavigator navigatorBtn"
name="prev"
style="background: {{section.settings.button_background}}; color: {{section.settings.button_font_color}}">PREV</button>
<button type="button"
class="nextNavigator navigatorBtn"
name="next"
style="background: {{section.settings.button_background}}; color: {{section.settings.button_font_color}}">NEXT</button>
<span class="errorMessage"></span>
</div>
<span class="productViewerClose" style=" stroke: {{section.settings.button_font_color}}; background: {{section.settings.button_background}}">{% render 'icon-close' %}</span>
</div>
<div class="productViewer"></div>
</div>
</div>
</product-viewer>

<script src="{{ 'product-form.js' | asset_url }}" defer="defer"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
function isIE() {
const ua = window.navigator.userAgent;
const msie = ua.indexOf('MSIE ');
const trident = ua.indexOf('Trident/');

return (msie > 0 || trident > 0);
}

if (!isIE()) return;
const hiddenInput = document.querySelector('#{{ product_form_id }} input[name="id"]');
const noScriptInputWrapper = document.createElement('div');
const variantSwitcher = document.querySelector('variant-radios[data-section="{{ product.id }}-{{forloop.index0}}"]') || document.querySelector('variant-selects[data-section="{{ product.id }}-{{forloop.index0}}"]');
noScriptInputWrapper.innerHTML = document.querySelector('.product-form__noscript-wrapper-{{ product.id }}-{{forloop.index0}}').textContent;
variantSwitcher.outerHTML = noScriptInputWrapper.outerHTML;

document.querySelector('#Variants-{{ product.id }}').addEventListener('change', function(event) {
hiddenInput.value = event.currentTarget.value;
});

});

</script>
<script type="application/ld+json">
{
"@context": "http://schema.org/",
"@type": "Product",
"name": {{ product.title | json }},
"url": {{ shop.url | append: product.url | json }},
{% if seo_media -%}
{%- assign media_size = seo_media.preview_image.width | append: 'x' -%}
"image": [
{{ seo_media | img_url: media_size | prepend: "https:" | json }}
],
{%- endif %}
"description": {{ product.description | strip_html | json }},
{% if product.selected_or_first_available_variant.sku != blank -%}
"sku": {{ product.selected_or_first_available_variant.sku | json }},
{%- endif %}
"brand": {
"@type": "Thing",
"name": {{ product.vendor | json }}
},
"offers": [
{%- for variant in product.variants -%}
{
"@type" : "Offer",
{%- if variant.sku != blank -%}
"sku": {{ variant.sku | json }},
{%- endif -%}
"availability" : "http://schema.org/{% if variant.available %}InStock{% else %}OutOfStock{% endif %}",
"price" : {{ variant.price | divided_by: 100.00 | json }},
"priceCurrency" : {{ cart.currency.iso_code | json }},
"url" : {{ shop.url | append: variant.url | json }}
}{% unless forloop.last %},{% endunless %}
{%- endfor -%}
]
}
</script>

{% schema %}
{
"name": "Quickview",
"settings": [
{
"type": "header",
"content": "Quickview button style"
},
{
"type": "color",
"id": "button_background",
"label": "Button background",
"default": "#000"
},
{
"type": "color",
"id": "button_font_color",
"label": "Button font color",
"default": "#fff"
},
{
"type": "text",
"id": "button_label",
"label": "Button text",
"default": "QUICKVIEW"
},
{
"type": "header",
"content": "Pop-up modal style"
},
{
"type": "select",
"id": "color_scheme",
"options": [
{
"value": "accent-1",
"label": "Accent 1"
},
{
"value": "accent-2",
"label": "Accent 2"
},
{
"value": "background-1",
"label": "Background 1"
},
{
"value": "background-2",
"label": "Background 2"
},
{
"value": "inverse",
"label": "Inverse"
}
],
"default": "background-1",
"label": "t:sections.all.colors.label"
}
],
"presets": [
{
"name": "Quickview"
}
]
}
{% endschema %}

 

3. We need to create another section for our template. We are using Section Rendering API from Shopify. Name this newly created section "quick-view-template." Then paste the code below.

 {{ 'section-main-product.css' | asset_url | stylesheet_tag }}
{{ 'section-featured-product.css' | asset_url | stylesheet_tag }}
{{ 'component-price.css' | asset_url | stylesheet_tag }}
{{ 'component-rte.css' | asset_url | stylesheet_tag }}
{{ 'component-loading-overlay.css' | asset_url | stylesheet_tag }}
{{ 'quick-view.css' | asset_url | stylesheet_tag }}


<link rel="stylesheet" href="{{ 'component-deferred-media.css' | asset_url }}" media="print" onload="this.media='all'">

{%- assign first_3d_model = product.media | where: "media_type", "model" | first -%}
{%- if first_3d_model -%}
{{ 'component-product-model.css' | asset_url | stylesheet_tag }}
<link id="ModelViewerStyle" rel="stylesheet" href="https://cdn.shopify.com/shopifycloud/model-viewer-ui/assets/v1.0/model-viewer-ui.css" media="print" onload="this.media='all'">
<link id="ModelViewerOverride" rel="stylesheet" href="{{ 'component-model-viewer-ui.css' | asset_url }}" media="print" onload="this.media='all'">
{%- endif -%}

<section>
<div class="section-{{ section.id }}-padding">
<div class="featured-product product grid grid--1-col gradient color-{{ section.settings.color_scheme }}{% if section.settings.secondary_background == false %} isolate{% endif %} {% if product.media.size > 0 %}grid--2-col-tablet{% else %}product--no-media{% endif %}">
<div class="grid__item product__media-wrapper">
{% if product %}
<slider-quick-view>
<ul class="largeImage">
{%- for media in product.media -%}
<li class="large-image-item product__media-item grid__item slider__slide {% if forloop.first %} is-active {% endif %}
{% if media.media_type != 'image' %} hide-modal {% endif %}"
data-media-id="{{ media.id }}">
{% render 'product-thumbnail', media: media, position: forloop.index, loop: true, modal_id: product.id, xr_button: true %}
</li>
{%- endfor -%}
</ul>
<div class="slider-wrapper">
<button type="button" class="slider-button slider-button--prev bigger-slider {% if product.media.size <= 4 %}hidden{% endif %}"
name="previous"
aria-label="{{ 'accessibility.previous_slide' | t }}">{% render 'icon-caret' %}</button>

<ul class="product-slider-box slider" role="list">
{%- assign variant_images = product.images| where: 'attached_to_variant?', true | map: 'src' -%}
{%- for media in product.media -%}
<li class="product-slider slider__slide" data-thumb-id="{{ media.id }}">
{% if media.media_type == 'video' %}
<div class="video-btn" style="display: block" >
<span class="product__media-icon motion-reduce v-btn" aria-hidden="true">
{%- liquid
case media.media_type
when 'video' or 'external_video'
render 'icon-play'
when 'model'
render 'icon-3d-model'
else
render 'icon-zoom'
endcase
-%}
</span>
<img class="slide-image {% if forloop.first %}active-thumb {% endif %}"
src="{{ media.preview_image | img_url: 'large' }}"
alt="{{ media.alt}}"
style="aspect-ratio: 1;">
</div>
{% else %}
<img class="slide-image {% if forloop.first %}active-thumb {% endif %}"
src="{{ media.preview_image | img_url: 'large' }}"
alt="{{ media.alt }}"
style="aspect-ratio: 1;">
{% endif %}
</li>
{% endfor %}
</ul>
<button type="button" class="slider-button slider-button--next bigger-slider {% if product.media.size <= 4 %}hidden{% endif %}"
name="next"
aria-label="{{ 'accessibility.next_slide' | t }}">{% render 'icon-caret' %}</button>
</div>
</slider-quick-view>
{% endif %}
</div>
<div class="product__info-wrapper grid__item">
<div id="ProductInfo-{{ section.id }}" class="product__info-container">
{%- assign product_form_id = 'product-form-' | append: section.id -%}

<h2 class="h2 product__title" {{ section.shopify_attributes }}>
{%- if product.title != blank -%}
{{ product.title | escape }}
{%- else -%}
{{ 'onboarding.product_title' | t }}
{%- endif -%}
</h2>

<div class="no-js-hidden" id="price-{{ section.id }}" role="status" {{ block.shopify_attributes }}>
{%- render 'price', product: product, use_variant: true, show_badges: true, price_class: 'price--large' -%}
</div>
{%- if shop.taxes_included or shop.shipping_policy.body != blank -%}
<div class="product__tax caption rte">
{%- if shop.taxes_included -%}
{{ 'products.product.include_taxes' | t }}
{%- endif -%}
{%- if shop.shipping_policy.body != blank -%}
{{ 'products.product.shipping_policy_html' | t: link: shop.shipping_policy.url }}
{%- endif -%}
</div>
{%- endif -%}
{%- if product != blank -%}
<div {{ block.shopify_attributes }}>
{%- form 'product', product -%}
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
{{ form | payment_terms }}
{%- endform -%}
</div>
{%- endif -%}

<div class="product-form__input product-form__quantity{% if settings.inputs_shadow_vertical_offset != 0 and settings.inputs_shadow_vertical_offset < 0 %} product-form__quantity-top{% endif %}" {{ block.shopify_attributes }}>
<label class="form__label" for="Quantity-{{ section.id }}">
{{ 'products.product.quantity.label' | t }}
</label>
<quantity-input class="quantity">
<button class="quantity__button no-js-hidden" name="minus" type="button">
<span class="visually-hidden">{{ 'products.product.quantity.decrease' | t: product: product.title | escape }}</span>
{% render 'icon-minus' %}
</button>
<input class="quantity__input"
type="number"
name="quantity"
id="Quantity-{{ section.id }}"
min="1"
value="1"
form="{{ product_form_id }}"
>
<button class="quantity__button no-js-hidden" name="plus" type="button">
<span class="visually-hidden">{{ 'products.product.quantity.increase' | t: product: product.title | escape }}</span>
{% render 'icon-plus' %}
</button>
</quantity-input>
</div>

<variant-viewer class="no-js-hidden" data-section="{{ section.id }}" data-url="{{ product.url }}" data-update-url="false" {{ block.shopify_attributes }}>
{%- for option in product.options_with_values -%}
<div class="product-form__input product-form__input--dropdown">
<label class="form__label" for="Option-{{ section.id }}-{{ forloop.index0 }}">
{{ option.name }}
</label>
<div class="select">
<select id="Option-{{ section.id }}-{{ forloop.index0 }}"
class="select__select"
name="options[{{ option.name | escape }}]"
form="{{ product_form_id }}"
>
{%- for value in option.values -%}
<option value="{{ value | escape }}" {% if option.selected_value == value %}selected="selected"{% endif %}>
{{ value }}
</option>
{%- endfor -%}
</select>
{% render 'icon-caret' %}
</div>
</div>
{%- endfor -%}

<script type="application/json">
{{ product.variants | json }}
</script>
</variant-viewer>


<noscript class="product-form__noscript-wrapper-{{ section.id }}">
<div class="product-form__input{% if product.has_only_default_variant %} hidden{% endif %}">
<label class="form__label" for="Variants-{{ section.id }}">{{ 'products.product.product_variants' | t }}</label>
<div class="select">
<select name="id" id="Variants-{{ section.id }}" class="select__select" form="{{ product_form_id }}">
{%- for variant in product.variants -%}
<option
{% if variant == product.selected_or_first_available_variant %}selected="selected"{% endif %}
{% if variant.available == false %}disabled{% endif %}
value="{{ variant.id }}"
>
{{ variant.title }}
{%- if variant.available == false %} - {{ 'products.product.sold_out' | t }}{% endif %}
- {{ variant.price | money | strip_html }}
</option>
{%- endfor -%}
</select>
{% render 'icon-caret' %}
</div>
</div>
</noscript>

<div {{ block.shopify_attributes }}>
{%- if product != blank -%}
<product-form class="product-form">
<div class="product-form__error-message-wrapper" role="alert" hidden>
<svg aria-hidden="true" focusable="false" role="presentation" class="icon icon-error" viewBox="0 0 13 13">
<circle cx="6.5" cy="6.50049" r="5.5" stroke="white" stroke-width="2"/>
<circle cx="6.5" cy="6.5" r="5.5" fill="#EB001B" stroke="#EB001B" stroke-width="0.7"/>
<path d="M5.87413 3.52832L5.97439 7.57216H7.02713L7.12739 3.52832H5.87413ZM6.50076 9.66091C6.88091 9.66091 7.18169 9.37267 7.18169 9.00504C7.18169 8.63742 6.88091 8.34917 6.50076 8.34917C6.12061 8.34917 5.81982 8.63742 5.81982 9.00504C5.81982 9.37267 6.12061 9.66091 6.50076 9.66091Z" fill="white"/>
<path d="M5.87413 3.17832H5.51535L5.52424 3.537L5.6245 7.58083L5.63296 7.92216H5.97439H7.02713H7.36856L7.37702 7.58083L7.47728 3.537L7.48617 3.17832H7.12739H5.87413ZM6.50076 10.0109C7.06121 10.0109 7.5317 9.57872 7.5317 9.00504C7.5317 8.43137 7.06121 7.99918 6.50076 7.99918C5.94031 7.99918 5.46982 8.43137 5.46982 9.00504C5.46982 9.57872 5.94031 10.0109 6.50076 10.0109Z" fill="white" stroke="#EB001B" stroke-width="0.7"/>
</svg>
<span class="product-form__error-message"></span>
</div>

{%- form 'product', product, id: product_form_id, class: 'form', novalidate: 'novalidate', data-type: 'add-to-cart-form' -%}
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}" disabled>
<div class="product-form__buttons">
<button
type="submit"
name="add"
class="product-form__submit button button--full-width {% if section.settings.show_dynamic_checkout and product.selling_plan_groups == empty %}button--secondary{% else %}button--primary{% endif %}"
{% if product.selected_or_first_available_variant.available == false %}disabled{% endif %}
>
<span>
{%- if product.selected_or_first_available_variant.available -%}
{{ 'products.product.add_to_cart' | t }}
{%- else -%}
{{ 'products.product.sold_out' | t }}
{%- endif -%}
</span>
<div class="loading-overlay__spinner hidden">
<svg aria-hidden="true" focusable="false" role="presentation" class="spinner" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<circle class="path" fill="none" stroke-width="6" cx="33" cy="33" r="30"></circle>
</svg>
</div>
</button>
{%- if section.settings.show_dynamic_checkout -%}
{{ form | payment_button }}
{%- endif -%}
</div>
{%- endform -%}
</product-form>
{%- else -%}
<div class="product-form">
<div class="product-form__buttons form">
<button
type="submit"
name="add"
class="product-form__submit button button--full-width button--primary"
disabled
>
{{ 'products.product.sold_out' | t }}
</button>
</div>
</div>
{%- endif -%}
</div>


{%- if product.metafields.reviews.rating.value != blank -%}
{% liquid
assign rating_decimal = 0
assign decimal = product.metafields.reviews.rating.value.rating | modulo: 1
if decimal >= 0.3 and decimal <= 0.7
assign rating_decimal = 0.5
elsif decimal > 0.7
assign rating_decimal = 1
endif
%}
<div class="rating" role="img" aria-label="{{ 'accessibility.star_reviews_info' | t: rating_value: product.metafields.reviews.rating.value, rating_max: product.metafields.reviews.rating.value.scale_max }}">
<span aria-hidden="true" class="rating-star color-icon-{{ settings.accent_icons }}" style="--rating: {{ product.metafields.reviews.rating.value.rating | floor }}; --rating-max: {{ product.metafields.reviews.rating.value.scale_max }}; --rating-decimal: {{ rating_decimal }};"></span>
</div>
<p class="rating-text caption">
<span aria-hidden="true">{{ product.metafields.reviews.rating.value }} / {{ product.metafields.reviews.rating.value.scale_max }}</span>
</p>
<p class="rating-count caption">
<span aria-hidden="true">({{ product.metafields.reviews.rating_count }})</span>
<span class="visually-hidden">{{ product.metafields.reviews.rating_count }} {{ "accessibility.total_reviews" | t }}</span>
</p>
{%- endif -%}


<a href="{{ product.url }}" class="link product__view-details animate-arrow">
{{ 'products.product.view_full_details' | t }}
{% render 'icon-arrow' %}
</a>
</div>
</div>
</div>
<product-modal id="ProductModal-{{ section.id }}" class="product-media-modal media-modal">
<div class="product-media-modal__dialog" role="dialog" aria-label="{{ 'products.modal.label' | t }}" aria-modal="true" tabindex="-1">
<button id="ModalClose-{{ section.id }}" type="button" class="product-media-modal__toggle" aria-label="{{ 'accessibility.close' | t }}">{% render 'icon-close' %}</button>

<div class="product-media-modal__content" role="document" aria-label="{{ 'products.modal.label' | t }}" tabindex="0">
{%- liquid
if product.selected_or_first_available_variant.featured_media != null
assign media = product.selected_or_first_available_variant.featured_media
render 'product-media', media: media, loop: section.settings.enable_video_looping, variant_image: section.settings.hide_variants
endif
-%}

{%- for media in product.media -%}
{%- liquid
if section.settings.hide_variants and media_to_render contains media.id
assign variant_image = true
else
assign variant_image = false
endif

unless media.id == product.selected_or_first_available_variant.featured_media.id
render 'product-media', media: media, loop: section.settings.enable_video_looping, variant_image: variant_image
endunless
-%}
{%- endfor -%}
</div>
</div>
</product-modal>
</div>
</section>

{% javascript %}
if (!customElements.get('product-modal')) {
customElements.define('product-modal', class ProductModal extends ModalDialog {
constructor() {
super();
}

hide() {
super.hide();
}

show(opener) {
super.show(opener);
this.showActiveMedia();
}

showActiveMedia() {
this.querySelectorAll(`[data-media-id]:not([data-media-id="${this.openedBy.getAttribute("data-media-id")}"])`).forEach((element) => {
element.classList.remove('active');
}
)
const activeMedia = this.querySelector(`[data-media-id="${this.openedBy.getAttribute("data-media-id")}"]`);
const activeMediaTemplate = activeMedia.querySelector('template');
const activeMediaContent = activeMediaTemplate ? activeMediaTemplate.content : null;
activeMedia.classList.add('active');
activeMedia.scrollIntoView();

const container = this.querySelector('[role="document"]');
container.scrollLeft = (activeMedia.width - container.clientWidth) / 2;

if (activeMedia.nodeName == 'DEFERRED-MEDIA' && activeMediaContent && activeMediaContent.querySelector('.js-youtube'))
activeMedia.loadContent();
}
});
}
{% endjavascript %}
<script src="{{ 'product-form.js' | asset_url }}" defer="defer"></script>
{%- if first_3d_model -%}
<script type="application/json" id="ProductJSON-{{ product.id }}">
{{ product.media | where: 'media_type', 'model' | json }}
</script>
<script src="{{ 'product-model.js' | asset_url }}" defer></script>
{%- endif -%}

{%- liquid
if product.selected_or_first_available_variant.featured_media
assign seo_media = product.selected_or_first_available_variant.featured_media
else
assign seo_media = product.featured_media
endif
-%}

<script type="application/ld+json">
{
"@context": "http://schema.org/",
"@type": "Product",
"name": {{ product.title | json }},
"url": {{ shop.url | append: product.url | json }},
{% if seo_media -%}
{%- assign media_size = seo_media.preview_image.width | append: 'x' -%}
"image": [
{{ seo_media | img_url: media_size | prepend: "https:" | json }}
],
{%- endif %}
"description": {{ product.description | strip_html | json }},
{% if product.selected_or_first_available_variant.sku != blank -%}
"sku": {{ product.selected_or_first_available_variant.sku | json }},
{%- endif %}
"brand": {
"@type": "Thing",
"name": {{ product.vendor | json }}
},
"offers": [
{%- for variant in product.variants -%}
{
"@type" : "Offer",
{%- if variant.sku != blank -%}
"sku": {{ variant.sku | json }},
{%- endif -%}
"availability" : "http://schema.org/{% if variant.available %}InStock{% else %}OutOfStock{% endif %}",
"price" : {{ variant.price | divided_by: 100.00 | json }},
"priceCurrency" : {{ cart.currency.iso_code | json }},
"url" : {{ shop.url | append: variant.url | json }}
}{% unless forloop.last %},{% endunless %}
{%- endfor -%}
]
}
</script>

<script>
document.addEventListener('DOMContentLoaded', function() {
function isIE() {
const ua = window.navigator.userAgent;
const msie = ua.indexOf('MSIE ');
const trident = ua.indexOf('Trident/');

return (msie > 0 || trident > 0);
}

if (!isIE()) return;
const hiddenInput = document.querySelector('#{{ product_form_id }} input[name="id"]');
const noScriptInputWrapper = document.createElement('div');
const variantSwitcher = document.querySelector('variant-radios[data-section="{{ section.id }}"]') || document.querySelector('variant-selects[data-section="{{ section.id }}"]');
noScriptInputWrapper.innerHTML = document.querySelector('.product-form__noscript-wrapper-{{ section.id }}').textContent;
variantSwitcher.outerHTML = noScriptInputWrapper.outerHTML;

document.querySelector('#Variants-{{ section.id }}').addEventListener('change', function(event) {
hiddenInput.value = event.currentTarget.value;
});
});
</script>

{% if product.media.size > 0 %}
<script src="{{ 'media-gallery.js' | asset_url }}" defer="defer"></script>
{% endif %}

{% schema %}
{
"name": "Quick view template",
"tag": "section",
"class": "section section-featured-product",
"settings": [
{
"type": "header",
"content": "t:sections.featured-product.settings.header.content",
"info": "t:sections.featured-product.settings.header.info"
},
{
"type": "checkbox",
"id": "hide_variants",
"default": false,
"label": "t:sections.main-product.settings.hide_variants.label"
},
{
"type": "checkbox",
"id": "show_dynamic_checkout",
"default": true,
"label": "t:sections.featured-product.blocks.buy_buttons.settings.show_dynamic_checkout.label",
"info": "t:sections.featured-product.blocks.buy_buttons.settings.show_dynamic_checkout.info"
},
{
"type": "text",
"id": "share_label",
"label": "t:sections.featured-product.blocks.share.settings.text.label",
"default": "Share"
}
],
"presets": [
{
"name": "Quick view template"
}
]
}
{% endschema %}

 

4. Next, find the Asset folder, and create a new CSS file. Name it "quick-view." Then open the newly created file and paste the code below.

.card-wrapper {
position: relative;
}

.card-wrapper:hover .buttonViewer {
display: flex;
}

.buttonViewer {
width: 80%;
max-height: 4rem;
height: max-content;
top: 33%;
margin: auto;
left: 0;
right: 0;
position: absolute;
z-index: 4;
display: none;
align-items: center;
justify-content: center;
transition: all .3s ease;
border-radius: 5px;
padding: 3px;
}

.buttonViewer:hover {
transform: scale(1.1);
}

.desktopbutton {
display: flex;
align-items: center;
}

.buttonViewer svg {
max-width: 30px;
width: 90%;
margin: auto;
aspect-ratio: 1;
}

.mobilebutton {
display: none;
}

.productViewContainer {
position: absolute;
height: 65%;
width: 60%;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
display: flex;
flex-direction: column;
overflow-y: auto;
border-radius: 5px;
}

.productViewerClose {
width: 30px;
padding: 5px;
border-radius: 3px;
height: max-content;
top: 15px;
position: sticky;
z-index: 1;
display: flex;
align-self: flex-end;
transition: transform .2s ease;
}

.productViewerClose:hover {
transform: scale(1.1);
}

.productViewerClose > svg {
stroke-width: 1px;
}

.viewerBackground {
position: fixed;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0,0,0,0.7);
z-index: 6;
display: none;
}
.productViewer {
display: flex;
width: 100%;
padding: 0% 5%;
}
.productViewer > section {
width: 100%;
}

.productViewer .product__media-wrapper {
width: 35%;
height: max-content;
}
.productViewer .product__info-wrapper {
padding: 0 !important;
max-width: 65% !important;
}
.productViewer .featured-product {
gap: 5% !important;
}

.largeImage {
width: 70%;
margin: auto;
}

.slider-wrapper img {
width: 100%;
}

.productViewer:not(.product--no-media)>.product__info-wrapper {
padding: 0 0 0 5% !important;
}
.cart__warnings a:after {
z-index: 0;
}
.productViewContainer .is-active {
padding: 0 !important;
display: block !important;
}
.productViewContainer .product__media-item:not(.is-active) {
display: none;
}
.productViewContainer .thumbnail-slider {
display: flex !important;
}
.productViewContainer .thumbnail-list {
margin: auto !important;
}
.productViewContainer .thumbnail-list.slider--tablet-up .thumbnail-list__item.slider__slide {
width: calc(35% - 0.8rem) !important;
aspect-ratio: 1;
}
.productViewContainer .slider-button {
align-self: center;
width: 25px;
}
.slider-button > .iconArrow {
fill: black;
}
.slider-button[disabled] > .iconArrow {
fill: rgba(var(--color-foreground),.3);
cursor: not-allowed;
}

.quick-view-modal {
display: flex !important;
}
.fixed {
overflow: hidden !important;
}

@media only screen and (max-width: 750px) {
.productViewContainer {
width: 100%;
}
}

@media only screen and (max-width: 481px) {
.productViewer .featured-product {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
}
.productViewer .product__media-wrapper {
width: 100% !important;
margin: auto;
max-width: 25rem;
}
.productViewer .product__info-wrapper {
width: 100% !important;
max-width: 100% !important;
}
.productViewer:not(.product--no-media)>.product__info-wrapper {
padding: 0 !important;
}
.mobilebutton {
display: flex;
}
.desktopbutton {
display: none;
}
.desktopbutton > span {
width: 100%;
font-size: clamp(12px, 2vw, 20px);
}
.buttonViewer {
width: 20%;
height: 3rem;
top: 5%;
left: 75%;
display: flex;
}
}

.slider-container {
margin: 0 auto;
width: 90%;
display: flex;
flex-flow: column wrap;
align-items: center;
position: relative;
}

.large-image {
width: 100%;
height: 100%;
position: relative;
overflow:hidden;
}

.large-image-item {
max-width: 100%;
width: 100%;
max-height: 100%;
padding: 0;
position: relative;
margin: 0 auto;
}

.xoomImage {
position: absolute;
top: 0;
left: 0;
}

.large-image-item:not(:first-child){
display: none !important;
}

ul {
padding-inline-start: 0px !important;
}

slider-component {
position: relative;
display: block;
width:100%;
}

.slider-wrapper {
display: flex;
justify-content: center;
width: 100%;
margin-top: 1rem;
}

.slider__slide {
scroll-snap-align: start;
flex-shrink: 0;
}

.product-slider-box {
flex-wrap: inherit;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
width: 100%;
display: flex;
z-index: 1;
margin: 0 !important;
}

.product-slider {
height: 100%;
width: 25%;
padding: 5px;
display: inline-block;
}

.slide-image {
width: 100%;
object-fit: cover;
}

.slider {
scrollbar-color: rgb(var(--color-foreground)) rgba(var(--color-foreground), 0.04);
-ms-overflow-style: none;
scrollbar-width: none;
}

.slider::-webkit-scrollbar {
height: 0.4rem;
width: 0.4rem;
display: none;
}

.no-js .slider {
-ms-overflow-style: auto;
scrollbar-width: auto;
}

.no-js .slider::-webkit-scrollbar {
display: initial;
}

.slider::-webkit-scrollbar-thumb {
background-color: rgb(var(--color-foreground));
border-radius: 0.4rem;
border: 0;
}

.slider::-webkit-scrollbar-track {
background: rgba(var(--color-foreground), 0.04);
border-radius: 0.4rem;
}


/*=== buttons === */
.button-wrapper {
height: 100%;
width: 105%;
position: absolute;
display: flex;
justify-content: space-between;
align-items: center;
}

.slider-container::-webkit-scrollbar {
display: none;
}

.slider-buttons {
display: flex;
align-items: center;
justify-content: center;
}

.slider-button {
color: rgba(var(--color-foreground), 0.75);
background: transparent;
border: none;
cursor: pointer;
width: 44px;
height: 44px;
}

.slider-button:not([disabled]):hover {
color: rgb(var(--color-foreground));
}

.slider-button .icon {
height: 1.2rem;
}

.slider-button[disabled] .icon {
color: rgba(var(--color-foreground), 0.3);
}

.slider-button--next .icon {
margin-right: -0.2rem;
transform: rotate(-90deg) translateX(0.15rem);
}

.slider-button--prev .icon {
margin-left: -0.2rem;
transform: rotate(90deg) translateX(-0.15rem);
}

.slider-button--next:not([disabled]):hover .icon {
transform: rotate(-90deg) translateX(0.15rem) scale(1.07);
}

.slider-button--prev:not([disabled]):hover .icon {
transform: rotate(90deg) translateX(-0.15rem) scale(1.07);
}

/* video button when image is a video type */
.video-btn {
width: 100%;
height: 100%;
position: relative;
}

.v-btn {
top: 2% !important;
left: 2% !important;
}

.active-thumb {
border: 3px solid white;
outline: 2px solid black;
}

/* ==== custom codes in editing the product page ==== */
@media screen and (min-width: 750px) {
.custom-width {
padding: 0 10% !important;
}
}

@media screen and (min-width: 990px) {
.product__media-wrapper {
max-width: 50% !important;
width: calc(50% - 1rem / 2);
}

.product__info-wrapper {
padding-left: 5rem !important;
max-width: 50% !important;
width: calc(50% - 1rem / 2);
}
}

.quickViewButtonContainer {
top: 5px;
margin: 1rem;
display: flex;
justify-content: space-between;
position: sticky;
z-index: 2;
}

.errorMessage {
margin-left: 5px;
font-weight: 600;
}

.navigatorBtn {
border-radius: 3px;
height: 100%;
padding: 1px 3px;
margin-left: 5px;
}

.navigatorBtn:hover {
background: red;
}

.navigatorBtn:disabled {
cursor: not-allowed;
opacity: 0.5;
}

/* hide modal */
.productViewer .product__media-icon {
display: none !important;
}
.video-btn .product__media-icon {
display: flex !important;
}

.productViewer .product__media-toggle:hover {
cursor: pointer !important
}

.productViewer .slider-button > svg {
stroke-width: 1px;
height: 1.2rem !important;
}

 

5. Next, open the global.js file under the same folder, Asset and add the code below. 

 class SliderQuickView extends HTMLElement {
constructor() {
super();
this.slider = this.querySelector('.slider');
this.sliderItems = this.querySelectorAll('.product-slider');
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));
this.sliderItems.forEach(thumb => thumb.addEventListener('click', this.imageId.bind(this)));
}

initPages() {
const sliderItemsToShow = Array.from(this.sliderItems).filter(element => element.clientWidth > 0);
this.sliderLastItem = sliderItemsToShow[sliderItemsToShow.length - 1];
if (sliderItemsToShow.length === 0) return;
this.slidesPerPage = Math.floor(this.slider.clientWidth / sliderItemsToShow[0].clientWidth);
this.totalPages = sliderItemsToShow.length - this.slidesPerPage + 1;
this.update();
}

update() {
if (!this.pageCount || !this.pageTotal) return;
this.currentPage = Math.round(this.slider.scrollLeft / this.sliderLastItem.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.slider.clientWidth): this.slider.scrollLeft - (this.slider.clientWidth);
this.slider.scrollTo({
left: Math.floor(slideScrollPosition)
});

this.currSlide = Math.round(slideScrollPosition / this.slider.clientWidth);
if (this.currSlide === 0 ) {
this.prevButton.setAttribute('disabled', true);
} else {
this.prevButton.removeAttribute('disabled');
}
if (this.currSlide === this.totalPages / 3 ) {
this.nextButton.setAttribute('disabled', true);
} else {
this.nextButton.removeAttribute('disabled');
}
}

imageId(e) {
this.clickedImage = e.currentTarget.dataset.thumbId;
this.imageUpdate(this.clickedImage );
this.thumbUpdate(this.clickedImage);
}

imageUpdate(image){
this.newImage = this.querySelector(`[data-media-id="${image}"]`);
this.parentData = this.newImage.parentElement;
this.parentData.prepend(this.newImage);
this.newImage.style.display = "block";
}

thumbUpdate(variantImage) {
this.activeThumb = this.querySelector(`[data-thumb-id='${variantImage}']`);

this.sliderItems.forEach(slider => slider.querySelector('img').classList.remove('active-thumb'));
this.activeThumb.scrollIntoView({block: "nearest", inline: "center"});
this.activeThumb.querySelector('img').classList.add('active-thumb');
}
}

customElements.define('slider-quick-view', SliderQuickView);


class ButtonViewer extends HTMLElement {
constructor() {
super();

this.productViewer = document.querySelector('product-viewer');
this.attachQuickView();
this.addEventListener('click', this.passId.bind(this));
this.prevProduct;
this.nextProduct;
this.buttons = document.querySelectorAll('button-viewer');
this.indexLength;
}
attachQuickView() {
this.closest('ul').querySelectorAll('button-viewer').forEach((button, index) => {
button.setAttribute('data-view-index', index);
});
this.indexLength = this.closest('ul').querySelectorAll('button-viewer').length;
this.innerHTML = this.productViewer.querySelector('#buttonViewer').innerHTML;
this.insertAdjacentHTML('afterbegin', this.domParser(this.innerHTML));
}

domParser(html) {
return new DOMParser()
.parseFromString(html, 'text/html')
.querySelector('body').innerHTML;
}

passId(event) {
this.productHandle = event.currentTarget.dataset.product;
this.productViewer.openViewer( this.productHandle);

this.assignIndex = event.currentTarget.dataset.viewIndex;
this.productViewer.currentViewer(this.closest('ul'), this.assignIndex, this.indexLength);
}
}

customElements.define('button-viewer', ButtonViewer);


class ProductViewer extends HTMLElement {
constructor() {
super();
this.productViewerContainer= this.querySelector('.viewerBackground');
this.productViewer = this.querySelector('.productViewer');
this.buttonViewerClose = this.querySelector('.productViewerClose');
this.buttonViewerClose.addEventListener('click', this.closeViewer.bind(this));

this.prevButton = this.querySelector('.prevNavigator');
this.nextButton = this.querySelector('.nextNavigator');
this.errorContainer = this.querySelector('.errorMessage');
this.prevButton.addEventListener('click', this.onButtonClick.bind(this));
this.nextButton.addEventListener('click', this.onButtonClick.bind(this));

this.changeEvent = new Event('change', { bubbles: true })
this.current;
this.parent;
this.indexLength;
}

openViewer(productHandle) {
this.fetchProduct(productHandle);
document.body.classList.toggle('fixed');
this.productViewerContainer.classList.toggle('quick-view-modal ');
}

fetchProduct(productHandle) {
fetch(`/products/${productHandle}?section_id=quick-view-template`)
.then(response => response.text())
.then(data => {
this.parsed = this.domParser(data);
this.productViewer.innerHTML = this.parsed;
});
}

domParser(html) {
return new DOMParser()
.parseFromString(html, 'text/html')
.querySelector('body').innerHTML;
}

closeViewer() {
this.productViewerContainer.classList.toggle('quick-view-modal ');
document.body.classList.toggle('fixed');
}

onButtonClick(event) {
event.preventDefault();
event.target.getAttribute('name') == 'next' ? this.current = this.current + 1 : this.current = this.current - 1;

if(this.current == 0) {
this.errorMessage = 'This is the first product.';
this.prevButton.disabled = true;
}
else if(this.current == this.indexLength) {
this.errorMessage = 'This is the last product.';
this.nextButton.disabled = true;
}
else {
this.errorMessage = ' ';
this.prevButton.disabled = false;
this.nextButton.disabled = false;
}
this.errorContainer.innerHTML = this.errorMessage;
this.navigated = this.parent.querySelector(`[data-view-index='${this.current}']`);
if(!this.navigated) return;
this.fetchProduct(this.navigated.dataset.product);
}

currentViewer(currentParent, currentIndex, indexLength) {
this.parent = currentParent;
this.current = (currentIndex * 1);
this.indexLength = indexLength - 1;
}
}

customElements.define('product-viewer', ProductViewer);

class variantViewer extends VariantSelects {
constructor() {
super();
this.productViewer = document.querySelector('slider-quick-view');
}

updateMedia() {
if (!this.currentVariant) return;
if (!this.currentVariant.featured_media) return;

this.variantMedia = this.currentVariant.featured_media.id;
this.productViewer.imageUpdate(this.variantMedia);
this.productViewer.thumbUpdate(this.variantMedia);
}
}

customElements.define('variant-viewer', variantViewer);

 

6. We need to call this quick-view in the product card. Go to Snippet folder, if you have Dawn Theme version 2.5 above, open the card-product.liquid. Find the class "card-wrapper", then paste the code, after the line.

  <button-viewer class="quickViewBtn" data-product="{{card_product.handle}}" ></button-viewer>

 

For Dawn Theme 2.5 and lower, follow same instruction but paste the code below instead. 
   <button-viewer class="quickViewBtn" data-product="{{product_card_product.handle}}" ></button-viewer>

 

7. Last thing, go to the theme.liquid folder on top. Then find the  {% section 'announcement-bar' %},  paste the code above it. 

 {% section 'quick-view' %}

 

That's it. You can customize the the quick-view section if you go to Theme Editor. The Section should be at the very top. (",)

Copied!
Back to blog

Leave a comment