mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2026-06-13 00:25:28 -04:00
some client-side validation on contact form
exactly identical errors to the one the server sends, but saves a few requests
This commit is contained in:
+60
-49
@@ -10,9 +10,12 @@ if (contactForm) {
|
|||||||
// immediately prevent <form> from actually submitting to a new page
|
// immediately prevent <form> from actually submitting to a new page
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const submitButton = document.getElementById("contact-form-btn-submit");
|
// feedback <span>s for later
|
||||||
|
const successSpan = document.getElementById("contact-form-result-success");
|
||||||
|
const errorSpan = document.getElementById("contact-form-result-error");
|
||||||
|
|
||||||
// disable the whole form if the button has been disabled below (on success)
|
// disable the whole form if the button has been disabled below (on success)
|
||||||
|
const submitButton = document.getElementById("contact-form-btn-submit");
|
||||||
if (submitButton.disabled === true) {
|
if (submitButton.disabled === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -22,58 +25,66 @@ if (contactForm) {
|
|||||||
submitButton.disabled = true; // prevent accidental multiple submissions
|
submitButton.disabled = true; // prevent accidental multiple submissions
|
||||||
submitButton.style.cursor = "default";
|
submitButton.style.cursor = "default";
|
||||||
|
|
||||||
// feedback <span>s for later
|
try {
|
||||||
const successSpan = document.getElementById("contact-form-result-success");
|
// https://simonplend.com/how-to-use-fetch-to-post-form-data-as-json-to-your-api/
|
||||||
const errorSpan = document.getElementById("contact-form-result-error");
|
const formData = Object.fromEntries(new FormData(event.currentTarget).entries());
|
||||||
|
|
||||||
// https://simonplend.com/how-to-use-fetch-to-post-form-data-as-json-to-your-api/
|
// some client-side validation, these are all also checked on the server
|
||||||
const formData = Object.fromEntries(new FormData(event.currentTarget).entries());
|
// to be safe but we can save some unnecessary requests here.
|
||||||
|
// we throw identical error messages to the server's so they're caught in
|
||||||
|
// the same way below.
|
||||||
|
if (!formData.name || !formData.email || !formData.message) {
|
||||||
|
throw new Error("missingData");
|
||||||
|
}
|
||||||
|
if (!formData["h-captcha-response"]) {
|
||||||
|
throw new Error("invalidCaptcha");
|
||||||
|
}
|
||||||
|
|
||||||
fetch(contactForm.action, {
|
// post JSONified form input to /api/contact/
|
||||||
method: "POST",
|
fetch(contactForm.action, {
|
||||||
headers: {
|
method: "POST",
|
||||||
"Content-Type": "application/json",
|
headers: {
|
||||||
Accept: "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
Accept: "application/json",
|
||||||
body: JSON.stringify(formData),
|
},
|
||||||
})
|
body: JSON.stringify(formData),
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => {
|
|
||||||
if (data.success === true) {
|
|
||||||
// handle successful submission
|
|
||||||
// we can disable submissions & hide the send button now
|
|
||||||
submitButton.disabled = true;
|
|
||||||
submitButton.style.display = "none";
|
|
||||||
|
|
||||||
// just in case there *was* a PEBCAK error and it was corrected
|
|
||||||
errorSpan.style.display = "none";
|
|
||||||
|
|
||||||
// let user know we were successful
|
|
||||||
successSpan.innerText = "Success! You should hear from me soon. :)";
|
|
||||||
} else {
|
|
||||||
// pass on an error sent by the server
|
|
||||||
throw new Error(data.message);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.then((response) => response.json())
|
||||||
const message = error instanceof Error ? error.message : "Unknown";
|
.then((data) => {
|
||||||
|
if (data.success === true) {
|
||||||
|
// handle successful submission
|
||||||
|
// we can disable submissions & hide the send button now
|
||||||
|
submitButton.disabled = true;
|
||||||
|
submitButton.style.display = "none";
|
||||||
|
|
||||||
// give user feedback based on the error message returned
|
// just in case there *was* a PEBCAK error and it was corrected
|
||||||
if (message === "invalidCaptcha") {
|
errorSpan.style.display = "none";
|
||||||
errorSpan.innerText = "Error: Did you remember to click the CAPTCHA?";
|
|
||||||
} else if (message === "invalidEmail") {
|
|
||||||
errorSpan.innerText = "Error: Please double check your email address.";
|
|
||||||
} else if (message === "missingData") {
|
|
||||||
errorSpan.innerText = "Error: Please make sure that all fields are filled in.";
|
|
||||||
} else {
|
|
||||||
// something else went wrong, and it's probably my fault...
|
|
||||||
errorSpan.innerText = "Internal server error. Try again later?";
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset submit button to let user try again
|
// let user know we were successful
|
||||||
submitButton.innerText = "Try Again";
|
successSpan.innerText = "Success! You should hear from me soon. :)";
|
||||||
submitButton.disabled = false;
|
} else {
|
||||||
submitButton.style.cursor = "pointer";
|
// pass on an error sent by the server
|
||||||
});
|
throw new Error(data.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : "Unknown";
|
||||||
|
|
||||||
|
// give user feedback based on the error message returned
|
||||||
|
if (message === "invalidCaptcha") {
|
||||||
|
errorSpan.innerText = "Did you complete the CAPTCHA? (If you're human, that is...)";
|
||||||
|
} else if (message === "missingData") {
|
||||||
|
errorSpan.innerText = "Please make sure that all fields are filled in.";
|
||||||
|
} else {
|
||||||
|
// something else went wrong, and it's probably my fault...
|
||||||
|
errorSpan.innerText = "Internal server error. Try again later?";
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset submit button to let user try again
|
||||||
|
submitButton.innerText = "Try Again";
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.style.cursor = "pointer";
|
||||||
|
submitButton.blur(); // remove keyboard focus from the button
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ $themes: (
|
|||||||
super-duper-light: #1f1f1f,
|
super-duper-light: #1f1f1f,
|
||||||
links: #88c7ff,
|
links: #88c7ff,
|
||||||
success: #78df55,
|
success: #78df55,
|
||||||
error: #f54545,
|
error: #ff5151,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ div.layout-contact {
|
|||||||
p {
|
p {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
a code {
|
code {
|
||||||
background: none;
|
background: none !important;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
word-spacing: -0.2em;
|
word-spacing: -0.175em;
|
||||||
white-space: normal;
|
white-space: normal; // re-enable "word" wrapping
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
<p>🔐 You can grab my public key here: <a href="/pubkey.asc" title="My Public PGP Key" target="_blank" rel="pgpkey authn noopener"><code>6BF3 79D3 6F67 1480 2B0C 9CF2 51E6 9A39</code></a>.</p>
|
<p>🔐 You can grab my public key here: <a href="/pubkey.asc" title="My Public PGP Key" target="_blank" rel="pgpkey authn noopener"><code>6BF3 79D3 6F67 1480 2B0C 9CF2 51E6 9A39</code></a>.</p>
|
||||||
|
|
||||||
<form id="contact-form" action="/api/contact/" method="POST">
|
<form id="contact-form" action="/api/contact/" method="POST">
|
||||||
<input type="text" id="name" name="name" placeholder="Name">
|
<input type="text" name="name" placeholder="Name">
|
||||||
<input type="email" id="email" name="email" placeholder="Email">
|
<input type="email" name="email" placeholder="Email">
|
||||||
|
<textarea name="message" placeholder="Write something..."></textarea>
|
||||||
|
|
||||||
<textarea id="message" name="message" placeholder="Write something..."></textarea>
|
|
||||||
<span id="contact-form-md-info">Basic <a href="https://commonmark.org/help/" title="Markdown reference sheet" target="_blank" rel="noopener">Markdown syntax</a> is allowed here, e.g.: <strong>**bold**</strong>, <em>_italics_</em>, [<a href="https://jarv.is" target="_blank" rel="noopener">links</a>](https://jarv.is), and <code>`code`</code>.</span>
|
<span id="contact-form-md-info">Basic <a href="https://commonmark.org/help/" title="Markdown reference sheet" target="_blank" rel="noopener">Markdown syntax</a> is allowed here, e.g.: <strong>**bold**</strong>, <em>_italics_</em>, [<a href="https://jarv.is" target="_blank" rel="noopener">links</a>](https://jarv.is), and <code>`code`</code>.</span>
|
||||||
|
|
||||||
<h-captcha
|
<h-captcha
|
||||||
|
|||||||
Reference in New Issue
Block a user