-
-
Save husseyexplores/2e6d18a934387cc9150c44c9085f708c to your computer and use it in GitHub Desktop.
<button type="button" class="btn klaviyo-bis-trigger-custom" {% if product.selected_or_first_available_variant.available %}style="display: none;"{% endif %}>Notify me when available</button> | |
<div | |
id="KlaviyoBIS-{{ product.id }}" | |
class="modal modal--square modal--mobile-friendly klaviyo-bis-popup" | |
data-product-id="{{ product.id }}" | |
> | |
<div class="modal__inner"> | |
<div class="modal__centered medium-up--text-center"> | |
<div class="modal__centered-content"> | |
<div class="klaviyo-bis"> | |
<h2>{{ product.title }}</h2> | |
<p>{{ 'products.back_in_stock.popup_body_content' | t }}</p> | |
<div class="form-vertical" style="padding-top: 15px;"> | |
<form action="#" class="klaviyo-bis-form-custom"> | |
<input type="hidden" name="subscribe_for_newsletter" value="true"> | |
<input type="hidden" name="platform" value="shopify"> | |
<input type="hidden" name="g" value="RzPhNs"> <!-- lst id --> | |
<input type="hidden" name="a" value="QQyd2Z"> <!-- pub api key --> | |
<input type="hidden" name="product" value="{{ product.id }}"> | |
<div {% if product.variants.size == 1 %}style="display: none;"{% endif %}> | |
<label for="KlaviyoBIS-{{ product.id }}--variants">Options</label> | |
<select id="KlaviyoBIS-{{ product.id }}--variants" name="variant" class="variants" style="width: 100%;"> | |
{%- for v in product.variants -%} | |
{%- unless v.available -%} | |
<option value="{{ v.id }}">{{ v.title }}</option> | |
{%- endunless -%} | |
{%- endfor -%} | |
</select> | |
</div> | |
<label for="KlaviyoBIS-{{ product.id }}--name">Name</label> | |
<input type="tel" id="KlaviyoBIS-{{ product.id }}--name" class="input-full" name="name" value="{% if customer %}{{ customer.first_name | default: '' }} {{ customer.last_name | default: '' }}{% endif %}"> | |
<label for="KlaviyoBIS-{{ product.id }}--name">Email *</label> | |
<input type="email" id="KlaviyoBIS-{{ product.id }}--email" class="input-full" name="email" autocorrect="off" autocapitalize="off" value="" required> | |
<label for="KlaviyoBIS-{{ product.id }}--phone">Phone Number</label> | |
<input type="tel" id="KlaviyoBIS-{{ product.id }}--phone" class="input-full" name="phone_number" pattern="[0-9\-]*" value="{% if customer %}{{ customer.phone | default: '' }}{% endif %}"> | |
<div class="completed_message alert" style="display: none;">{{ 'products.back_in_stock.subscription_success_label' | t }} <a href="#" role="button" class="text-close js-modal-close">Close</a></div> | |
<div class="error_message alert" style="display: none;"></div> | |
<button class="btn submit-btn" style="width: 100%;">{{ 'products.back_in_stock.popup_button_label' | t }}</button> | |
</form> | |
</div> | |
<!-- <button type="button" class="text-close js-modal-close">Close</button> --> | |
</div> | |
</div> | |
<button type="button" class="modal__close js-modal-close text-link"> | |
<svg aria-hidden="true" focusable="false" role="presentation" class="icon icon-close" viewBox="0 0 64 64"><path d="M19 17.61l27.12 27.13m0-27.12L19 44.74"/></svg> | |
<span class="icon__fallback-text">{{ 'general.accessibility.close_modal' | t | json }}</span> | |
</button> | |
</div> | |
</div> | |
</div> | |
<script> | |
(function() { | |
function domReady(fn) { | |
// If we're early to the party | |
document.addEventListener("DOMContentLoaded", fn); | |
// If late; I mean on time. | |
if (document.readyState === "interactive" || document.readyState === "complete" ) { | |
fn(); | |
} | |
} | |
let pid = {{ product.id }}; | |
let currentVId = {{ product.selected_or_first_available_variant.id }}; | |
domReady(function() { | |
let id = 'KlaviyoBIS-' + pid | |
let modal = new theme.Modals(id, id) | |
let modalElement = document.getElementById(id) | |
let productSection = document.getElementById('ProductSection-' + pid) | |
if (productSection) { | |
let trigger = productSection.querySelector('.klaviyo-bis-trigger-custom') | |
let bisForm = productSection.querySelector('.klaviyo-bis-form-custom') | |
let submitBtn = bisForm.querySelector('.submit-btn') | |
let bisFormVariants = bisForm.querySelector('.variants') | |
let succEl = bisForm.querySelector('.completed_message') | |
let errEl = bisForm.querySelector('.error_message') | |
let variantOptionsArray = Array.from(bisFormVariants.options) | |
productSection.addEventListener('variantChange', e => { | |
let variant = e.detail.variant | |
let available = variant.available | |
currentVId = variant.id | |
if (!available) { | |
trigger.style.display = 'block' | |
} else { | |
trigger.style.display = 'none' | |
} | |
}) | |
trigger.addEventListener('click', function() { | |
bisFormVariants.selectedIndex = variantOptionsArray.findIndex(x => x.value == currentVId) | |
modal.open(); | |
}) | |
bisForm.addEventListener('submit', e => { | |
console.log('submit') | |
e.preventDefault(); | |
e.stopPropagation(); | |
if (bisForm.checkValidity()) { | |
postFormData() | |
} | |
}) | |
function postFormData() { | |
succEl.style.display = 'none' | |
errEl.style.display = 'none' | |
var formData = new FormData(bisForm); | |
var data = {}; | |
formData.forEach((value, key) => { | |
value = typeof value === 'string' ? value.trim() : value | |
if (value) { | |
if (key == 'name') { | |
let nameParts = value.split(' ') | |
if (nameParts[0]) { | |
data.$first_name = nameParts[0] | |
} | |
if (nameParts[1]) { | |
data.$last_name = nameParts[1] | |
} | |
} else { | |
data[key] = value; | |
} | |
} | |
}); | |
console.log(data) | |
if (!data.email || !data.variant || !data.product) { | |
return | |
} | |
// data.variant = 1338281066525 | |
// data.product = 114834341917 | |
submitBtn.setAttribute('disabled', 'true') | |
fetch("https://a.klaviyo.com/onsite/components/back-in-stock/subscribe", { | |
"headers": { | |
"accept": "*/*", | |
"accept-language": "en-US,en;q=0.9", | |
"content-type": "application/x-www-form-urlencoded", | |
}, | |
"body": objToStrUrlEncode(data), | |
"method": "POST", | |
"mode": "cors", | |
"credentials": "omit" | |
}) | |
.then(r => r.json()) | |
.then(r => { | |
if (r.status == 200 || r.success) { | |
succEl.style.display = 'block' | |
submitBtn.style.display = 'none' | |
subscribeToList(data) | |
} else { | |
errEl.innerHTML = r.message | |
errEl.style.display = 'block' | |
submitBtn.removeAttribute('disabled') | |
} | |
}) | |
} | |
function subscribeToList(data) { | |
let newData = { $email: data.email, a: data.a, g: data.g, $fields: [] } | |
if (data.$first_name) { | |
newData.$first_name = data.$first_name | |
newData.$fields.push('$first_name') | |
} | |
if (data.$last_name) { | |
newData.$last_name = data.$last_name | |
newData.$fields.push('$last_name') | |
} | |
if (data.phone_number) { | |
newData.$phone_number = data.phone_number | |
newData.$fields.push('$phone_number') | |
} | |
newData.$fields = newData.$fields.join(',') | |
fetch("https://a.klaviyo.com/ajax/subscriptions/subscribe", { | |
"headers": { | |
"accept": "*/*", | |
"accept-language": "en-US,en;q=0.9", | |
"access-control-allow-headers": "*", | |
"content-type": "application/x-www-form-urlencoded; charset=UTF-8", | |
}, | |
"referrer": "https://help.klaviyo.com/", | |
"referrerPolicy": "strict-origin-when-cross-origin", | |
"body": objToStrUrlEncode(newData), | |
"method": "POST", | |
"mode": "cors", | |
"credentials": "omit" | |
}) | |
} | |
function objToStrUrlEncode(obj) { | |
const str = []; | |
for (var key in obj) { | |
if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') { | |
str.push(encodeURIComponent(key) + "=" + encodeURIComponent(obj[key])) | |
} | |
} | |
return str.join('&') | |
} | |
} | |
}) | |
})(); | |
</script> |
Here's a simpler version:
The kaviyo api has been tucked away in a separate module.
<form id="custom_kaviyo_form">
<input name="a" type="hidden" value="<KLAVIYO ACCOUNT ID>">
<input name="g" type="hidden" value="<KLAVIYO LIST ID>">
<input name="first_name" type="text">
<input name="last_name" type="text">
<input name="email" type="email" required>
<button type="submit">Notify me</button>
</form>
<script type="module">
import {
post,
postBackInStock,
postNewsletter as subscribeToKlaviyoList,
} from 'https://cdn.shopify.com/s/files/1/2388/0287/t/11/assets/back-in-stock.js?v=30684385657065969231675343227'
const formElement = document.getElementById('custom_kaviyo_form')
formElement.addEventListener('submit', async e => {
e.preventDefault()
try {
const result = await subscribeToKlaviyoList(formElement)
if (!result.ok) {
throw new Error(result.error)
}
alert(`You're now subscribed!`)
} catch(e) {
// could not subscribe!
alert(`Failed to subscribed. Reason: "${e.message}"`)
}
})
</script>
And if you want to use klaviyo back-in-stock feature, then make sure to add the product.id
and variant.id
in the form like so,
<input name="product" type="hidden" value="{{ product.id }}">
<input name="variant" type="hidden" value="{{ variant.id }}">
And use postBackInStock
instead of subscribeToKlaviyoList
damn that's 🔥 actually. I like this a lot more, thanks for sharing.
listen to my weird ass use case and see how ur second version is so perfect ❤ i wana set my unavailable product as a separate prod not a variant, and just reference it with it's id - user clicks a button to open mini form and register interest from the available prods page.
all examples used product form and extracting variables from page and i didn't want to write bare amounts of code to do it myself but i like this version much better. nice one 👍
Glad you found it useful! You can ping me if you get stuck or need any pointers.
I was thinking about doing this for a pre-launch notify list and now i don't want to lol 😂
Thanks for illustrating how much work this is for literally no benefit (in my case). I will do something simpler instead lol.