Skip to content

Instantly share code, notes, and snippets.

@callaginn
Last active September 19, 2024 07:09
Show Gist options
  • Save callaginn/7e64626dc6d648936ff0e4f3d83a5304 to your computer and use it in GitHub Desktop.
Save callaginn/7e64626dc6d648936ff0e4f3d83a5304 to your computer and use it in GitHub Desktop.
Shopify Ajax Contact Form
// Before implementing this, you'll need to contact Shopify support and ask them to turn off Google's ReCaptcha
// for your Shopify store's contact forms. Otherwise, it will redirect to the captcha's verification page.
// Retrieves input data from a form and returns it as a JSON object:
function formToJSON(elements) {
return [].reduce.call(elements, function (data, element) {
data[element.name] = element.value;
return data;
}, {});
}
// Get Shopify Friendly URL String
function getUrlString(data) {
var urlParameters = Object.entries(data).map(function (e) {
return e.join('=');
}).join('&');
return urlParameters;
}
function getUrlParameter(sParam) {
var sPageURL = decodeURIComponent(window.location.search.substring(1)),
sURLVariables = sPageURL.split('&'),
sParameterName,
i;
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
return sParameterName[1] === undefined ? true : sParameterName[1];
}
}
}
function ajaxFormInit(form) {
var form_type = form.querySelector("[name=form_type]").value,
inputs = form.querySelectorAll("[name]"),
alert = form.querySelector('[data-alert="status"]'),
alert_msgs = form.querySelector('.form-alerts');
form.addEventListener('submit', function(e){
e.preventDefault();
var action = form.getAttribute("action");
if (alert_msgs) {
var alert_msg = JSON.parse(alert_msgs.innerHTML)
}
console.log("Form Action: " + action);
console.log("Submitting " + form_type + " form...");
fetch(action, {
method: 'POST',
body: getUrlString(formToJSON(inputs)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': 'text/html, */*; q=0.01',
'X-Requested-With': 'XMLHttpRequest'
}
}).then(function(response) {
console.log(response);
console.log(response.status);
if (alert) {
alert.className = "alert alert-success";
alert.innerHTML = alert_msg.success;
}
var checkoutUrl = getUrlParameter("checkout_url");
if (checkoutUrl) {
window.location = getUrlParameter("checkout_url");
} else if (response.status === 200 && form_type !== "contact") {
window.location.pathname = "/account"
}
}).catch(function(err) {
console.error(err);
if (alert) {
alert.className = "alert alert-error";
alert.innerHTML = alert_msg.error;
}
});
});
}
// Init Shopify Forms
document.querySelectorAll("[name=form_type]").forEach(function(el) {
ajaxFormInit(el.closest("form"));
});
{% comment %}
Contact Form Wide
{% endcomment %}
{%- form 'contact' -%}
<div class="row">
<div class="col-md-4 mb-3">
<label for="name">{{ 'contact.form.name' | t }} <i class="fa fa-asterisk"></i></label>
<input type="text" class="form-control" name="contact[name]" autocomplete="name" required>
</div>
<div class="col-md-4 mb-3">
<label for="email">{{ 'contact.form.email' | t }} <i class="fa fa-asterisk"></i></label>
<input type="email" class="form-control" name="contact[email]" autocomplete="email" required>
</div>
<div class="col-md-4 mb-3">
<label for="phone">{{ 'contact.form.phone' | t }} <i class="fa fa-asterisk"></i></label>
<input type="tel" class="form-control" name="contact[phone]" autocomplete="tel" required>
</div>
</div>
<div class="mb-3">
<label for="message">{{ 'contact.form.message' | t }}</label>
<textarea id="message" class="form-control" name="contact[body]" rows="7"></textarea>
</div>
<!-- 2019 Honeypot / Checkbox Placeholder -->
<div class="checkbox captcha"><input type="text" class="honeypot" autocomplete="off" style="display:none;"></div>
<script type="application/json" class="form-alerts">
{
"error": "{{ 'general.forms.post_error' | t }}",
"success": "{{ 'contact.form.post_success' | t }}"
}
</script>
<div class="d-none" data-alert="status"></div>
<button type="button" class="btn btn-lg btn-outline-primary btn-submit disabled" disabled>{{ 'contact.form.send' | t }}</button>
{%- endform -%}
@JsChiSurf
Copy link

JsChiSurf commented Nov 28, 2023

Thanks for the script. Has anybody have an luck with this recently?

I have a sandbox store that I've been playing around with and after some slight modification to the code to get "mostly" working in my environment (the stock functions still bind to the form, even with event.preventDefault(), so the custom Ajax code essentially doesn't run for me anyway. I circumvented this by cloning the form at page load and then binding this Ajax library instead.

This is all great, and prevents any sort of default page redirection, etc, with the hopes of just staying on the page from where the form is submitted (for me this is a modal / dialog). However, no matter what I do, I'm getting a HTTP error '429' on the attempted post once turning the captcha off in the store preferences.

If I turn captcha back on, it forces the redirect even when posting via AJAX thus defeating the point.

What's even more crazy is with captcha turned off, the default contact form (separate template) still issues the captcha. What's the point?

This would makes sense though that while I have captcha turned off in the store I am still getting the HTTP 429 error when attempting to submit via the ajax since it seems clear captcha is still being enforced.

I have not reached out to Shopify yet, but in reading the posts here, it seems like they have no interest in helping fully disable despite the 'settings' being turned off.

EDIT: I can confirm as well that going in over VPN and switching between various locations the 429 status code does not initially occur. I guess it is just a matter of what the "rate limit" is, but I feel like there is too much risk in "hoping" this won't be encountered in a production environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment