diff --git a/.gitignore b/.gitignore index 496ee2ca6..5ee7cd0c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.DS_Store \ No newline at end of file +.DS_Store +assets/.DS_Store \ No newline at end of file diff --git a/assets/css/style.css b/assets/css/style.css index a9d18b733..6adc70c82 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -407,10 +407,10 @@ footer { padding: 1em 1.5em; height: 56px; } -.input-group.is-valid .error-border, .input-group.is-valid .error-border:focus { +.input-group.is-error .error-border, .input-group.is-error .error-border:focus { outline: 2px solid var(--error); } -.input-group.is-valid.valid .error-message { +.input-group.is-error.valid .error-message { display: none; } @media (max-width: 767px) { @@ -455,7 +455,7 @@ footer { color: var(--error); } -.is-valid .error-message { +.is-error .error-message { display: block; } diff --git a/assets/js/auth/form-validation.js b/assets/js/auth/form-validation.js new file mode 100644 index 000000000..db1336bbb --- /dev/null +++ b/assets/js/auth/form-validation.js @@ -0,0 +1,138 @@ +export const form = document.querySelector('.form'); +export const inputList = form.querySelectorAll("input"); +export const submitBtn = document.querySelector('#submitBtn'); +export const VALIDATE_LIST = Object.freeze({ + EMAIL: 'val-email', + NICKNAME: 'val-nickname', + PASSWORD: 'val-password', + CONFIRM_PASSWORD: 'val-confirm-password', +}) +// 유효값에 대한 정의 +export const validate = { + errorPlacement: function (e, message) { + const inputFormDiv = e.parentNode; + let errorDiv = inputFormDiv.querySelector('.error-message'); + if (!errorDiv) { + errorDiv = document.createElement('div'); + errorDiv.classList.add('error-message'); + } + errorDiv.innerHTML = message; + inputFormDiv.appendChild(errorDiv); + + }, + showError: function (e, message) { + const inputFormDiv = e.parentNode; + inputFormDiv.classList.remove('valid'); + e.classList.add('error-border'); + e.dataset.valid = 'false'; + this.errorPlacement(e, message); + }, + success: function (e) { + const inputFormDiv = e.parentNode; + let errorDiv = inputFormDiv.querySelector('.error-message'); + e.classList.remove('error-border'); + if (errorDiv) { + errorDiv.remove(); + } + inputFormDiv.classList.add('valid'); + e.dataset.valid = 'true'; + }, + messages: { + [VALIDATE_LIST.EMAIL]: { + required: '이메일을 입력해주세요.', + pattern: '잘못된 이메일형식입니다.', + }, + [VALIDATE_LIST.NICKNAME]: '닉네임을 입력해주세요.', + [VALIDATE_LIST.PASSWORD]: { + required: '비밀번호를 입력해주세요.', + minlength: '비밀번호를 8자 이상 입력해주세요.', + }, + [VALIDATE_LIST.CONFIRM_PASSWORD]: { + required: '비밀번호를 입력해주세요.', + minlength: '비밀번호를 8자 이상 입력해주세요.', + equalTo: '비밀번호가 일치하지 않습니다.', + } + }, + rules: { + [VALIDATE_LIST.EMAIL]: { + required: true, + pattern: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/, + }, + [VALIDATE_LIST.NICKNAME]: { + required: true, + }, + [VALIDATE_LIST.PASSWORD]: { + required: true, + minlength: 8, + }, + [VALIDATE_LIST.CONFIRM_PASSWORD]: { + required: true, + minlength: 8, + equalTo: '#password', + } + } +} + +// input 유효값 check +export function checkInputs(input) { + const inputType = input.name; + const inputValName = `val-${inputType}`; + const value = input.value.trim(); + const rules = validate.rules[inputValName]; + const message = validate.messages[inputValName]; + + // Specific validation checks + if (inputType === 'email') { + if (rules.required && !value) { + validate.showError(input, message.required); + return false; + } + if (value && !rules.pattern.test(value)) { + validate.showError(input, message.pattern); + return false; + } + validate.success(input); + return true; + } else if (inputType === 'nickname') { + if (rules && !value) { + validate.showError(input, message); + return false; + } + validate.success(input); + return true; + } else if (inputType === 'password') { + if (rules.required && !value) { + validate.showError(input, message.required); + return false; + } + if (value && value.length < rules.minlength) { + validate.showError(input, message.minlength); + return false; + } + validate.success(input); + + } else if (inputType === 'confirm-password') { + if (rules.required && !value) { + validate.showError(input, message.required); + return false; + } + if (value && value.length < rules.minlength) { + validate.showError(input, message.minlength); + return false; + } + if (value && value !== document.querySelector('#password').value) { + validate.showError(input, message.equalTo); + return false; + } + validate.success(input); + return true; + } +} + +// form 전체에 대한 검증 +export function checkAllInput() { + let formSubmitDone = [...form.querySelectorAll('input')].every(input => input.dataset.valid === 'true'); + submitBtn.disabled = !formSubmitDone; + return formSubmitDone; +} + diff --git a/assets/js/auth/login.js b/assets/js/auth/login.js new file mode 100644 index 000000000..73c6247a3 --- /dev/null +++ b/assets/js/auth/login.js @@ -0,0 +1,51 @@ +import { + form, + inputList, + checkInputs, + checkAllInput, +} from './form-validation.js'; + +inputList.forEach((input) => { + input.addEventListener('blur', function () { + this.parentNode.classList.add('is-error'); + checkInputs(input); + }); + input.addEventListener('keyup', () => { + checkInputs(input); + checkAllInput(); + }) +}); + +// form 제출 +form.addEventListener('submit', function (event) { + event.preventDefault(); + if (checkAllInput) { + alert('로그인성공'); + window.location.href = '/items.html'; + } +}); + +// 패스워드 보기 +function passwordToggle(e) { + let button = e.currentTarget; + let passwordInput = button.parentElement.querySelector('input'); + let toggleIcon = document.createElement('i'); + toggleIcon.classList.add('icon'); + + const shouldShowPassword = passwordInput.type === 'password'; + passwordInput.type = shouldShowPassword ? 'text' : 'password'; + + toggleIcon.classList.toggle('ic_visible_on', !shouldShowPassword); + toggleIcon.classList.toggle('ic_visible_off', shouldShowPassword); + + const ariaLabel = shouldShowPassword ? '비밀번호 숨기기' : '비밀번호 보기'; + this.setAttribute("aria-label", ariaLabel); + + button.innerHTML = ''; + button.appendChild(toggleIcon); +} + +const passwordToggleBtns = document.querySelectorAll('.password-toggle-button'); +passwordToggleBtns.forEach(btn => { + btn.addEventListener('click', passwordToggle); +}); \ No newline at end of file diff --git a/assets/js/auth/signup.js b/assets/js/auth/signup.js new file mode 100644 index 000000000..9d2121f89 --- /dev/null +++ b/assets/js/auth/signup.js @@ -0,0 +1,52 @@ +import { + form, + inputList, + checkInputs, + checkAllInput, +} from './form-validation.js'; + +inputList.forEach((input) => { + input.addEventListener('blur', function () { + this.parentNode.classList.add('is-error'); + checkInputs(input); + }); + input.addEventListener('keyup', () => { + checkInputs(input); + checkAllInput(); + }) +}); + +// form 제출 +form.addEventListener('submit', function (event) { + event.preventDefault(); + if (checkAllInput) { + alert('회원가입성공'); + window.location.href = '/signup.html'; + } +}); + + +// 패스워드 보기 +function passwordToggle(e) { + let button = e.currentTarget; + let passwordInput = button.parentElement.querySelector('input'); + let toggleIcon = document.createElement('i'); + toggleIcon.classList.add('icon'); + + const shouldShowPassword = passwordInput.type === 'password'; + passwordInput.type = shouldShowPassword ? 'text' : 'password'; + + toggleIcon.classList.toggle('ic_visible_on', !shouldShowPassword); + toggleIcon.classList.toggle('ic_visible_off', shouldShowPassword); + + const ariaLabel = shouldShowPassword ? '비밀번호 숨기기' : '비밀번호 보기'; + this.setAttribute("aria-label", ariaLabel); + + button.innerHTML = ''; + button.appendChild(toggleIcon); +} + +const passwordToggleBtns = document.querySelectorAll('.password-toggle-button'); +passwordToggleBtns.forEach(btn => { + btn.addEventListener('click', passwordToggle); +}); \ No newline at end of file diff --git a/assets/js/signup2.js b/assets/js/auth/signup2.js similarity index 80% rename from assets/js/signup2.js rename to assets/js/auth/signup2.js index 7d0274b19..be14cf41e 100644 --- a/assets/js/signup2.js +++ b/assets/js/auth/signup2.js @@ -2,8 +2,6 @@ const form = document.querySelector("form"); const inputList = form.querySelectorAll("input"); const submitBtn = form.querySelector("#submitBtn"); -submitBtn.disabled = true; - inputList.forEach((input) => { input.addEventListener("keyup", function () { checkInputs(input); @@ -98,31 +96,8 @@ form.addEventListener("submit", (e) => { } }); - - // 패스워드 토글기능 -let passwordToggleBtns = document.querySelectorAll('.password-toggle-button'); - -function passwordToggle(e) { - let button = e.currentTarget; - let passwordInput = button.parentElement.querySelector('input'); - let toggleIcon = document.createElement('i'); - toggleIcon.classList.add('icon'); - if (passwordInput.type == "password") { - passwordInput.type = "text"; - toggleIcon.classList.remove('ic_visible_on'); - toggleIcon.classList.add('ic_visible_off') - this.setAttribute("aria-label", "비밀번호 숨기기"); - } else { - passwordInput.type = "password"; - toggleIcon.classList.remove('ic_visible_off'); - toggleIcon.classList.add('ic_visible_on'); - this.setAttribute("aria-label", "비밀번호 보기"); - } - button.innerHTML = ''; - button.appendChild(toggleIcon); -} - +const passwordToggleBtns = document.querySelectorAll('.password-toggle-button'); passwordToggleBtns.forEach(btn => { btn.addEventListener('click', passwordToggle); }); \ No newline at end of file diff --git a/assets/js/form-validation.js b/assets/js/form-validation.js deleted file mode 100644 index 9536686cd..000000000 --- a/assets/js/form-validation.js +++ /dev/null @@ -1,137 +0,0 @@ -export let form = document.querySelector('.form'); -export let email = document.querySelector('#email'); -export let password = document.querySelector('#password'); -export let confirmPassword = document.querySelector('#confirmPassword'); -export let nickName = document.querySelector('#nickname'); -export let submitBtn = document.querySelector('#submitBtn'); - -export const validate = { - errorPlacement: function (e, message) { - let errorDiv = e.nextElementSibling; - if (errorDiv && errorDiv.classList.contains('error-message')) { - errorDiv.innerHTML = message; - } else { - errorDiv = document.createElement('div'); - errorDiv.classList.add('error-message'); - errorDiv.innerHTML = message; - e.parentNode.insertBefore(errorDiv, e.nextSibling); - } - }, - showError: function (e, message) { - e.parentNode.classList.remove('valid') - e.classList.add('error-border'); - e.dataset.valid = 'false'; - this.errorPlacement(e, message); - }, - success: function (e) { - e.classList.remove('error-border'); - e.parentNode.classList.add('valid'); - e.dataset.valid = 'true'; - let errorDiv = e.nextElementSibling; - if (errorDiv && errorDiv.classList.contains('error-message')) { - errorDiv.innerHTML = ''; - } - }, - messages: { - 'val-email': { - required: '이메일을 입력해주세요.', - minlength: '잘못된 이메일 형식입니다.', - }, - 'val-nickname': '닉네임을 입력해주세요.', - 'val-password': { - required: '비밀번호를 입력해주세요.', - minlength: '비밀번호를 8자 이상 입력해주세요.', - }, - 'val-confirm-password': { - required: '비밀번호를 입력해주세요.', - minlength: '비밀번호를 8자 이상 입력해주세요.', - equalTo: '비밀번호가 일치하지 않습니다.', - } - }, - rules: { - 'val-email': { - required: true, - email: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/, - }, - 'val-nickname': { - required: true, - }, - 'val-password': { - required: true, - minlength: 8, - }, - 'val-confirm-password': { - required: true, - minlength: 8, - equalTo: '#password', - } - }, -} - -export function validateEmail() { - let value = email.value.trim(); - let rules = validate.rules['val-email']; - if (rules.required && !value) { - validate.showError(email, validate.messages['val-email'].required); - return false; - } - if (value && !rules.email.test(value)) { - validate.showError(email, validate.messages['val-email'].minlength); - return false; - } - validate.success(email); - return true; -} - -export function validateNickname() { - let value = nickName.value.trim(); - let rules = validate.rules['val-nickname']; - if (rules && !value) { - validate.showError(nickName, validate.messages['val-nickname']); - return false; - } - validate.success(nickName); - return true; -} - -export function validatePassword() { - let value = password.value.trim(); - let rules = validate.rules['val-password']; - if (rules.required && !value) { - validate.showError(password, validate.messages['val-password'].required); - return false; - } - if (value && value.length < rules.minlength) { - validate.showError(password, validate.messages['val-password'].minlength); - return false; - } - validate.success(password); - return true; -} - -export function validateConfirmPassword() { - let value = confirmPassword.value.trim(); - let passwordValue = password.value; - let rules = validate.rules['val-confirm-password']; - if (rules.required && !value) { - validate.showError(confirmPassword, validate.messages['val-confirm-password'].required); - return false; - } - if (value && value.length < rules.minlength) { - validate.showError(confirmPassword, validate.messages['val-confirm-password'].minlength); - return false; - } - if (value && value !== passwordValue) { - validate.showError(confirmPassword, validate.messages['val-confirm-password'].equalTo); - return false; - } - validate.success(confirmPassword); - return true; -} - - -export function checkFormValidity() { - let formSubmitDone = [...form.querySelectorAll('input')].every(input => input.dataset.valid === 'true'); - submitBtn.disabled = !formSubmitDone; -} - diff --git a/assets/js/login.js b/assets/js/login.js deleted file mode 100644 index 5bb59b3dd..000000000 --- a/assets/js/login.js +++ /dev/null @@ -1,48 +0,0 @@ -import { - form, - email, - password, - validateEmail, - validatePassword, - checkFormValidity, - submitBtn -} from './form-validation.js'; -import { passwordToggleBtns, passwordToggle } from './password-toggle.js'; - -submitBtn.disabled = true; - -email.addEventListener('blur', function () { - this.parentNode.classList.add('is-valid'); - if (!validateEmail) { this.parentNode.classList.remove('is-valid'); } -}); -password.addEventListener('blur', function () { - this.parentNode.classList.add('is-valid'); - validatePassword(); -}); - -email.addEventListener('keyup', function () { - if (this.parentNode.classList.contains('is-valid')) { - } - validateEmail(); - checkFormValidity(); -}); -password.addEventListener('keyup', function () { - if (this.parentNode.classList.contains('is-valid')) { - } - validatePassword(); - checkFormValidity(); -}); - -form.addEventListener('submit', function (event) { - event.preventDefault(); - if (validateEmail() && validatePassword()) { - alert('로그인성공'); - window.location.href = '/items.html'; - } -}); - - -// 패스워드 보기 -passwordToggleBtns.forEach(btn => { - btn.addEventListener('click', passwordToggle); -}); \ No newline at end of file diff --git a/assets/js/password-toggle.js b/assets/js/password-toggle.js deleted file mode 100644 index 140eb27aa..000000000 --- a/assets/js/password-toggle.js +++ /dev/null @@ -1,21 +0,0 @@ -export let passwordToggleBtns = document.querySelectorAll('.password-toggle-button'); - -export function passwordToggle(e) { - let button = e.currentTarget; - let passwordInput = button.parentElement.querySelector('input'); - let toggleIcon = document.createElement('i'); - toggleIcon.classList.add('icon'); - if (passwordInput.type == "password") { - passwordInput.type = "text"; - toggleIcon.classList.remove('ic_visible_on'); - toggleIcon.classList.add('ic_visible_off') - this.setAttribute("aria-label", "비밀번호 숨기기"); - } else { - passwordInput.type = "password"; - toggleIcon.classList.remove('ic_visible_off'); - toggleIcon.classList.add('ic_visible_on'); - this.setAttribute("aria-label", "비밀번호 보기"); - } - button.innerHTML = ''; - button.appendChild(toggleIcon); -} diff --git a/assets/js/signup.js b/assets/js/signup.js deleted file mode 100644 index b0b23a9f2..000000000 --- a/assets/js/signup.js +++ /dev/null @@ -1,74 +0,0 @@ -import { - form, - email, - password, - confirmPassword, - nickName, - validateEmail, - validateNickname, - validatePassword, - validateConfirmPassword, - checkFormValidity, - submitBtn -} from './form-validation.js'; -import { passwordToggleBtns, passwordToggle } from './password-toggle.js'; - -submitBtn.disabled = true; - - -email.addEventListener('blur', function () { - this.parentNode.classList.add('is-valid'); - validateEmail(); -}); -nickName.addEventListener('blur', function () { - this.parentNode.classList.add('is-valid'); - validateNickname(); -}); -password.addEventListener('blur', function () { - this.parentNode.classList.add('is-valid'); - validatePassword(); -}); -confirmPassword.addEventListener('blur', function () { - this.parentNode.classList.add('is-valid'); - validateConfirmPassword(); -}); - -email.addEventListener('keyup', function () { - if (this.parentNode.classList.contains('is-valid')) { - } - validateEmail(); - checkFormValidity(); -}); -nickName.addEventListener('keyup', function () { - if (this.parentNode.classList.contains('is-valid')) { - } - validateNickname(); - checkFormValidity(); -}); -password.addEventListener('keyup', function () { - if (this.parentNode.classList.contains('is-valid')) { - } - validatePassword(); - checkFormValidity(); -}); -confirmPassword.addEventListener('keyup', function () { - if (this.parentNode.classList.contains('is-valid')) { - } - validateConfirmPassword(); - checkFormValidity(); -}); - - -form.addEventListener('submit', function (event) { - if (validateEmail() && validatePassword() && validateConfirmPassword() && validateNickname()) { - event.preventDefault(); - alert('회원가입성공'); - window.location.href = '/signup.html'; - } -}); - - -// 패스워드 보기 -passwordToggleBtns.forEach(btn => { - btn.addEventListener('click', passwordToggle); -}); \ No newline at end of file diff --git a/assets/scss/_form.scss b/assets/scss/_form.scss index 6ef4076e7..cb1b861f9 100644 --- a/assets/scss/_form.scss +++ b/assets/scss/_form.scss @@ -59,12 +59,12 @@ } - &.is-valid .error-border, - &.is-valid .error-border:focus { + &.is-error .error-border, + &.is-error .error-border:focus { outline: 2px solid var(--error); } - &.is-valid.valid .error-message { + &.is-error.valid .error-message { display: none; } @@ -109,7 +109,7 @@ color: var(--error); } -.is-valid .error-message { +.is-error .error-message { display: block; } diff --git a/login.html b/login.html index 7b54b6714..99c64e751 100644 --- a/login.html +++ b/login.html @@ -10,7 +10,6 @@
- - - + -