배움 __IL/TIL 1기

TIL : 95번째- 230421 [4-3-금]

Mo_bi!e 2023. 4. 21. 18:10

I. DOM

1. 이벤트

(1) 이벤트 객체 클릭했을 때 태그 찾기

1) 클릭했을 때 색 바꾸기

<ul>
    <li class="current"><span>1</span></li>
    <li>
        <div>
        <span>2</span>
        </div>
    </li>
    <li><span>3</span></li>
    <li><span>4</span></li>
</ul>
.s2 ul{
    padding: 0;
    list-style-type: none;    
    display: flex;            
}            
.s2 span{
    display: inline-flex;                
    width:100px;
    height: 100px;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: bold;
    background-color: blue;

    /* transition: .5s; */

}  

.s2 li.selected .box,
.s2 li.selected span
{

    /* 애니메이션 넣기 */
    animation-name : selectedani;
    animation-duration: 2s;
    animation-direction: alternate;

}
let section = this.document.getElementsByTagName("section")[0];
let ul  = section.querySelector("ul");
let current = ul.querySelector("li.current");
let lis = ul.querySelectorAll("li");


// let lis = ?;
for(let li of lis)
    li.onclick = function(e){
        current = e.target;
        current.style.backgroundColor = "red";

    }

그런데 치명적인 오류가 있어! span에 다가 적용되는게 문제야!

2) span 적용 문제

current 는 타겟이 아니라 부모의 요소로 볼 수있어.

   for(let li of lis)
        li.onclick = function(e){
            // current = e.target;
            current = e.target.parentElement;
            console.log(current);
            current.style.backgroundColor = "red";

        }
}

li 태그가 클릭된다 그런데 클릭을 해도 색상은 바뀌지 않는다.

대상을 잘 잡아줘야해

 

3)span 적용문제 해결하기 -> li에 적용하기

let section = this.document.getElementsByTagName("section")[0];
let ul  = section.querySelector("ul");
let current = ul.querySelector("li.current");
let lis = ul.querySelectorAll("li");


// let lis = ?;
for(let li of lis)
    li.onclick = function(e){
        // current = e.target;
        current = e.target.parentElement;
        console.log(current);
        // current.style.backgroundColor = "red";
        current.firstElementChild.style.backgroundColor = "red";


    }

parentElement 를 선택해서 current에 넣는다

그러고 나서 firstElementChild로도 가능하다 (즉 첫번재 요소 만 잡아주기)

이렇게 하면 li가 잡히면서 가능해

 

4) 보다 바람직한 해결방법

ul태그 안span찾는 것을 해보자!

// let lis = ?;
for(let li of lis)
    li.onclick = function(e){
        // current = e.target;
        current = e.target.parentElement;
        // console.log(current);
        // current.style.backgroundColor = "red";
        // current.firstElementChild.style.backgroundColor = "red";


        //while문으로 이용방법
        let span = current.firstElementChild;
        while(span.tagName !== "SPAN") //대문자로!
            span = span.firstElementChild;
        console.log(span);

        //??????
        span.style.backgroundColor = "red";


        //for문 이용방법
        // for(let span = current.firstElementChild;
        //     span.tagName !== "span";
        //     span = span.firstElementChild);
    }

span 에서 tagName 이 SPAN일 때 까지 찾는방식이야

그리고 찾았을 때 해당 요소를 span안에 대입하는 방식이야

이 방식을 기본으로 두고 꼭하자!!

 

 

(2) 클릭 했을 때 스타일 변경 시키기

1) li 태그 찾아주기와 클래스name이 current인 것 찾기

 

li태그 current찾고 나서, span에다가 스타일 적용하는것이 목적이다

- current 잡아주기

<ul>
    <li class="current"><span>1</span></li>
    <li>
        <div>
        <span>2</span>
        </div>
    </li>
    <li><span>3</span></li>
    <li><span>4</span></li>
</ul>

li 태그안 div태그가 잡히는것을 방지하고 span까지 가기위해서 만들어진 코드

