Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Рецепт "Мгновенная валидация форм" #5131

Merged
merged 47 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
304a274
Создаю новую статью рецепт "мгновенная валидация"
makarovaiuliia Jan 19, 2024
2f21d23
Исправление ошибок
makarovaiuliia Jan 19, 2024
3f33b7f
Форматирование кода в файле .md
makarovaiuliia Jan 19, 2024
80719ff
Добавляю в разметку акценты на словах
makarovaiuliia Jan 19, 2024
ed6def5
Добавляю правильную ссылку на demo
makarovaiuliia Jan 19, 2024
1788fe1
Изменение разметки
makarovaiuliia Jan 19, 2024
c86fe32
Добавляет проверку длины сообщения и усовершенствует текст статьи
makarovaiuliia Jan 20, 2024
46eea4d
Убирает дублирование функции checkInputValidity
makarovaiuliia Jan 20, 2024
fcc36cf
Вычитывает текст
skorobaeus Jan 22, 2024
fd13efe
Тюнит вёрстку
skorobaeus Jan 23, 2024
7b6d8a5
Ублажает линтер
skorobaeus Jan 23, 2024
292b01e
Ублажает линтер ещё
skorobaeus Jan 23, 2024
b7e044b
Растит айфрейм
skorobaeus Jan 23, 2024
ca5102d
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
93938b5
Update recipes/validation/demos/final-form/index.html
makarovaiuliia Feb 4, 2024
bd97ee0
Merge branch 'doka-guide:main' into article/recipes
makarovaiuliia Feb 4, 2024
d15cd75
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
a40a5dc
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
73635e1
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
c0f9f2d
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
dd5d141
Update recipes/validation/demos/final-form/index.html
makarovaiuliia Feb 4, 2024
920676d
Update recipes/validation/demos/final-form/index.html
makarovaiuliia Feb 4, 2024
b0b5513
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
5ae90b8
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
6da35c3
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
be1d99e
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
0305984
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
e0f5657
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
102ffeb
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
0d6d9e5
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
7377e6c
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
c4a4a1e
Update recipes/validation/index.md
makarovaiuliia Feb 4, 2024
46477a1
Привожу в порядок в соответствии с комментариями
makarovaiuliia Feb 4, 2024
afc6af4
Объединение
makarovaiuliia Feb 4, 2024
fb3e8a1
Улучшая часть статьи с деактивированной кнопкой и убирает спан с ошиб…
makarovaiuliia Feb 16, 2024
fb0c8be
пытаюсь совладать с линтером
makarovaiuliia Feb 16, 2024
0f081a8
добавляет новую разметку в статью
makarovaiuliia Feb 16, 2024
f127b51
300px > 450px
skorobaeus Feb 19, 2024
f95bafc
Форматирует примеры, + контрибьютор
TatianaFokina Feb 19, 2024
85d0c5b
Форматирует код примера
solarrust Feb 19, 2024
abcb14b
Редактирует текст статьи
solarrust Feb 19, 2024
3cd7f15
Делает ошибку к форме доступной
TatianaFokina Mar 4, 2024
cdc24b7
Ищет внутри формы
TatianaFokina Mar 4, 2024
6742586
Актуализирует примеры
TatianaFokina Mar 4, 2024
789b2a4
Удаляет упс из демки
TatianaFokina Mar 4, 2024
363e55c
Слегка улучшает скрипт
TatianaFokina Mar 5, 2024
8a2c518
Актуализирует примеры кода
TatianaFokina Mar 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
372 changes: 372 additions & 0 deletions recipes/validation/demos/final-form/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,372 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<title>Мгновенная валидация полей — Валидация формы — Дока</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" >
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}

html {
color-scheme: dark;
}

html, body {
height: 100%;
}

body {
display: grid;
justify-content: center;
align-items: center;
padding: 50px 50px 75px;
background-color: #18191C;
color: #FFFFFF;
font-family: "Roboto", sans-serif;
font-size: 18px;
line-height: 1.4;
font-weight: 400;
}

.form {
display: flex;
flex-direction: column;
gap: 10px;
}

.form__label {
font-weight: 500;
font-size: 24px;
}

.form__field-container {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto calc(14px * 2.2);
}

.form__field {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto auto;
align-items: center;
gap: 10px
}

.form__type-input {
width: 100%;
border: 1px solid #FFFFFF;
border-radius: 6px;
margin-block-end: 5px;
padding: 10px 15px;
background-color: transparent;
color: #FFFFFF;
font-size: 18px;
font-weight: 300;
font-family: inherit;
-webkit-appearance: none;
appearance: none;
}

.form__type-input:focus {
border-color: #C56FFF;
outline: none;
}

.form__type-input-error {
border: 1px solid #FF8630;
background-color: rgb(255 134 48 / 0.1);
}

.form__error {
display: none;
grid-column: 1;
align-self: self-start;
grid-row-end: span 2;
color: #FF8630;
font-size: 14px;
line-height: 1.1;
}

.form__checkbox-label .form__error {
grid-column: 1 / 3;
}

.form__error-active {
display: block;
}

.form__checkbox-label {
position: relative;
display: grid;
grid-template-columns: 24px auto;
gap: 0 15px;
margin-inline-start: auto;
cursor: pointer;
}

.form__type-checkbox {
width: 24px;
height: 24px;
opacity: 0;
padding: 0;
}

.form__type-checkbox + .form__type-checkbox-title::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 24px;
height: 24px;
border: 1px solid #FFFFFF;
border-radius: 6px;
}

.form__type-checkbox:focus + .form__type-checkbox-title::before {
border-color: #C56FFF;
}

.form__type-checkbox:checked + .form__type-checkbox-title::after {
content: '';
position: absolute;
left: 5px;
top: 5px;
width: 14px;
height: 14px;
border-radius: 3px;
background-color: #C56FFF;
}

.button {
border: 2px solid transparent;
margin-inline-start: auto;
padding: 10px 15px;
background-color: #C56FFF;
border-radius: 6px;
color: #000000;
font-size: 18px;
font-weight: 300;
font-family: inherit;
cursor: pointer;
transition: background-color 0.2s linear;
}

.button:hover {
border: 2px solid #FFFFFF;
background-color: #FFFFFF;
}

.button:focus-visible {
outline: none;
border: 2px solid #FFFFFF;
}

.button-inactive {
cursor: not-allowed;
background-color: rgb(211 211 211 / 0.6);
}

.button-inactive:hover {
background-color: rgb(211 211 211 / 0.2);
border: 2px solid transparent;
}

.form__empty-error {
padding: 10px 0;
font-size: 18px;
color: #FF8630;
}

.form__error, .form__empty-error, .form__checkbox-label, .button {
inline-size: 450px;
}

