Form Validation
해당 포스트는 제로베이스 오프라인스쿨 진행 과정 중 JS PairProgramming과제를 진행하며 회고를 정리하는 글이다.
11. Form validation TIL
1. .icon/ .error 선택 -> e.target.parentNode의 자식으로 검사
다음과 같은 계층구조를 가진 html이 있다. input 요소에서 input 값을 받아오면 그에 맞게 .icon과 .error를 바꾸어주려고 한다.
<form class="form signin" novalidate>
<div class="title">SIGN IN</div>
<div class="input-container">
<input type="text" id="signin-userid" name="userid" required autocomplete="off" />
<i class="icon icon-success bx bxs-check-circle hidden"></i>
<i class="icon icon-error bx bxs-x-circle hidden"></i>
<div class="error"></div>
</div>
<div class="input-container">
<input type="password" id="signin-password" name="password" required autocomplete="off" />
<i class="icon icon-success bx bxs-check-circle hidden"></i>
<i class="icon icon-error bx bxs-x-circle hidden"></i>
<div class="error"></div>
</div>
</form>
document.querySelectorAll('.icon-success');
document.querySelectorAll('.icon-error');
document.querySelectorAll('.error');
이 HTML에서 .icon-success, .icon-error, .error 요소를 선택하려고 하면, 해당되는 모든 요소가 선택되게 된다. 부모의 이름도 .input-container로 모두 같으므로, 필요한 요소만을 구분해 선택하기는 어렵다.
그렇다면, 유일한 요소를 찾아야 한다. 현재 input 값을 받는 input 요소는 이벤트가 발생된 유일한 요소이며, e.target으로 쉽게 찾을 수 있다. 이 input 요소의 부모에서 필요한 요소를 찾는다면 그 요소가 바로 필요한 요소이다.
$input.addEventListener('input', e => {
e.target.parentNode.querySelector('.icon-success');
e.target.parentNode.querySelector('.icon-error');
e.target.parentNode.querySelector('.error');
});
2. SIGNIN / SIGNUP 버튼 disabled 관리
SIGNIN / SIGNUP 버튼에 disabled 속성을 한번에 관리하기 위하여 각 input요소의 id를 키값으로 갖는 상태 변수를 만들어 관리를 진행하였다. 각 버튼들이 활성화 되는 조건(Input 요소들의 입력 값이 validation 과정을 거쳤을 시 일치 하였을 경우)을 만족할 시에만 동작을 하도록 구현을 하였다.
const signButtonState = {
'signin-userid': false,
'signin-password': false,
'signup-userid': false,
'signup-name': false,
'signup-password': false,
'signup-confirm-password': false,
};
const checkDisabled = () => {
document.querySelector('.signin.button').disabled = !(
signButtonState['signin-userid'] && signButtonState['signin-password']
);
document.querySelector('.signup.button').disabled = !(
signButtonState['signup-userid'] &&
signButtonState['signup-name'] &&
signButtonState['signup-password'] &&
signButtonState['signup-confirm-password']
);
};
const setSignButtonState = (key, newValue) => {
signButtonState[key] = newValue;
checkDisabled();
};
3. 코드 중복 줄이기
SIGNIN Form 과 SIGNUP Form에서 동일한 기능을 하는 이벤트 핸들러를 코드 중복을 줄이기 위해 하기와 같이 작성을 하였다.
// input email
[...document.getElementsByName('userid')].forEach($userid => {
$userid.addEventListener(
'input',
_.debounce(e => {
const regExp = /^[0-9a-z]([-_\.]?[0-9a-z])*@[0-9a-z]([-_\.]?[0-9a-z])*\.[a-z]{2,3}$/i;
const errMessage = '이메일 형식에 맞게 입력해 주세요.';
validation(e.target, regExp, errMessage);
}, 300)
);
});
// input password
[...document.getElementsByName('password')].forEach($password => {
$password.addEventListener(
'input',
_.debounce(e => {
const regExp = /^[0-9a-z]{6,12}$/i;
const errMessage = '영문 또는 숫자를 6~12자 입력하세요.';
validation(e.target, regExp, errMessage);
if ($signupConfirmPassword.value.length > 0) confirmPassword();
}, 300)
);
});
Form을 토글 하고 input, icon, error메세지 요소들을 초기화 하는 작업에 대한 코드 중복을 줄이고자 하기와 같이 작성하였다.
// toggle form & init from
[...document.querySelectorAll('.link')].forEach($link => {
$link.addEventListener('click', e => {
if (!e.target.matches('a')) return;
const $form = $link.parentNode;
// toggle form
[...$forms].forEach($form => {
$for m.classList.toggle('hidden');
});
// input 초기화
[...$form.querySelectorAll('input')].forEach($input => {
$input.value = '';
setSignButtonState($input.id, false);
});
// icon 초기화
[...$form.querySelectorAll('.icon')].forEach($icon => {
$icon.classList.add('hidden');
});
// error 초기화
[...$form.querySelectorAll('.error')].forEach($error => {
$error.textContent = '';
});
});
});
<배운점>
++ 코드 중복을 최대한 제거하여 가독성 좋은 코드를 만드는 것이 목표였는데 코드 중복 제거를 진행 하는 과정이 정말 쉽지 않다는 것을 알게되었다. 앞으로도 코드 중복을 최소화 하는 방향을 명심하고 코드를 구현해 나아가야 겠다고 생각했다.