Приклад опису помилок у формі
Опис
Після надсилання форми і отримання помилок валідації відбувається наступне:
- Над кожним полем, в якому має місце помилка (далі – помилкові поля), з'являється видимий опис суті помилки.
- Ці описи мають унікальні id, які вказуються в атрибуті "aria-describedby" кожного відповідного помилкового поля. Таким чином, при фокусі на полі скрінрідер, окрім його назви, озвучить ще й опис помилки.
- Перед формою створюється текст, фокус ставиться на нього. Текст повідомляє про наявність помилок.
- У кінці цього текста розміщуються якірні посилання, активація яких переводитиме користувача до відповідних помилкових полів.
- Рамки помилкових полей додатково забарвлюються в червоний колір, але це не послаблює видимість маркера фокусу, бо він має відступ 2px і товщину 2px.
При повторному надсиланні форми створені атрибути "aria-describedby" і згенеровані елементи прибираються з DOM.
Демонстрація
Відповідний код HTML
<div id="formCustom">
<div id="formStatus" style="display: none"></div>
<div class="mb25" id="anchorsSpawner" style="display: none"></div>
<div class="mb25">
<div class="mb4"><label for="inputEmail">Електронна пошта</label></div>
<input class="input" id="inputEmail" size="30" required aria-required="true">
</div>
<div class="mb25">
<div class="mb4"><label for="inputName">Ім'я</label></div>
<input class="input" id="inputName" size="30" required aria-required="true">
</div>
<div class="flex_row_sta_cen">
<div class="mr16">
<button class="button" id="buttonSendWithError">Симулювати помилки</button>
</div>
<div>
<button class="button" id="buttonSendWithSuccess">Симулювати успіх</button>
</div>
</div>
</div>
Відповідний код JS
const buttonSendWithError = document.getElementById("buttonSendWithError");
const buttonSendWithSuccess = document.getElementById("buttonSendWithSuccess");
const inputEmail = document.getElementById("inputEmail");
const inputName = document.getElementById("inputName");
const formStatus = document.getElementById("formStatus");
const anchorsSpawner = document.getElementById("anchorsSpawner");
buttonSendWithError.addEventListener("click", () => {
Form.send("error");
});
buttonSendWithSuccess.addEventListener("click", () => {
Form.send("success");
});
class Form
{
static send(simulation="success") {
// remove previously generated statuses and links:
formStatus.innerHtml = "";
formStatus.removeAttribute("tabindex");
formStatus.style.display = "none";
anchorsSpawner.style.display = "none";
while (anchorsSpawner.firstChild) {
anchorsSpawner.removeChild(anchorsSpawner.firstChild);
}
// remove previously generated errors:
let messages = document.getElementsByClassName("error-message");
Array.from(messages).forEach(message => {
message.remove();
});
// remove descriptions and respective attributes:
let inputsDescribed = document.getElementsByClassName("input-described");
Array.from(inputsDescribed).forEach(input => {
input.classList.remove("input-described");
input.classList.remove("input-with-error");
input.removeAttribute("aria-describedby");
});
// just simulation:
let response = {};
if (simulation === "error") {
inputEmail.value = "not_email";
inputName.value = "";
response = {
"errors" : {
"inputEmail" : "Має бути валідною адресою електронної пошти",
"inputName" : "Не має бути порожнім"
}
}
}
setTimeout(function(){
Form.handleResponse(response);
}, 500);
}
static handleResponse(response) {
if (!response.hasOwnProperty("errors")) {
this.showStatus("Форма прийнята без помилок.");
return true;
}
let message, input, anchors = [];
for (const inputId in response["errors"]) {
console.log(inputId + " = " + response["errors"][inputId]);
message = document.createElement("p");
message.setAttribute("id", "error-"+inputId);
message.classList.add("error-message");
message.textContent = "Помилка: " + response["errors"][inputId];
input = document.getElementById(inputId);
input.insertAdjacentElement("beforebegin", message);
input.setAttribute("aria-describedby", "error-"+inputId);
input.classList.add("input-described");
input.classList.add("input-with-error");
anchors.push(inputId);
}
this.showStatus("Форма містить помилки. Відповідні поля нижче мають пояснення.", anchors);
}
static showStatus(text, anchors=[]) {
if (anchors.length > 0) {
for (let anchor of anchors) {
let label = document.querySelector('label[for="'+anchor+'"]');
let formAnchorLink = document.createElement("a");
formAnchorLink.setAttribute("id", "formAnchorLink");
formAnchorLink.setAttribute("href", "#"+anchor);
formAnchorLink.classList.add("link");
formAnchorLink.innerText = 'перейти до виправлення поля "'+label.innerText+'"';
let formAnchorLinkContainer = document.createElement("div");
formAnchorLinkContainer.appendChild(formAnchorLink);
anchorsSpawner.appendChild(formAnchorLinkContainer);
}
}
anchorsSpawner.style.display = "block";
formStatus.innerHTML = text;
formStatus.setAttribute("tabindex", "0");
formStatus.style.display = "block";
formStatus.focus();
}
}
Відповідний код CSS
#formCustom {
background-color: #efefef;
padding: 20px;
}
p {
margin: 0px 0px 2px 0px;
padding: 0px;
}
.error-message {
color: #d90000;
}
.input {
background-color: #ffffff;
border: 1px solid #cecece;
border-radius: .25rem;
color: #000;
font-family: "font_normal";
font-size: 16px;
outline-color: #0984e3;
padding: 10px;
}.input:focus {
outline: 2px solid #4444FF !important;
outline-offset: 2px !important;
}
.input-with-error {
border: 1px #d90000 solid !important;
}
button {
cursor: pointer;
}button:focus {
outline: 2px solid #4444FF;
outline-offset: 2px;
}