diff --git a/assets/css/member.css b/assets/css/member.css index 14a90f4f..35bd7936 100644 --- a/assets/css/member.css +++ b/assets/css/member.css @@ -27,6 +27,25 @@ color: var(--gray800); } +.form__input-box .error-msg { + margin-top: var(--space-xxs); + padding: 0 var(--space-xs); + font-size: 14px; +} + +/* validation 실패시 */ +.form__input-box.isError .input { + border-color: var(--error); +} +.form__input-box.isError .error-msg { + color: var(--error); +} + +/* validation 통과시 */ +.form__input-box.isValid .input { + border-color: var(--primary-color); +} + /* 비밀번호 인풋박스박스 */ .form__input-box-pw .input-box__input { position: relative; diff --git a/assets/css/variables.css b/assets/css/variables.css index 0681cd48..c1dc768f 100644 --- a/assets/css/variables.css +++ b/assets/css/variables.css @@ -14,6 +14,7 @@ --gray700: #374151; --gray800: #1f2937; --gray900: #111827; + --error: #f74747; /* space */ --space-xxs: 8px; diff --git a/assets/js/member.js b/assets/js/member.js new file mode 100644 index 00000000..5dc1ef0f --- /dev/null +++ b/assets/js/member.js @@ -0,0 +1,137 @@ +// 에러 메세지 생성 함수 +const formEl = document.querySelector(".member-box__form"); +const submitBtn = document.querySelector(".member-box__form .form__submitBtn"); + +// 빈값일 때 메세지 지정 +const EMPTY_MSG = { + nickname: "닉네임을 입력해주세요.", + email: "이메일을 입력해주세요.", + password: "비밀번호를 입력해주세요.", + passwordConfirm: "비밀번호를 입력해주세요.", +}; + +// validation 규칙 +const VALIDATION_RULE = { + nickname: { + isValid: function (value) { + return !!value.length; + }, + failedMsg: null, + }, + email: { + isValid: function (value) { + const PATTERN = + /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/; + return PATTERN.test(value); + }, + failedMsg: "잘못된 이메일 형식입니다.", + }, + password: { + isValid: function (value) { + const PATTERN = /^[0-9a-zA-Z]{8}/; + return PATTERN.test(value); + }, + failedMsg: "비밀번호를 8자 이상 입력해주세요.", + }, + passwordConfirm: { + isValid: function (value) { + const password = document.querySelector("#password"); + return password.value === value; + }, + failedMsg: "비밀번호가 일치하지 않습니다.", + }, +}; + +// 에러 메세지 생성 +function createErrorMsg(text, inputBox) { + // 에러 메세지 중복 생성 방지 + if (inputBox.querySelector(".error-msg")) return; + + const msg = document.createElement("p"); + msg.textContent = text; + msg.classList.add("error-msg"); + inputBox.classList.add("isError"); + inputBox.append(msg); +} + +// input에 값 입력시 isError / isValid 초기화 +function changeInputReset(e) { + const inputBox = e.target.closest(".form__input-box"); + + if (inputBox.classList.contains("isError")) { + inputBox.classList.remove("isError"); + inputBox.querySelector(".error-msg").remove(); + } + + if (inputBox.classList.contains("isValid")) { + inputBox.classList.remove("isValid"); + } +} + +// 페이지내의 전체 input validation 통과했는지 확인 +function checkAllPass() { + const inputs = document.querySelectorAll(".member-box__form .input"); + const isAllPass = Array.from(inputs).every((input) => + input.closest(".form__input-box").classList.contains("isValid") + ); + + submitBtn.disabled = !isAllPass; +} + +// 빈값 검사 +function checkEmpty(name, inputBox) { + if (!EMPTY_MSG[name]) return; // EMPTY_MSG에 유효한 값이 있는지 확인 + + createErrorMsg(EMPTY_MSG[name], inputBox); +} + +// validation 검사 +function checkValidation(value, name, inputBox) { + if (!VALIDATION_RULE[name]) return; // VALIDATION_RULE에 유효한 값이 있는지 확인 + + const isValid = VALIDATION_RULE[name].isValid(value); // isValid 검사 + + if (!isValid) { + // 항목별 validation 실패시 + createErrorMsg(VALIDATION_RULE[name].failedMsg, inputBox); + } else { + // 항목별 validation 통과시 + inputBox.classList.add("isValid"); + } +} + +// focusout 핸들링 함수 +function handleFocusOut({ target }) { + if (!target.classList.contains("input")) return; + const { value, name } = target; + const inputBox = target.closest(".form__input-box"); + + if (!value.length) { + // 값이 없으면, + checkEmpty(name, inputBox); + } else { + // 값이 있으면, + checkValidation(value, name, inputBox); + } + + // 전체 input 유효성 통과헀는지 검사 + checkAllPass(); +} + +function togglePassword(e) { + if (!e.target.closest(".input-box__toggle")) return; + const pwBox = e.target.closest(".input-box__input"); + const inputBox = pwBox.querySelector(".input"); + const isShow = pwBox.classList.contains("pw_show"); + pwBox.classList.toggle("pw_show"); + inputBox.setAttribute("type", isShow ? "password" : "text"); +} + +function movePage(e) { + location.href = e.target.dataset.moveLink; +} + +formEl.addEventListener("input", changeInputReset); +formEl.addEventListener("focusout", handleFocusOut); +formEl.addEventListener("click", togglePassword); +submitBtn.addEventListener("click", movePage); diff --git a/login.html b/login.html index 739b9f12..efc3fdf1 100644 --- a/login.html +++ b/login.html @@ -35,12 +35,12 @@

- +
@@ -57,7 +57,14 @@

- + @@ -100,5 +107,6 @@

간편 로그인하기

+ diff --git a/signup.html b/signup.html index 78d950a8..54b982a4 100644 --- a/signup.html +++ b/signup.html @@ -46,12 +46,12 @@

- +
@@ -69,14 +69,14 @@

- -
+
@@ -93,7 +93,14 @@

- + @@ -136,5 +143,6 @@

간편 로그인하기

+