@media (max-width: 768px) {
body {
padding: 30px;
}

.form__error, .form__empty-error, .form__checkbox-label, .button {
inline-size: 300px;
}
}
</style>
</head>
<body>
<form class="form" name="form" aria-describedby="form-error" novalidate>
<div class="form__field-container">
<label class="form__field">
<span class="form__label">Имя:</span>
<input
type="text"
id="input__name"
class="form__type-input"
placeholder="Иван"
pattern="^[a-zA-Zа-яА-ЯЁё\s\-]+$"
data-error-message="Разрешены символы латиницы, кириллицы, знаки дефиса и пробелы."
aria-describedby="name-error"
required
>
</label>
<span class="form__error input__name-error" id="name-error" aria-live="polite"></span>
</div>
<div class="form__field-container">
<label class="form__field">
<span class="form__label">Фамилия:</span>
<input
type="text"
id="input__surname"
class="form__type-input"
placeholder="Васильевич"
pattern="^[a-zA-Zа-яА-ЯЁё\s\-]+$"
data-error-message="Разрешены символы латиницы, кириллицы, знаки дефиса и пробелы."
aria-describedby="surname-error"
required
>
</label>
<span class="form__error input__surname-error" id="surname-error" aria-live="polite"></span>
</div>
<div class="form__field-container">
<label class="form__field">
<span class="form__label">Почта:</span>
<input
type="email"
id="input__e-mail"
class="form__type-input"
placeholder="menyaet.professiyu@ivan.com"
aria-describedby="email-error"
required
>
</label>
<span class="form__error input__e-mail-error" id="email-error" aria-live="polite"></span>
</div>
<div class="form__field-container">
<label class="form__field">
<span class="form__label">Возраст:</span>
<input
type="number"
id="input__age"
class="form__type-input"
placeholder="40"
min="18"
max="100"
aria-describedby="age-error"
required
>
</label>
<span class="form__error input__age-error" id="age-error" aria-live="polite"></span>
</div>
<div class="form__field-container">
<label class="form__checkbox-label">
<input
type="checkbox"
id="input__checkbox"
class="form__type-input form__type-checkbox"
checked
aria-describedby="checkbox-error"
required
/>
<span class="form__type-checkbox-title">Я согласен быть царём</span>
</label>
<span class="form__error input__checkbox-error" id="checkbox-error" aria-live="polite"></span>
</div>
<button type="submit" class="button" aria-describedby="empty-error">Отправить</button>
<span class="form__empty-error" id="empty-error" aria-live="assertive"></span>
</form>

<script>
const form = document.querySelector('.form')
const inputList = Array.from(document.querySelectorAll('.form__type-input'))
TatianaFokina marked this conversation as resolved.
Show resolved Hide resolved
const buttonElement = form.querySelector('.button')
const formErrorElement = document.getElementById('empty-error')
TatianaFokina marked this conversation as resolved.
Show resolved Hide resolved

startValidation()

function startValidation() {
toggleButton()
form.addEventListener('submit', (event) => {
event.preventDefault()
if (hasInvalidInput()) {
formError()
}
})
inputList.forEach((inputElement) => {
inputElement.addEventListener('input', () => {
checkInputValidity(inputElement)
toggleButton()
})
})
}

function checkInputValidity(inputElement) {
if (inputElement.validity.patternMismatch) {
inputElement.setCustomValidity(inputElement.dataset.errorMessage)
} else {
inputElement.setCustomValidity(checkLengthMismatch(inputElement))
}

if (!inputElement.validity.valid) {
toggleErrorSpan(inputElement, inputElement.validationMessage)
} else {
toggleErrorSpan(inputElement)
}
}

function checkLengthMismatch(inputElement) {
if (inputElement.type !== 'text') {
return ''
}
const valueLength = inputElement.value.trim().length;
TatianaFokina marked this conversation as resolved.
Show resolved Hide resolved
if (valueLength < inputElement.minLength) {
return `Минимальное количество символов: ${inputElement.minLength}`
}
return ''
}

function hasInvalidInput() {
return inputList.some((inputElement) => {
return !inputElement.validity.valid
})
}

function toggleErrorSpan(inputElement, errorMessage) {
const errorElement = document.querySelector(`.${inputElement.id}-error`)
if (errorMessage) {
inputElement.classList.add('form__type-input-error')
errorElement.textContent = errorMessage
errorElement.classList.add('form__error-active')
} else {
inputElement.classList.remove('form__type-input-error')
errorElement.textContent = ''
errorElement.classList.remove('form__error-active')
}
}

function toggleButton() {
if (hasInvalidInput()) {
buttonElement.classList.add('button-inactive')
buttonElement.setAttribute('aria-disabled', 'true')
} else {
buttonElement.classList.remove('button-inactive')
buttonElement.setAttribute('aria-disabled', 'false')
formErrorElement.textContent = ''
}
}

function formError() {
const errorMessage = 'Заполните все поля для отправки формы.'
formErrorElement.textContent = errorMessage
}
</script>
</body>
</html>
Loading
Loading