// 뭘하는거임??
current = e.target.parentElement;
while(current.tagName !== "LI") //대문자로!
current = current.firstElementChild;

 

 

현재 클릭한것에서 li찾기

 

2) 태그의 클래스명을 바꾸어서 본 스타일을 적용시키는 방식

{
    let section = this.document.getElementsByTagName("section")[0];
    let ul  = section.querySelector("ul");
    let current = ul.querySelector("li.current");
    let lis = ul.querySelectorAll("li");

    for(let li of lis)
        li.onclick = function(e){
            // 뭘하는거임?? 
            current = e.target.parentElement;
            while(current.tagName !== "LI") //대문자로!
                current = current.firstElementChild;

            //while문으로 이용방법
            let span = current.firstElementChild;
            while(span.tagName !== "SPAN") //대문자로!
                span = span.firstElementChild;
            console.log(span);

            // 이렇게 스타일을 적용하는 것은 바람직하지 않다.
            // 왜냐 나중에 생각이 바뀌어서 다양하게 하고싶은데, 스타일을 코드로 하면안된다
            //js로 한다는거는 동적인것인데, 시작할 때 거는 미리 준비하게 해야한다
            //이렇게 보다는 현재 선택된 녀석의 스타일의 클래스명을 바꾸자!!
            span.style.backgroundColor = "red";
            console.log(current);
        }
}

스타일은 js로 하기보다, 미리만들어 놓을 수 있는 스타일은 만든다

그리고 태그의 클래스명을 바꾸어서 본 스타일을 적용시키는 방식으로 하자

.s2 li.current .box,
.s2 li.currnet span
{
    background-color: red;
}

 

li 태그에, current라는 클래스를 추가해주는 것을 고민해보자

    // 4. 이벤트 객체
    {
        let s1 = document.querySelector(".s2");
        let ul = s1.querySelector("ul");
        let current = ul.querySelector("li.current");
        let lis = ul.querySelectorAll("li");

        for(let li of lis)
            li.onclick = function(e){
                //* 현재 선택된 노드를 찾고
                current = e.target.parentElement;
                while(current.tagName !== "LI")
                    current = current.parentElement;
                //* 선택된 노드임을 표시하는 스타일 변경
                current.classList.toggle("current");
                

                console.log(current);
            }
    }

current.classList.toggle 를 이용한다

토글 방식으로 current를 넣었다가 빼엇떤 방식으로 on/off 방식으로 설정이 쉽게 가능하다.

(3) 두개 클릭했을 때 서로 바꾸기

1) 콘솔로 띄어보기

두개를 클릭했을 때 두개를 맞바꾸는 것을 구현해보자!

 

같은 부분을 두번 클릭했을 때 콘솔에 띄우는 방법을 알아보자

 

두가지 방법이 있는데, 모든 요소들을 순회하는 방법보다, 그 부분만 셀렉터를 이용해서 들고 온 뒤에 log를 띄어주면 된다.

//다음처럼 for문을 사용하는 코드는 바람직하지 않다
{
let lis = ul.querySelectorAll("li");
let hasCurrent = false;
for(let li of lis){
    if(li.classList.contains("current")){
        hasCurrent = true;
        console.log()
    }
    break;

}
}

//위의 방법보다 다음처럼 그냥 있는지 쉽게 확인할 수 있다
if(ul.querySelector("li.current"))
    console.log("두번째 선택");

이 경우 위와같이 두가지 방법이 있다

 

첫번째는 for문으로 순회하면서 같은게 있는 찾는 방법

두번쨰는 css 셀렉터 방식을 이용해서 바로 찾는 방식이다 (부모로 올라가서!)

성능 등 다양한 방식을 고려 하면후자가 바람직 하다고 볼 수 있다.

 

2) 바꿔주기

