-
Notifications
You must be signed in to change notification settings - Fork 43
Customizing API forms
To render a form in Violet Rails, you have to use the snippet, {{ cms:helper render_form, <form_id_here> }}
in your HTML. Forms come with default styles but they can be easily customized to fit the design of your website with custom CSS and JavaScript. Inspect the form markup in your browser devtools to see the classes that can be targeted to add custom styles and JavaScript functionality.
.form-group label {
font-weight: bold;
color: #6b2bc4;
}
.form-control::placeholder {
color: #9e9e9e;
}
.form-control[type="text"],
.form-control[type="email"] {
padding: 1rem;
}
.violet-cta-form .input[type="submit"] {
background-color: #6b2bc4;
color: #fff;
}
Suppose, you need to change the order of form inputs:
function rearrangeFormInputs() {
const form = document.querySelector(".violet-cta-form");
// The first .form-group is the div containing the input elements
// need to give this a different class to differentiate it from input elements having .form-group class
const formControlsContainer = form.querySelector(".form-group");
formControlsContainer.className = "form-controls-container";
const formControls = form.querySelectorAll(".form-group");
// the order in which we want the input elements to be arranged
const formControlsOrder = {
username: 0,
email: 1,
password: 2,
password_confirmation: 3,
};
const orderedFormControls = new Array(formControls.length);
formControls.forEach((control) => {
// a form control will have a second class like "vr-username" and we need to extract "username"
const inputName = control.classList[1].slice(3);
const orderIndex = formControlsOrder[inputName];
// inserting the input element at the index position we want
orderedFormControls[orderIndex] = control;
});
formControlsContainer.replaceChildren(...orderedFormControls);
}
Script for rearranging the labels of checkbox and radio inputs:
function rearrangeCheckboxAndRadioLabels() {
const inputContainers = document.querySelectorAll(
".violet-cta-form .checkbox-container"
);
inputContainers.forEach((inputContainer) => {
const label = inputContainer.querySelector("span");
inputContainer.appendChild(label);
});
}
Script for hiding placeholder-text option in select
elements:
function hidePlaceholderTextOptionForSelectElements() {
const selectElements = document.querySelectorAll(".violet-cta-form select");
selectElements.forEach((element) => {
// the first option is an empty option which is used for placeholder text
const emptyOption = element.querySelector("option");
emptyOption.setAttribute("hidden", true);
});
}
Script for prompting the user to solve reCAPTCHA (reCAPTCHA V2) by showing an alert when the user tries to submit the form without solving it:
const form = document.querySelector(".violet-cta-form");
function addRecaptchaAlert() {
const recaptchaElement = form.querySelector(".g-recaptcha");
const alertMarkup = `
<p class="recaptcha-alert" style="display: none">
Please solve reCAPTCHA before submitting the form
</p>
`;
recaptchaElement.insertAdjacentHTML("afterend", alertMarkup);
}
function enhanceSubmitButtonToShowRecaptchaAlert() {
const submitButton = form.querySelector('input[type="submit"]');
const buttonWrapperMarkup = `
<span class="submit-btn-wrapper">
<span class="click-area" title="reCAPTCHA needs to be solved" tabindex="0" role="button" aria-label="Submit"></span>
</span>
`;
submitButton.insertAdjacentHTML("beforebegin", buttonWrapperMarkup);
const submitButtonWrapper = form.querySelector(".submit-btn-wrapper");
submitButtonWrapper.prepend(submitButton);
const clickableArea = form.querySelector(".click-area");
clickableArea.addEventListener("click", showRecaptchaAlert);
clickableArea.addEventListener("keydown", function (e) {
if (e.code !== "Enter" && e.code !== "Space") return;
showRecaptchaAlert();
});
function showRecaptchaAlert() {
if (submitButton.getAttribute("disabled")) {
const recaptchaAlert = document.querySelector(".recaptcha-alert");
recaptchaAlert.style.display = "block";
}
}
}
function hideRecaptchaAlertWhenInputIsFocused() {
form.addEventListener("focusin", function (e) {
if (!e.target.classList.contains("form-control")) {
return;
}
const recaptchaAlert = form.querySelector(".recaptcha-alert");
recaptchaAlert.style.display = "none";
});
}
Styles relevant to showing reCAPTCHA alert:
.submit-btn-wrapper {
position: relative;
}
/* this will be the clickable area when the button is disabled */
.submit-btn-wrapper .click-area {
display: inline-block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: not-allowed;
/* to make it stack on top of the button */
z-index: 1;
}
/* this selects .click-area that comes immediately after a submit button that is not disabled */
input[type="submit"]:not(:disabled) + .click-area {
transform: scale(0);
}
Script for showing a text field when the user selects a predefined option in a select
dropdown or checkbox/radio group, e.g. "Other":
- query all
select
input option elements and checkbox/radio inputs whose value is equal to the provided label, for example,<option value="Other">Other</option>
- attach a change event listener to the parent of each
selectInputOption
- attach a change event listener to each selected
checkbox/radio
input - add the text field if a select input's value becomes the provided label OR it includes (in case of select2, the value will be an array) that label
- in case of a selected
checkbox/radio
input, add the text field if it is checked - remove the text field otherwise
// adds a text input field when user selects an option, e.g. "Other" in a Select or checkbox group input
function addTextFieldWhenOptionIsSelected(optionLabel) {
const elemIds = $(
`.checkbox-container > input[value="${optionLabel}"][type="checkbox"]`
).map(function () {
return "#" + $(this).attr("id");
});
const selectInputOptions = $(`select > option[value="${optionLabel}"]`);
elemIds.each(function (_index, id) {
const checkboxElem = $(id);
checkboxElem.on("change", function () {
const parent = $(this).parent(".checkbox-container");
if ($(this).is(":checked")) {
addTextField(this, parent);
} else {
removeTextField(this, parent, optionLabel);
}
});
});
selectInputOptions.each(function (_index, option) {
const selectInput = $(option).parent();
selectInput.on("change", function () {
// in case of select2 input, the value will be in an array format
if (
selectInput.val() === optionLabel ||
selectInput.val().includes(optionLabel)
) {
addTextField(option, selectInput.parent());
} else {
removeTextField(option, selectInput.parent(), optionLabel);
}
});
});
function addTextField(element, elemParent) {
const textField = $("<input>")
.attr("type", "text")
.attr("placeholder", "Please specify...")
.addClass("form-control checkbox-text-field");
$(elemParent).append(textField);
// Changing option value according to user input value
textField.on("keyup", function () {
$(element).attr("value", $(this).val().trim());
});
}
function removeTextField(elem, elemParent, optionLabel) {
const textField = $(elemParent).find(
'input.checkbox-text-field[type="text"]'
);
textField.remove();
// Reverting back to the original value attribute.
$(elem).attr("value", optionLabel);
}
}