"Notify Me Button" Snippet
Read this article to learn more about the snippet usage.
Usage:
{% render 'back-in-stock' %}
Code:
{% comment %}
Back In Stock snippet (for themes where the app does not support app blocks).
Usage (minimal):
{% render 'back-in-stock', product: product %}
Usage (overriding any text/option):
{% render 'back-in-stock',
product: product,
button_text: 'Notify me when available',
already_subscribed_text: 'You are subscribed on this product',
disable_already_subscribed: false,
modal_title: 'Notify me when available',
modal_subtitle: '<p>Get notified when product back in stock.</p>',
email_placeholder: 'Your Email',
prefill_customer_email: true,
show_variant_selector: true,
modal_description: 'Your info stays private and will never be shared with anyone.',
subscribe_button_text: 'Notify me when available',
thank_you: '<p>Thank you!</p>',
button_text_signed: "You're signed up! We'll notify you.",
we_will_notify: '<p>We will notify you when the product is available.</p>',
error_email_text: 'Your e-mail address is wrong.',
error_text: 'Something went wrong, please try again later.'
%}
Required prop: product
All text props are optional. When a text prop is omitted, the default is read
from the translation files (locales/en.default.json) via the `t` filter, e.g.
'back_in_stock.settings.button_text.default'.
Note: the custom element, CSS classes and JS are namespaced as
`r-back-in-stock-snippet` so they never collide with the official app block
(`r-back-in-stock`) if both are present on the same page.
{% endcomment %}
{% comment %}
Back In Stock snippet (for themes where the app does not support app blocks).
Usage (minimal):
{% render 'back-in-stock', product: product %}
Usage (overriding any text/option):
{% render 'back-in-stock',
product: product,
button_text: 'Notify me when available',
already_subscribed_text: 'You are subscribed on this product',
disable_already_subscribed: false,
modal_title: 'Notify me when available',
modal_subtitle: '<p>Get notified when product back in stock.</p>',
email_placeholder: 'Your Email',
prefill_customer_email: true,
show_variant_selector: true,
modal_description: 'Your info stays private and will never be shared with anyone.',
subscribe_button_text: 'Notify me when available',
thank_you: '<p>Thank you!</p>',
button_text_signed: "You're signed up! We'll notify you.",
we_will_notify: '<p>We will notify you when the product is available.</p>',
error_email_text: 'Your e-mail address is wrong.',
error_text: 'Something went wrong, please try again later.'
%}
Required prop: product
All text props are optional. When a text prop is omitted, the default is read
from the translation files (locales/en.default.json) via the `t` filter, e.g.
'back_in_stock.settings.button_text.default'.
Note: the custom element, CSS classes and JS are namespaced as
`r-back-in-stock-snippet` so they never collide with the official app block
(`r-back-in-stock`) if both are present on the same page.
{% endcomment %}
{%- liquid
assign p = product | default: product
assign _content_style = content_style | default: 'text'
# Prop defaults (fall back to translations in locales/en.default.json)
assign _button_text = 'back_in_stock.settings.button_text.default' | t
if button_text != blank
assign _button_text = button_text
endif
assign _already_subscribed_text = 'back_in_stock.settings.already_subscribed_text.default' | t
if already_subscribed_text != blank
assign _already_subscribed_text = already_subscribed_text
endif
assign _modal_title = 'back_in_stock.settings.modal_title.default' | t
if modal_title != blank
assign _modal_title = modal_title
endif
assign _email_placeholder = 'back_in_stock.settings.email_placeholder.default' | t
if email_placeholder != blank
assign _email_placeholder = email_placeholder
endif
assign _subscribe_button_text = 'back_in_stock.settings.subscribe_button_text.default' | t
if subscribe_button_text != blank
assign _subscribe_button_text = subscribe_button_text
endif
assign _button_text_signed = 'back_in_stock.settings.button_text_signed.default' | t
if button_text_signed != blank
assign _button_text_signed = button_text_signed
endif
assign _modal_subtitle = 'back_in_stock.settings.modal_subtitle.default' | t
if modal_subtitle != blank
assign _modal_subtitle = modal_subtitle
endif
assign _modal_description = 'back_in_stock.settings.modal_description.default' | t
if modal_description != blank
assign _modal_description = modal_description
endif
assign _thank_you = 'back_in_stock.settings.thank_you.default' | t
if thank_you != blank
assign _thank_you = thank_you
endif
assign _we_will_notify = 'back_in_stock.settings.we_will_notify.default' | t
if we_will_notify != blank
assign _we_will_notify = we_will_notify
endif
assign _error_email_text = 'back_in_stock.settings.error_email_text.default' | t
if error_email_text != blank
assign _error_email_text = error_email_text
endif
assign _already_subscribed_error_text = 'back_in_stock.settings.already_subscribed_error_text.default' | t
if already_subscribed_error_text != blank
assign _already_subscribed_error_text = already_subscribed_error_text
endif
assign _error_text = 'back_in_stock.settings.error_text.default' | t
if error_text != blank
assign _error_text = error_text
endif
# checkbox props: nil => default true/false
if prefill_customer_email == null
assign _prefill_customer_email = true
else
assign _prefill_customer_email = prefill_customer_email
endif
if show_variant_selector == null
assign _show_variant_selector = true
else
assign _show_variant_selector = show_variant_selector
endif
if disable_already_subscribed == null
assign _disable_already_subscribed = false
else
assign _disable_already_subscribed = disable_already_subscribed
endif
-%}
{%- assign is_app_blocked = shop.metafields.back_in_stock.app_blocked -%}
{%- assign public_admin_settings = shop.metafields.refactor_back_in_stock.settings.value -%}
{%- assign subscribed_variants = customer.metafields.refactor_back_in_stock['subscribed-variants'].value -%}
{%- assign current_variant_id = p.selected_or_first_available_variant.id | append: '' -%}
{%- assign customer_is_subscribed = false -%}
{%- assign subscribed_ids = '' -%}
{%- if subscribed_variants -%}
{%- for variant in subscribed_variants -%}
{%- assign subscribed_ids = subscribed_ids | append: variant[0] | append: ',' -%}
{%- endfor -%}
{%- endif -%}
{%- if subscribed_variants[current_variant_id] -%}
{%- assign customer_is_subscribed = true -%}
{%- endif -%}
{%- liquid
assign locale_code = request.locale.iso_code | split: '-' | first | strip
assign dir_value = 'ltr'
if locale_code == 'ar' or locale_code == 'he' or locale_code == 'fa' or locale_code == 'ur'
assign dir_value = 'rtl'
endif
-%}
{%- unless is_app_blocked and request.design_mode == false -%}
<style>
.r-back-in-stock-snippet { display: none; }
.r-back-in-stock-snippet.is-active { display: block; }
.r-back-in-stock-snippet__notify-btn {
display: flex;
justify-content: center;
align-items: center;
gap: 6px;
cursor: pointer;
}
.r-back-in-stock-snippet__notify-btn--disabled { pointer-events: none; }
.r-back-in-stock-snippet__form { display: flex; flex-direction: column; }
.r-back-in-stock-snippet__form.is-loading { opacity: 0.5; pointer-events: none; }
.r-back-in-stock-snippet__text { font-size: 16px; font-weight: 600; margin: 0; }
.r-back-in-stock-snippet__label {
font-size: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.r-back-in-stock-snippet__modal {
display: flex;
position: fixed;
top: 0; bottom: 0; left: 0; right: 0;
align-items: center;
justify-content: center;
z-index: 10000;
}
.r-back-in-stock-snippet__modal p { margin: 0; }
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-overlay {
display: flex;
width: 100%;
height: 100%;
background: rgba(27, 27, 27, 0.6);
position: absolute;
z-index: 1;
cursor: pointer;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content {
margin: 0 15px;
border-radius: 4px;
padding: 24px;
position: relative;
z-index: 2;
background: #fff;
width: 480px;
display: flex;
flex-direction: column;
gap: 24px;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content header {
padding-bottom: 16px;
border-bottom: 1px solid #d7d7d7;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content h3 {
margin: 0 0 2px 0;
font-weight: 700;
font-size: 20px;
line-height: 1.4;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__modal-text {
margin: 0;
font-size: 14px;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__name {
font-weight: 700;
font-size: 16px;
line-height: 1.4;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__variant-select {
font-size: 14px;
padding: 16px 14px;
border: 1px solid #888;
border-radius: 4px;
outline: none;
box-shadow: none;
width: 100%;
appearance: none;
-webkit-appearance: none;
font-family: inherit;
cursor: pointer;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='currentColor' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 1rem center;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__input {
font-size: 14px;
padding: 16px 14px;
border: 1px solid #888;
border-radius: 4px;
outline: none;
box-shadow: none;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__input-info {
font-size: 13px;
font-weight: normal;
line-height: 1;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__button {
margin-top: 24px;
width: 100%;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks {
max-width: 280px;
margin: 0 auto 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks img,
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks .r-back-in-stock-snippet__checkmark { max-width: 100px; height: auto; }
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks-texts {
display: flex;
flex-direction: column;
gap: 4px;
text-align: center;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks-texts .r-back-in-stock-snippet__thanks-header {
font-size: 20px;
font-weight: 700;
margin: 0;
line-height: 1.4;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks-texts .r-back-in-stock-snippet__thanks-header p {
text-align: center !important;
}
.r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__thanks-texts .r-back-in-stock-snippet__thanks-info {
font-size: 16px;
font-weight: 400;
margin: 0;
line-height: 1.4;
}
.r-back-in-stock-snippet__label:has(.r-back-in-stock-snippet__message.is-error, .r-back-in-stock-snippet__message.is-success) .r-back-in-stock-snippet__input {
border-color: #b71600;
color: #b71600;
}
.r-back-in-stock-snippet__label:has(.r-back-in-stock-snippet__message.is-error, .r-back-in-stock-snippet__message.is-success) .r-back-in-stock-snippet__input::placeholder {
color: #b71600;
}
.r-back-in-stock-snippet__message {
font-size: 14px;
font-weight: 400;
display: none;
}
.r-back-in-stock-snippet__message.is-error {
display: block;
color: #b71600;
}
[dir="rtl"].r-back-in-stock-snippet__modal .r-back-in-stock-snippet__modal-content .r-back-in-stock-snippet__variant-select {
background-position: left 1rem center;
}
.r-back-in-stock-snippet__blocked-message {
display: flex;
align-items: center;
justify-content: center;
margin-top: 8px;
}
.r-back-in-stock-snippet__blocked-message span {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 8px 12px;
border: 1px solid #b71600;
color: #b71600;
}
/* helpers (from styles.css) */
.r-back-in-stock-snippet .hidden,
.r-back-in-stock-snippet__modal.hidden { display: none !important; }
.r-back-in-stock-snippet__modal .close-btn {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
cursor: pointer;
display: inline-block;
}
[dir="rtl"] .r-back-in-stock-snippet__modal .close-btn { right: auto; left: 8px; }
.r-back-in-stock-snippet__modal .close-btn::before,
.r-back-in-stock-snippet__modal .close-btn::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 13px;
height: 1.5px;
background-color: rgb(27, 27, 27);
transform-origin: center;
transition: background-color 0.2s ease;
}
.r-back-in-stock-snippet__modal .close-btn::before { transform: translate(-50%, -50%) rotate(45deg); }
.r-back-in-stock-snippet__modal .close-btn::after { transform: translate(-50%, -50%) rotate(-45deg); }
.r-back-in-stock-snippet__modal .close-btn:hover::before,
.r-back-in-stock-snippet__modal .close-btn:hover::after { background-color: #333; }
</style>
<r-back-in-stock-snippet
dir="{{ dir_value }}"
class="r-back-in-stock-snippet {% unless p.selected_or_first_available_variant.available %}is-active{% endunless %}"
data-product-id="{{ p.id }}"
data-subscribed-variants="{{ subscribed_ids }}"
data-disable-already-subscribed="{{ _disable_already_subscribed }}"
data-show-if-continue-selling="{{ public_admin_settings.showIfContinueSelling }}"
data-show-if-track-quantity-disabled="{{ public_admin_settings.showIfTrackQuantityDisabled }}"
>
<div
class="r-back-in-stock-snippet__notify-btn r-back-in-stock-snippet__notify-btn--{{ _content_style }} {% if _disable_already_subscribed %}r-back-in-stock-snippet__notify-btn--disabled{% endif %}"
>
{% if _content_style == 'icon' %}
<span class="r-back-in-stock-snippet__notify-icon" aria-hidden="true">
{% endif %}
<svg
class="r-back-in-stock-snippet__icon"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<path d="M21.75 6.75V17.25C21.75 17.6478 21.592 18.0294 21.3107 18.3107C21.0294 18.592 20.6478 18.75 20.25 18.75H3.75C3.35218 18.75 2.97064 18.592 2.68934 18.3107C2.40804 18.0294 2.25 17.6478 2.25 17.25V6.75M21.75 6.75C21.75 6.35218 21.592 5.97064 21.3107 5.68934C21.0294 5.40804 20.6478 5.25 20.25 5.25H3.75C3.35218 5.25 2.97064 5.40804 2.68934 5.68934C2.40804 5.97064 2.25 6.35218 2.25 6.75M21.75 6.75L12.8535 12.909C12.6027 13.0825 12.305 13.1755 12 13.1755C11.695 13.1755 11.3973 13.0825 11.1465 12.909L2.25 6.75" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
{% if _content_style == 'icon' %}
</span>
{% endif %}
<span
class="r-back-in-stock-snippet__unsigned"
data-default-text="{{ _button_text }}"
data-subscribed-text="{{ _already_subscribed_text }}"
>
{% if _content_style == 'text' %}
<span class="r-back-in-stock-snippet__unsigned-label">
{% if customer_is_subscribed %}
{{ _already_subscribed_text }}
{% else %}
{{ _button_text }}
{% endif %}
</span>
{% endif %}
</span>
<span class="r-back-in-stock-snippet__signed hidden">
{% if _content_style == 'icon' %}
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M20 6L9 17L4 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
{% else %}
{{ _button_text_signed }}
{% endif %}
</span>
</div>
{% if is_app_blocked %}
<div class="r-back-in-stock-snippet__blocked-message">
<span
>R+ Back In Stock is inactive.
<br>
Please activate a subscription to continue using the app.</span
>
</div>
{% endif %}
<div dir="{{ dir_value }}" class="r-back-in-stock-snippet__modal hidden">
<div class="r-back-in-stock-snippet__modal-overlay"></div>
<div class="r-back-in-stock-snippet__modal-content">
<span class="close-btn"></span>
<header>
<h3>{{- _modal_title -}}</h3>
{% if _modal_subtitle != empty and _modal_subtitle != null %}
<p class="r-back-in-stock-snippet__modal-text">{{- _modal_subtitle -}}</p>
{% endif %}
</header>
<form class="r-back-in-stock-snippet__form js-r-back-in-stock-snippet__form">
{% unless _show_variant_selector and p.variants.size > 1 %}
{% assign fallback_variant = null %}
{% for variant in p.variants %}
{% unless variant.available %}
{% assign fallback_variant = variant %}
{% break %}
{% endunless %}
{% endfor %}
<input name="variant_id" value="{{ fallback_variant.id }}" type="hidden">
{% endunless %}
{% if customer %}
<input name="customer_id" value="{{ customer.id }}" type="hidden">
{% endif %}
<label class="r-back-in-stock-snippet__label">
<span class="r-back-in-stock-snippet__name">
{{- p.title -}}
-
{{- p.selected_or_first_available_variant.title -}}
</span>
{% if _show_variant_selector %}
{% assign variants = p.variants %}
{% if variants.size > 1 %}
<div class="r-back-in-stock-snippet__variant-selector">
<select name="variant_id" class="r-back-in-stock-snippet__variant-select">
{% assign first_found = false %}
{% for variant in variants %}
{% assign show_variant = false %}
{% if variant.available == false %}
{% assign show_variant = true %}
{% endif %}
{% if public_admin_settings.showIfContinueSelling
and variant.inventory_policy == 'continue'
and variant.inventory_quantity < 1
%}
{% assign show_variant = true %}
{% endif %}
{% if public_admin_settings.showIfTrackQuantityDisabled and variant.inventory_management == nil %}
{% assign show_variant = true %}
{% endif %}
{% if show_variant %}
<option
value="{{ variant.id }}"
{% if variant.id == p.selected_or_first_available_variant.id %}
selected
{% assign first_found = true %}
{% elsif first_found == false %}
selected
{% assign first_found = true %}
{% endif %}
>
{{ variant.title }}
</option>
{% endif %}
{% endfor %}
</select>
</div>
{% endif %}
{% endif %}
<input
name="email"
class="r-back-in-stock-snippet__input"
autocomplete="email"
type="text"
placeholder="{{ _email_placeholder }}"
{% if _prefill_customer_email and customer %}
value="{{ customer.email }}"
{% endif %}
>
<span class="r-back-in-stock-snippet__message js-r-back-in-stock-snippet__message"></span>
{% if _modal_description != empty and _modal_description != null %}
<span class="r-back-in-stock-snippet__input-info">{{- _modal_description -}}</span>
{% endif %}
</label>
<button type="submit" class="button r-back-in-stock-snippet__button">
{{- _subscribe_button_text -}}
</button>
</form>
<div class="r-back-in-stock-snippet__thanks hidden">
<svg
class="r-back-in-stock-snippet__checkmark"
xmlns="http://www.w3.org/2000/svg"
width="100"
height="100"
viewBox="0 0 100 100"
fill="none"
role="img"
aria-label="success"
>
<circle cx="50" cy="50" r="46" fill="#E7F6EC" stroke="#2E7D32" stroke-width="3"/>
<path d="M30 51.5L44 65.5L71 36.5" stroke="#2E7D32" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<div class="r-back-in-stock-snippet__thanks-texts">
<div class="r-back-in-stock-snippet__thanks-header">
{% if _thank_you != empty and _thank_you != null %}
{{- _thank_you -}}
{% endif %}
</div>
<div class="r-back-in-stock-snippet__thanks-info">
{% if _we_will_notify != empty and _we_will_notify != null %}
{{- _we_will_notify -}}
{% endif %}
</div>
</div>
</div>
</div>
</div>
{%- assign _error_email_text_final = _error_email_text -%}
{%- assign _already_subscribed_error_text_final = _already_subscribed_error_text -%}
{%- assign _error_text_final = _error_text -%}
{%- if _error_email_text_final contains 'missing translation' -%}
{%- assign _error_email_text_final = "Your e-mail address is wrong." -%}
{%- endif -%}
{%- if _already_subscribed_error_text_final contains 'missing translation' -%}
{%- assign _already_subscribed_error_text_final = "You are already subscribed to this product." -%}
{%- endif -%}
{%- if _error_text_final contains 'missing translation' -%}
{%- assign _error_text_final = "Something went wrong, reload the page and try again." -%}
{%- endif -%}
<template class="t-r-back-in-stock-snippet__error-email">{{- _error_email_text_final -}}</template>
<template class="t-r-back-in-stock-snippet__error-already-subscribed">{{- _already_subscribed_error_text_final -}}</template>
<template class="t-r-back-in-stock-snippet__error">{{- _error_text_final -}}</template>
<template class="t-r-back-in-stock-snippet__variants">
[
{% for variant in p.variants %}
{
"id": {{ variant.id | json }},
"name": {{ variant.name | json }},
"title": {{ variant.title | json }},
"available": {{ variant.available | json }},
"inventory_quantity": {{ variant.inventory_quantity | json }},
"inventory_policy": {{ variant.inventory_policy | json }},
"inventory_management": {{ variant.inventory_management | json }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
</template>
</r-back-in-stock-snippet>
<script>
(() => {
function initRBackInStock() {
if (customElements.get('r-back-in-stock-snippet')) return;
class RBackInStock extends HTMLElement {
constructor() {
super();
this.boundedThemeAddToCartHandler = this.themeAddToCartHandler.bind(this);
this.boundedFormHandler = this.formHandler.bind(this);
this.boundedShowPopup = this.showPopup.bind(this);
this.boundedHandlePopupClick = this.handlePopupClick.bind(this);
this.boundedUpdateVariantId = this.updateVariantId.bind(this);
this.boundedResetMessage = this.resetMessage.bind(this);
this.boundedSyncVariantSelect = this.syncVariantSelect.bind(this);
this.signedVariants = {};
this.sessionSignedVariants = {};
}
async connectedCallback() {
this.initElements();
await this.updateVariantId();
this.addEventListeners();
}
disconnectedCallback() {
this.removeEventListeners();
}
initElements() {
this.labelNameEl = this.querySelector('.r-back-in-stock-snippet__name');
this.form = this.querySelector('.js-r-back-in-stock-snippet__form');
this.unsignedText = this.querySelector('.r-back-in-stock-snippet__unsigned');
this.signedText = this.querySelector('.r-back-in-stock-snippet__signed');
this.thanksEl = this.querySelector('.r-back-in-stock-snippet__thanks');
this.inputEl = this.querySelector('.r-back-in-stock-snippet__input');
this.errorEmailText = this.querySelector('.t-r-back-in-stock-snippet__error-email');
this.errorAlreadySubscribedText = this.querySelector('.t-r-back-in-stock-snippet__error-already-subscribed');
this.errorText = this.querySelector('.t-r-back-in-stock-snippet__error');
this.message = this.querySelector('.js-r-back-in-stock-snippet__message');
this.addToCartForm = document.querySelector("form[action='/cart/add']");
this.productId = this.dataset.productId ?? null;
this.variantsJSON = JSON.parse(this.querySelector('.t-r-back-in-stock-snippet__variants').innerHTML);
this.shopifyVariantIdEl = document.querySelector("input[name='id']");
this.btnTrigger = this.querySelector('.r-back-in-stock-snippet__notify-btn');
this.popupEl = this.querySelector('.r-back-in-stock-snippet__modal');
this.variantSelect = this.popupEl?.querySelector('.r-back-in-stock-snippet__variant-select');
if (this.popupEl) {
document.body.appendChild(this.popupEl);
}
const subscribedVariantsAttr = this.dataset.subscribedVariants;
if (subscribedVariantsAttr) {
subscribedVariantsAttr
.split(',')
.filter(Boolean)
.forEach((id) => {
this.signedVariants[Number(id)] = true;
});
}
this.observeVariantValue();
}
observeVariantValue() {
const el = this.shopifyVariantIdEl;
if (!el || el.__rSnippetValuePatched) {
return;
}
const prototypeDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
Object.defineProperty(el, 'value', {
get: function () {
return prototypeDescriptor.get.call(this);
},
set: (newValue) => {
prototypeDescriptor.set.call(el, newValue);
this.boundedUpdateVariantId();
},
configurable: true,
});
el.__rSnippetValuePatched = true;
}
addEventListeners() {
this.form?.addEventListener('submit', this.boundedFormHandler);
this.addToCartForm?.addEventListener('change', this.boundedThemeAddToCartHandler);
this.btnTrigger?.addEventListener('click', this.boundedShowPopup);
this.popupEl?.addEventListener('click', this.boundedHandlePopupClick);
this.inputEl?.addEventListener('input', this.boundedResetMessage);
document.addEventListener('variant:change', this.boundedUpdateVariantId);
this.shopifyVariantIdEl?.addEventListener('change', this.boundedUpdateVariantId);
this.variantSelect?.addEventListener('change', this.boundedSyncVariantSelect);
}
removeEventListeners() {
this.form?.removeEventListener('submit', this.boundedFormHandler);
this.addToCartForm?.removeEventListener('change', this.boundedThemeAddToCartHandler);
this.btnTrigger?.removeEventListener('click', this.boundedShowPopup);
this.popupEl?.removeEventListener('click', this.boundedHandlePopupClick);
this.inputEl?.removeEventListener('input', this.boundedResetMessage);
document.removeEventListener('variant:change', this.boundedUpdateVariantId);
this.shopifyVariantIdEl?.removeEventListener('change', this.boundedUpdateVariantId);
this.variantSelect?.removeEventListener('change', this.boundedSyncVariantSelect);
}
async handlePopupClick(e) {
const target = e?.target;
const classList = target?.classList;
if (!classList) {
return;
}
const isCloseModal =
classList.contains('r-back-in-stock-snippet__modal-overlay') || classList.contains('close-btn');
if (isCloseModal) {
this.hidePopup();
}
}
showPopup() {
if (!this.popupEl) return;
this.variantId = this.getVariantId();
if (this.variantSelect) {
this.variantSelect.value = this.variantId;
}
const variantData = this.variantsJSON.find((el) => el.id === parseInt(this.variantId, 10));
if (this.labelNameEl && variantData) {
this.labelNameEl.innerText = variantData.name;
}
this.hideThanksBlock();
this.resetMessage();
this.popupEl.classList.remove('hidden');
}
hidePopup() {
if (!this.popupEl) return;
this.popupEl.classList.add('hidden');
this.hideThanksBlock();
this.resetMessage();
}
updateVariantId() {
this.variantId = this.getVariantId();
this.updateButton();
}
updateButton() {
this.updateButtonText();
this.updateButtonSettings();
}
updateButtonText() {
const label = this.unsignedText.querySelector('.r-back-in-stock-snippet__unsigned-label');
if (this.sessionSignedVariants[this.variantId]) {
this.signedText.classList.remove('hidden');
this.unsignedText.classList.add('hidden');
} else {
this.signedText.classList.add('hidden');
this.unsignedText.classList.remove('hidden');
if (label) {
label.textContent = this.signedVariants[this.variantId]
? this.unsignedText.dataset.subscribedText
: this.unsignedText.dataset.defaultText;
}
}
}
updateButtonSettings() {
this.updateVisibility();
if (this.dataset.disableAlreadySubscribed === 'true') {
if (this.signedVariants[this.variantId]) {
this.btnTrigger.classList.add('r-back-in-stock-snippet__notify-btn--disabled');
} else {
this.btnTrigger.classList.remove('r-back-in-stock-snippet__notify-btn--disabled');
}
}
}
getVariantId() {
if (this.variantSelect && !this.popupEl.classList.contains('hidden')) {
return Number(this.variantSelect.value);
}
const shopifyVariantId = this.shopifyVariantIdEl?.value;
const customVariantIdEl = this.querySelector(".r-back-in-stock-snippet__form input[name='variant_id']");
const params = new URLSearchParams(window.location.search);
const variantIdFromParams = params.get('variant');
return Number(shopifyVariantId || variantIdFromParams || customVariantIdEl?.value);
}
themeAddToCartHandler() {
this.updateVariantId();
this.resetMessage();
}
resetMessage() {
this.message.innerHTML = '';
this.message.classList.remove('is-error', 'is-success');
}
formHandler(event) {
event.preventDefault();
const payload = Object.fromEntries(new FormData(this.form));
const emailValidRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (typeof payload.email === 'string' && payload.email.match(emailValidRegex)) {
this.sendSubscriptionRequest(payload);
} else {
this.showMessage(this.errorEmailText.innerHTML);
return;
}
}
async sendSubscriptionRequest(payload) {
this.form.classList.add('is-loading');
const variantId = this.getVariantId() || payload.variant_id;
const res = await window.refactor_apps.back_in_stock.subscribeOnTheProduct({
productId: this.productId,
variantId: variantId,
email: payload.email,
customerId: payload.customer_id,
});
if (res.success) {
this.showThanksBlock();
this.signedVariants[this.variantId] = true;
this.sessionSignedVariants[this.variantId] = true;
this.updateButtonText();
} else if (res.message?.already_subscribed) {
const errors = this.errorAlreadySubscribedText.innerHTML;
this.showMessage(errors);
} else {
const errors = this.errorText.innerHTML;
this.showMessage(errors);
}
this.form.classList.remove('is-loading');
}
showThanksBlock() {
this.thanksEl.classList.remove('hidden');
this.form.classList.add('hidden');
}
hideThanksBlock() {
this.thanksEl.classList.add('hidden');
this.form.classList.remove('hidden');
}
showMessage(message) {
this.message.innerHTML = message;
this.message.classList.add('is-error');
}
syncVariantSelect() {
const selectedId = Number(this.variantSelect.value);
this.variantId = selectedId;
const variantData = this.variantsJSON.find((el) => el.id === selectedId);
if (this.labelNameEl && variantData) {
this.labelNameEl.innerText = variantData.name;
}
this.hideThanksBlock();
this.resetMessage();
}
shouldShowSubscribeButton(variant) {
if (!variant) return false;
const showIfContinueSelling = this.dataset.showIfContinueSelling === 'true';
const showIfTrackQuantityDisabled = this.dataset.showIfTrackQuantityDisabled === 'true';
const inventoryQuantity = Number(variant.inventory_quantity ?? 0);
if (!variant.available) {
return true;
}
if (showIfContinueSelling && inventoryQuantity < 1 && variant.inventory_policy === 'continue') {
return true;
}
if (showIfTrackQuantityDisabled && !variant.inventory_management) {
return true;
}
return false;
}
updateVisibility() {
const variantData = this.variantsJSON.find((variant) => variant.id === Number(this.variantId));
if (this.shouldShowSubscribeButton(variantData)) {
this.classList.add('is-active');
} else {
this.classList.remove('is-active');
}
}
}
customElements.define('r-back-in-stock-snippet', RBackInStock);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initRBackInStock);
} else {
initRBackInStock();
}
})();
</script>
{%- endunless -%}
Do you need help?
If you have any questions or run into issues, please contact us — we’re happy to help