{
    let s1 = document.querySelector(".s2");
    let ul = s1.querySelector("ul");
    let current = null; // ul.querySelectorAll("li.current");
    let lis = ul.querySelectorAll("li");

    for(let li of lis)
        li.onclick = function(e){

	if(ul.querySelector("li.current"))
   		let selected = ul.querySelector("li.current");
    
    if(current){

    // *** 실습 3 ; 박스 위치 바꾸기,  selected.classList.add("selected"); 이용!!

//공식
    let selected = e.target.parentElement;
    while(selected.tagName !== 'LI')
        selected = e.parentElement;

    selected.classList.add("selected");

//선택 된것 앞부분의 위치를 저장한다
    let selectedBefore = selected.previousElementSibling;
//replaceWith를 이용해서 요소를 넣어준다
    current.replaceWith(selected);

//저장한 위치가 있으면 거기 뒤에 current를 넣어준다.
    if(selectedBefore)
        selected.after(current);
    else    //엎으면 앞에 넣어준다.
        ul.prepend(current);

//기존것은 제거해준다
    current.classList.remove("current");
//이전에 클릭했던 내용을 없애준다.
    current=null;

//색변화는 안되게끔해준다.
    return;
 	}   
}

1 append : dom tree에 추가

 

2 prepand : 특정 요소의 첫번째 자식앞에다가 매개변수의 요소를 넣어준다.

 

3 replaceWith : 

Element.replaceWith() 

Element는 부모가 있는 자식이다.

이 자식대신에 () 매개변수를 넣어주면 매개변수 값이 대신 위치하게 되는 것을 의미한다.

 

 

4 after

let container = document.createElement("div");
let p = document.createElement("p");
container.appendChild(p);
let span = document.createElement("span");

p.after(span);

console.log(container.outerHTML);


// "<div><p></p>   <span></span>  </div>"

after은 엘레먼트 끝나고 바로다음에 넣어주는 방식이다.

span은 p 태그 다음에 들어왔다.

 

 

4가지 방식으로 위 문제를 해결 할 수 있다

 

2.  CSS

(1) transform 과 trasition

1) transform

.s2 li.current span
{
    background-color: red;
    border: 3px solid yellow;
    width: 120px;
    height: 120px;

    /* 다른 크기 영향안받고 해주기 */
    transform: scale(1.2, 1.2);

}

이렇게 하면 크기를 변형할 수있다.

 

2) trasition

일정 기간에 걸쳐 일어나게끔 하는것이 목적이다.

.s2 span{
    display: inline-flex;                
    width:100px;
    height: 100px;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: bold;
    background-color: blue;

    transition: .5s;
}

.5s 를 하면 0.5초 동안 변화할 수있다.

(2) 애니메이션

1) 의의

mdn 의 말처럼 이는 스타일의 변화를 부드럽게 하기 위함이다.

 

 

2) @keyframes

애니메이션관련

본 내용과 같이 trasitions 와 변화를 할 때 의 중간단계에 관련해서 컨트롤할 수있는 방법이다

각 시퀀스 중간중간에 다양한 방식으로 넣어 줄 수있다.

@keyframes zoomin {
    /* 처음에는 얼마였다가 */
from { 
    transform: translateX(1,1);
}
/* 50% 왔을 때 다른값을 할 것이다. */
/* 50%{
} */
    /* 나중에는 얼마로 바뀔 것이다. */
to {
    transform: translateX(1.2,1.2);
}
}

1.1 에서 2.2까지

.s2 li.current span
    {
        background-color: red;
        border: 3px solid yellow;
        width: 120px;
        height: 120px;

        /* 다른 크기 영향안받고 해주기 */
        /* transform: scale(1.2, 1.2); */

        /* 애니메이션 넣기 */
        animation-name : zoomin;
        animation-duration: 1s;
        animation-iteration-count: infinite;
        animation-direction: alternate;

    }

그리고 애니메이션을 적용하고 넣기 위해서는 다음과 같이 애니메이션을 넣어주어야해

이름, 기간, 반복, 방향 이런것을 넣어주면돼

 

- duration은 한싸이클의 애니메이션이 얼마에 걸쳐일어날지

