-> fontAwesome 링크 추가
: - 스코프(Scope)라는 영어 단어 자체는 ‘범위’라는 의미를 가진다.
- 컴퓨터 공학, 그리고 JavaScript 에서도 ‘범위’의 의미를 가지고 있다
: 말 그대로 전역에 선언되어있어 어느 곳에서든지 해당 변수에 접근 가능하다.
: 해당 지역에서만 접근할 수 있어 지역을 벗어난 곳에선 접근할 수 없다
자바스크립트에서 함수를 선언하면 함수를 선언할 때마다 새로운 스코프를 생성하게 된다. 그러므로 함수 몸체에 선언한 변수는 해당 함수 몸체 안에서만 접근할 수 있는데 이걸 함수 스코프(function-scoped)라고 한다. 함수 스코프가 바로 지역 스코프의 예라고 할 수 있다.
querySelector() , querySelectorAll() , matches() 또는 Element.closest() 와 같은 DOM API에서 사용하는 경우 :scope 는 메서드가 호출 된 요소와 일치한다.
:scope
의사 클래스가 유용한 것으로 입증 된 상황 은 이미 검색된 Element 의 직계 자손을 가져와야 할 때, 즉 직접적인 애들을 데려오는 것이다.
:자바스크립트에서는 classList 함수를 사용하여 HTML 요소에 class 속성을 추가, 제거, 변경, 여부확인이 가능하다.
- 클래스를 조작하는 다양한 메서드들을 쓸 수 있다.
:해당 요소의 클래스 속성값을 추가, 같은 클래스명 있는 경우 무시
: 클래스의 속성값을 체크하여 제거한다. 없는 경우 무시
:해당 요소의 클래스의 유무를 확인하여 true 또는 false로 구분
: 클래스값이 있는지 체크하고 없으면 더하고 있으면 제거
-> index.unsugned.js
<body class="unsigned">
<div class="cover"></div>
<div class="resolution">
<div class="exc">!</div>
<div class="message">화면 크기를 더 크게 조정 해주세요.</div>
</div>
<form class="login-form" rel="loginForm">
<div class="logo-container">
<img class="logo" alt="카카오톡" th:src="@{/resources/images/login-form.logo.png}">
</div>
<div class="input-wrapper">
<label class="label">
<span hidden>이메일</span>
<input autofocus class="input" maxlength="50" name="email" placeholder="카카오계정 (이메일)" type="email">
</label>
<label class="label">
<span hidden>비밀번호</span>
<input maxlength="100" class="input" name="password" placeholder="비밀번호" type="password">
</label>
</div>
<input class="login-button" disabled type="submit" value="로그인">
<label class="auto-sign-container">
<input class="checkbox" name="autoSign" type="checkbox">
<span class="checker">
<i class="icon fa-solid fa-check"></i>
</span>
<span class="text">잠금모드로 자동로그인</span>
</label>
<ul class="menu">
<li class="item">
<a class="link" href="#" data-logic="register">카카오계정 만들기</a>
</li>
<li class="line"></li>
<li class="item">
<a class="link" href="#" data-logic="recover">비밀번호 재설정</a>
</li>
</ul>
</form>
<form class="register-form visible" rel="registerForm">
<div class="progress" rel="progress">
<div class="value" rel="value"></div>
</div>
<div class="step-container">
<div class="step terms visible">
<span class="title">카카오계정<br>서비스 약관에 동의해 주세요.</span>
<label class="check-label">
<input class="checkbox" name="terms-agree-all" type="checkbox">
<span class="checker">
<i class="icon fa-solid fa-check"></i>
</span>
<span class="text">모두 동의합니다.</span>
</label>
<span class="all-warning">전체 동의는 필수 및 선택정보에 대한 동의도 포함되어 있으며, 개별적으로도 동의를 선택하실 수 있습니다.<br>선택항목에 대한 동의를 거부하시는 경우에도 서비스는 이용이 가능합니다.</span>
<label class="check-label">
<input class="checkbox" name="terms-no-kid" type="checkbox">
<span class="checker">
<i class="icon fa-solid fa-check"></i>
</span>
<span class="text">만 14세 이상입니다.</span>
</label>
<label class="check-label">
<input class="checkbox" name="terms-term" type="checkbox">
<span class="checker">
<i class="icon fa-solid fa-check"></i>
</span>
<span class="text">[필수] 카카오계정 약관</span>
</label>
<label class="check-label">
<input class="checkbox" name="terms-privacy" type="checkbox">
<span class="checker">
<i class="icon fa-solid fa-check"></i>
</span>
<span class="text">[필수] 개인정보 수집 및 이용 동의</span>
</label>
<label class="check-label">
<input class="checkbox" name="terms-profile" type="checkbox">
<span class="checker">
<i class="icon fa-solid fa-check"></i>
</span>
<span class="text">[선택] 프로필정보 추가 수집 동의</span>
</label>
<input class="button" disabled type="button" rel="termsNext" value="동의">
</div>
</div>
</form>
</body>
-> index.insigned.css
@charset "UTF-8";
body.unsigned {
width: 100%;
height: 100%;
background-color: rgb(var(--common-color-yellow-5));
position: absolute;
}
body.unsigned > .cover {
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 50%);
position: fixed;
}
body.unsigned > .resolution {
top: 0;
left: 0;
width: 100%;
height: 100%;
align-items: center;
background-color: rgb(var(--common-color-yellow-5));
display: none;
flex-direction: column;
justify-content: center;
position: fixed;
z-index: 2;
}
body.unsigned > .resolution > .exc {
color: rgb(var(--common-color-brown-5));
cursor: default;
font-size: 5rem;
margin-bottom: 1rem;
user-select: none;
}
body.unsigned > .login-form {
top: 50%;
left: 50%;
width: 25rem;
align-items: stretch;
background-color: rgb(var(--common-color-yellow-5));
border: 0.0625rem solid rgba(var(--common-color-yellow-5), 90%);
border-radius: 0.25rem;
box-shadow: 0 0 0.5rem 0.0625rem rgba(50, 50, 50, 50%);
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: flex-start;
opacity: 0;
padding: 5rem 2.75rem;
pointer-events: none;
position: fixed;
transform: translate(-50%, -50%);
z-index: 1;
}
body.unsigned > .login-form.visible {
opacity: 1;
pointer-events: all;
}
body.unsigned > .login-form > .logo-container {
align-items: center;
display: flex;
flex-direction: row;
justify-content: center;
margin-bottom: 3rem;
}
body.unsigned > .login-form > .logo-container > .logo {
width: 7.5rem;
user-select: none;
-webkit-user-drag: none;
}
body.unsigned > .login-form > .input-wrapper {
align-items: stretch;
background-color: rgb(255, 255, 255);
border: 0.0625rem solid rgb(var(--common-color-yellow-4));
border-radius: 0.25rem;
display: flex;
flex-grow: 0;
flex-shrink: 0;
flex-direction: column;
justify-content: flex-start;
margin-bottom: 0.45rem;
overflow: hidden;
}
body.unsigned > .login-form > .input-wrapper > .label {
align-items: stretch;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
body.unsigned > .login-form > .input-wrapper > .label:not(:last-of-type) {
border-bottom: 0.0625rem solid rgb(225, 225, 225);
}
body.unsigned > .login-form > .input-wrapper > .label > .input {
background-color: rgb(255, 255, 255);
padding: 0.7rem 1rem;
}
body.unsigned > .login-form > .login-button {
background-color: rgb(var(--common-color-brown-5));
border: 0.0625rem solid rgb(var(--common-color-yellow-4));
border-radius: 0.25rem;
color: rgb(255, 255, 255);
cursor: pointer;
margin-bottom: 0.4rem;
padding: 0.7rem 0;
}
body.unsigned > .login-form > .login-button:hover {
background-color: rgb(var(--common-color-brown-6));
}
body.unsigned > .login-form > .login-button:active {
background-color: rgb(var(--common-color-brown-4));
}
body.unsigned > .login-form > .login-button:disabled {
background-color: rgb(246, 246, 246);
color: rgb(180, 180, 180);
cursor: default;
}
body.unsigned > .login-form > .auto-sign-container {
align-items: center;
color: rgb(var(--common-text-color-9));
cursor: pointer;
display: flex;
flex-direction: row;
font-size: 0.9rem;
justify-content: flex-start;
user-select: none;
}
body.unsigned > .login-form > .auto-sign-container > .checkbox {
display: none;
}
body.unsigned > .login-form > .auto-sign-container > .checker {
width: 1rem;
height: 1rem;
background-color: rgb(255, 255, 255);
border: 0.0625rem solid rgb(var(--common-color-yellow-4));
border-radius: 0.15rem;
margin-right: 0.5rem;
position: relative;
}
body.unsigned > .login-form > .auto-sign-container > .checker > .icon {
top: calc(50% + 0.0625rem);
left: 50%;
color: rgb(var(--common-color-brown-5));
display: none;
font-size: 0.8rem;
position: absolute;
transform: translate(-50%, -50%);
}
body.unsigned > .login-form > .auto-sign-container > .checkbox:checked + .checker > .icon {
display: inline-block;
}
body.unsigned > .login-form > .menu {
align-items: center;
display: flex;
flex-direction: row;
font-size: 0.9rem;
justify-content: center;
margin-top: 8rem;
}
body.unsigned > .login-form > .menu > .item > .link {
color: rgb(var(--common-text-color-9));
text-decoration: none;
}
body.unsigned > .login-form > .menu > .item > .link:hover {
text-decoration: underline;
}
body.unsigned > .login-form > .menu > .line {
height: 1rem;
border-right: 0.0625rem solid rgb(var(--common-color-yellow-4));
margin: 0 0.75rem;
}
body.unsigned > .register-form {
top: 50%;
left: 50%;
width: 25rem;
background-color: rgb(255, 255, 255);
border: 0.0625rem solid rgb(225, 225, 225);
border-radius: 0.25rem;
box-shadow: 0 0 0.5rem 0.0625rem rgba(50, 50, 50, 50%);
box-sizing: border-box;
opacity: 0;
padding: 2rem;
pointer-events: none;
position: fixed;
transform: translate(-50%, -50%);
z-index: 1;
}
body.unsigned > .register-form.visible {
opacity: 1;
pointer-events: all;
}
body.unsigned > .register-form > .progress {
width: 6.5rem;
height: 0.25rem;
background-color: rgb(225, 225, 225);
margin-bottom: 2rem;
position: relative;
}
body.unsigned > .register-form > .progress > .value {
top: 0;
left: 0;
width: 50%;
height: 100%;
background-color: rgb(100, 100, 100);
position: absolute;
}
body.unsigned > .register-form > .step-container > .step {
align-items: stretch;
display: none;
flex-direction: column;
justify-content: flex-start;
}
body.unsigned > .register-form > .step-container > .step.visible {
display: flex;
}
body.unsigned > .register-form > .step-container > .step > .title {
font-size: 1.375rem;
font-weight: 500;
margin-bottom: 2rem;
}
body.unsigned > .register-form > .step-container > .step > .check-label {
align-items: center;
cursor: pointer;
display: flex;
flex-direction: row;
justify-content: flex-start;
margin-bottom: 0.75rem;
user-select: none;
}
body.unsigned > .register-form > .step-container > .step > .check-label > .checkbox {
display: none;
}
body.unsigned > .register-form > .step-container > .step > .check-label > .checker {
width: 1.25rem;
height: 1.25rem;
border: 0.0625rem solid rgb(220, 220, 220);
border-radius: 50%;
margin-right: 0.625rem;
position: relative;
}
body.unsigned > .register-form > .step-container > .step > .check-label > .checkbox:checked + .checker {
background-color: rgb(var(--common-color-yellow-5));
border: 0.0625rem solid rgb(var(--common-color-yellow-5));
}
body.unsigned > .register-form > .step-container > .step > .check-label > .checker > .icon {
top: 50%;
left: 50%;
color: rgb(220, 220, 220);
font-size: 0.9rem;
position: absolute;
transform: translate(-50%, -50%);
}
body.unsigned > .register-form > .step-container > .step > .check-label > .checkbox:checked + .checker > .icon {
color: rgb(255, 255, 255);
}
body.unsigned > .register-form > .step-container > .step > .check-label > .text {
font-size: 1.0625rem;
font-weight: 500;
}
body.unsigned > .register-form > .step-container > .step > .all-warning {
border-bottom: 0.0625rem solid rgb(220, 220, 220);
color: rgb(150, 150, 150);
font-size: 0.8rem;
margin-bottom: 1rem;
padding-bottom: 1rem;
padding-left: 2.125rem;
text-align: justify;
}
body.unsigned > .register-form > .step-container > .step > .button {
background-color: rgb(var(--common-color-yellow-5));
border-radius: 0.25rem;
cursor: pointer;
margin-top: 2rem;
padding: 0.75rem 0;
}
body.unsigned > .register-form > .step-container > .step > .button:hover {
background-color: rgb(var(--common-color-yellow-4));
}
body.unsigned > .register-form > .step-container > .step > .button:active {
background-color: rgb(var(--common-color-yellow-3));
}
body.unsigned > .register-form > .step-container > .step > .button:disabled {
background-color: rgb(236,236,236);
color: rgb(180, 180, 180);
cursor: default;
}
@media screen and (max-width: 35rem) {
body.unsigned > .login-form.visible {
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 0;
justify-content: center;
transform: none;
}
body.unsigned > .register-form.visible {
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 0;
transform: none;
}
}
@media screen and (max-height: 45rem) {
body.unsigned > .login-form > .logo-container {
margin-bottom: 2rem;
}
body.unsigned > .login-form > .menu {
margin-top: 2rem;
}
}
@media screen and (max-height: 35rem) {
body.unsigned > .login-form.visible {
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 0;
justify-content: center;
transform: none;
}
}
@media screen and (max-height: 27.5rem) {
body.unsigned > .resolution {
display: flex;
}
body.unsigned > .login-form {
display: none;
}
body.unsigned > .register-form {
display: none;
}
}
-> index.unsigned.js
const Element = {
visibilityClassName: 'visible',
LoginForm: {
getElement: () => window.document.body.querySelector(':scope > [rel="loginForm"]'),
hide: () => Element.LoginForm.getElement().classList.remove(Element.visibilityClassName),
isShown: () => Element.LoginForm.getElement().classList.contains(Element.visibilityClassName),
show: () => Element.LoginForm.getElement().classList.add(Element.visibilityClassName)
},
RegisterForm: {
Progress: {
Value: {
getElement: () => Element.RegisterForm.Progress.getElement().querySelector(':scope > [rel="value"]')
},
getElement: () => Element.RegisterForm.getElement().querySelector(':scope > [rel="progress"]'),
setValue: (percent) => Element.RegisterForm.Progress.Value.getElement().style.width = `${percent}%`
},
Step: { // 약관동의 절차
Terms: { // 동의 버튼
Next: {
getElement: () => Element.RegisterForm.Step.Terms.getElement().querySelector(':scope > [rel=termsNext]')
},
names: [ //names 배열생성
{name: 'terms-no-kid', required: true},
{name: 'terms-term', required: true},
{name: 'terms-privacy', required: true},
{name: 'terms-profile', required: false}
],
getElement: () => Element.RegisterForm.Step.getContainer().querySelector(':scope > .step.terms'),
// terms의 구성요소로써 hide, isShown, show
hide: () => Element.RegisterForm.Step.Terms.getElement().classList.remove(Element.visibilityClassName),
isShown: () => Element.RegisterForm.Step.Terms.getElement().classList.contains(Element.visibilityClassName),
show: () => Element.RegisterForm.Step.Terms.getElement().classList.add(Element.visibilityClassName)
},
getContainer: () => Element.RegisterForm.getElement().querySelector(':scope > .step-container')
// Step의 요소는 getContainer는 함수 : step-container 약관동의의 내용이 담긴 div를 가져온다.
},
getElement: () => window.document.body.querySelector(':scope > [rel="registerForm"]'),
hide: () => Element.RegisterForm.getElement().classList.remove(Element.visibilityClassName),
isShown: () => Element.RegisterForm.getElement().classList.contains(Element.visibilityClassName),
show: () => Element.RegisterForm.getElement().classList.add(Element.visibilityClassName)
}
};
const Logic = {
register: () => {
Element.LoginForm.hide();
Element.RegisterForm.show();
}
};
window.document.body.querySelectorAll('a[data-logic]').forEach(x => {
x.addEventListener('click', e => {
e.preventDefault();
const dataLogic = x.dataset.logic;
if (typeof (Logic[dataLogic]) === 'function') {
Logic[dataLogic]();
}
});
});
// Element.RegisterForm.getElement()['terms-agree-all'].addEventListener('change', e => {
// //alert(e.target.checked);
// Element.RegisterForm.getElement()['terms-no-kid'].checked = e.target.checked;
// Element.RegisterForm.getElement()['terms-term'].checked = e.target.checked;
// Element.RegisterForm.getElement()['terms-privacy'].checked = e.target.checked;
// Element.RegisterForm.getElement()['terms-profile'].checked = e.target.checked;
// });
//모두 동의합니다 체크시 4가지 모두 체크된다.
Element.RegisterForm.getElement()['terms-agree-all'].addEventListener('change', e => {
Element.RegisterForm.Step.Terms.names.forEach(x => {
Element.RegisterForm.getElement()[x.name].checked = e.target.checked;
});
if (e.target.checked) {
Element.RegisterForm.Step.Terms.Next.getElement().removeAttribute('disabled');
} else {
Element.RegisterForm.Step.Terms.Next.getElement().setAttribute('disabled', 'disabled');
}
});
//모두 동의합니다 체크시 4가지 모두 체크된다. 에 대한 활성화 비활성화 '동의'가
Element.RegisterForm.Step.Terms.names.forEach(x => {
Element.RegisterForm.getElement()[x.name].addEventListener('change', e => {
if (!e.target.checked && Element.RegisterForm.getElement()['terms-agree-all'].checked) {
Element.RegisterForm.getElement()['terms-agree-all'].checked = false;
}// 4개 체크에 하나라도 빠지면 모두 동의한다라는체크가 빠진다는 의미 체크를 풀겟다.
if (Element.RegisterForm.Step.Terms.names
.filter(x => x.required === true)
.every(x => Element.RegisterForm.getElement()[x.name].checked)) { // 이 파트에 대한 설명
Element.RegisterForm.Step.Terms.Next.getElement().removeAttribute('disabled');
} else {
Element.RegisterForm.Step.Terms.Next.getElement().setAttribute('disabled', 'disabled');
} /* name 오브젝트에서 x.required === true 에 부합하는 애들만 걸러낸다. every는 전부 참일 때 반환한다.
x.name = name: `terms-term`이다 */
});
});
-> 현재까지 결과
- 모두 동의합니다 클릭시 4개 다 선택되도록 구현.
- 선택을 제외한 나머지를 다 선택시 동의버튼 활성화