- interation-count 는 몇번 반복될지이다. (이 경우 infinite 이면 무한반복이다)

- direction 은 다시 시작할디지 역방향으로 할지 지정하는 것이다. (alternate 는 주기마다 방향을 바꾸는 것이다.)

 

그런데 애니메이션이나 트랜지션은 같이 쓰면안돼 

둘 다 있으면 그 효과가 중복되기 때문이야

 

3) 다양한 애니메이션 설정

애니메이션 관련 조건 설정 -> 애니메이션 이벤트

애니메이션의 어떤 이벤트가 있는지 확인해야해

@keyframes zoomin {
    /* 처음에는 얼마였다가 */
from { 
    transform: translateX(1,1);
}
/* 50% 왔을 때 다른값을 할 것이다. */
30%{
    transform: rotate(360deg);
}

60%{
    transform: rotate(-360deg);
}
    /* 나중에는 얼마로 바뀔 것이다. */
to {
    transform: translateX(1.2,1.2);
}
}

@keyframes currentani {
    /* 처음에는 얼마였다가 */
from { 
    transform: translateX(1,1);
}
    /* 나중에는 얼마로 바뀔 것이다. */
to {
    transform: translateX(1.2,1.2);
}
}

@keyframes selectedani {
    /* 처음에는 얼마였다가 */
from { 
    transform: rotate(0deg);
}
to {
    transform: rotate(360deg);
}
}

.s2 ul{
    padding: 0;
    list-style-type: none;    
    display: flex;            
}            
.s2 span{
    display: inline-flex;                
    width:100px;
    height: 100px;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: bold;
    background-color: blue;

    /* transition: .5s; */

}  

.s2 li.selected .box,
.s2 li.selected span
{

    /* 애니메이션 넣기 */
    animation-name : selectedani;
    animation-duration: 2s;
    animation-direction: alternate;

}

.s2 li.current .box,
.s2 li.current span
{
    background-color: red;
    border: 3px solid yellow;
    width: 120px;
    height: 120px;

    /* 다른 크기 영향안받고 해주기 */
    /* transform: scale(1.2, 1.2); */

    /* 애니메이션 넣기 */
    animation-name : zoomin;
    animation-duration: 1s;
    animation-iteration-count: infinite;
    animation-direction: alternate;

}
{
let s1 = document.querySelector(".s2");
let ul = s1.querySelector("ul");
let current = ul.querySelector("li.current");
let lis = ul.querySelectorAll("li");

for(let li of lis)
    li.onclick = function(e){

        if(current){
            console.log("두번째 선택");
            //공식!!!
            let selected = e.target.parentElement;
            while(selected.tagName !== "LI")
                selected = selected.parentElement;
            console.log(current);

            selected.classList.add("selected");

            return;
        }


        //* 현재 선택된 노드를 찾고
        current = e.target.parentElement;
        while(current.tagName !== "LI")
            current = current.parentElement;
        //* 선택된 노드임을 표시하는 스타일 변경
        current.classList.toggle("current");


        console.log(current);
    }
}

결국 이런 경우에 

애니메이션 이벤트발생조건으로 selected 인경우 current 인경우 각각을 넣어주면된다


1. 보충

(1)

 

 

 

 

 

 

2. 회고 

1) 바람직한 방법으로 dom 조작과 css 컨트롤은 녹록치 않다

선생님이 말씀하신것처럼 이것은 긴 기간 해보면서 익혀지는것이라 하셨으니, 당장 욕심을 내기보다 추후에 하면서 한번씩 다시 보는 방식도 괜찮을 것같다.

'배움 __IL > TIL 1기' 카테고리의 다른 글

TIL : 98번째- 230426 [4-4-수]  (0) 2023.04.26
TIL : 97번째- 230425 [4-4-화]  (1) 2023.04.25
TIL : 94번째- 230420 [4-3-목]  (0) 2023.04.20
TIL : 93번째- 230419 [4-3-수]  (4) 2023.04.19
TIL : 92번째- 230418 [4-3-화]  (0) 2023.04.18