프로젝트 정리

[노마드코더 js 웹페이지] ToDoList페이지 정리

래모 2022. 1. 18. 23:49

기능들은 강의에서 다 했던 것들이라 따로 정리해서 여기다는 안 쓸거임

https://sand8594.tistory.com/13 

https://sand8594.tistory.com/14

https://sand8594.tistory.com/15

https://sand8594.tistory.com/16

https://sand8594.tistory.com/17

 

[노마드코더] 바닐라JS 공부 7일차(To Do List)

기본 세팅 // Setup 7.0 html <!DOCTYPE html> Momentum App js const toDoForm = document.getElementById("todo-form"); const toDoInput = toDoForm.querySelector("input"); const toDoList = document.getEle..

sand8594.tistory.com

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Momentum App</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Do+Hyeon&display=swap" rel="stylesheet">
    <style>
        * {
            font-family: 'Do Hyeon', sans-serif;
        }
    </style>
    <link rel = "stylesheet" href = "style.css">
</head>
<body>
    <div class = "momentum">
        <header>
            <div class = "header-container" id = "todolist-string">To Do List</div>
            <div class = "header-container" id = "login-wrap">
                <form class = "hidden" id = "login-form">
                    <input required maxlength = "15" type = "text" placeholder = "What is your name?"/>
                    <button>Log In</button>
                </form>
                <h1 id = "greeting" class = "hidden"></h1>
            </div>
            <div class = "header_container" id = "date&clock">
                <div id = "now_date"></div>
                <div id="clock">00:00:00</div>
            </div>
        </header>
            
        
        <form id = "todo-form">
            <input required type="text" placeholder="Write a To Do and Press Eneter">
        </form>
        <ul id = "todo-list">
            
        </ul>
        <div id="quote">
            <span></span>
            <span></span>
        </div>
        <div id ="weather">
            <span></span>
            <span></span>
    </div>
    
    </div>
    <script src = "js/clock.js"></script>
    <script src = "js/greeting.js"></script>
    <script src = "js/quotes.js"></script>
    <script src = "js/background.js"></script>
    <script src = "js/todo.js"></script>
    <script src = "js/weather.js"></script>
</body>
</html>

기능만 구현한 초기 페이지

2022.01.18

momentum 클래스 설정

하얀 색 박스가 정가운데 오도록 해야함

 

가운데 정렬하기

.momentum {
  margin: 0 auto; 
}

0은 위 아래 여백을 주지 않는다는 의미이고 auto는 가로 중앙에 배치한다는 뜻이다.

이걸 쓰면 그냥 이렇게 가운데에 띡 정렬됨

박스 모델 말고 img나 text는 text-align:center 사용

 

나는 위를 좀 띄우고 싶으므로

margin: 75px auto로 설정했음

 

나머지 박스 크기들이나 이런거 다 해서 momentum 클래스 css설정은 이렇게 마무리되었음

.momentum {
  margin: 75px auto;
  max-width: 900px;
  padding: 30px;
  width: 65%;
  background-color: rgb(250, 248, 248);
  border-radius: 10px;
  box-shadow: 5px 5px 5px 5px rgb(163, 163, 163);
}

 

전체적인 설정 완료

header 설정

header의 요소 세개 (todolist글귀, greeting, 날짜)는 서로 한 줄에 출력하고 싶음

그래서 grid 쓸 거임 (flex 써도 됨)

 

grid에 대한 자세한 설명은 이 포스팅이 제일 좋음! 참고

https://studiomeal.com/archives/533

 

이번에야말로 CSS Grid를 익혀보자

이 포스트에는 실제 코드가 적용된 부분들이 있으므로, 해당 기능을 잘 지원하는 최신 웹 브라우저로 보시는게 좋습니다. (대충 인터넷 익스플로러로만 안보면 된다는 이야기) 이 튜토리얼은 “

studiomeal.com

header {
  display: grid;
  grid-template-columns: 1fr 3fr 1fr;
}

부모 요소에 display:gird 이렇게 설정하고 나는 1:3:1 비율이 좋을 거 같아서 각각 fr로 작성해주었다.

 

header 중 greeting 설정

로그인 버튼이 별로 실용성이 없는 것 같아서 없앴고

이름 입력받는 창의 테두리를 배경과 같은 색으로 설정하도록 했음

=> js 파일 수정

loginForm.style.borderColor = chosenColor;

이거만 쓰면 됨

 

최종 css는 이렇게

#login-form {
  border: 3px solid;
  border-radius: 10px;
  width: 250px;
  margin: 0 auto;
}
#login-input {
  font-size: 25px;
  outline: none;
  border: none;
  text-align: center;
}
#greeting {
  font-size: 30px;
}

이름 쓰기 전 후


2022.01.22

배경색 설정

갑자기 그라데이션 해서 넣고 싶음 그래서 background.js를 이렇게 변경해주었음

const colors = [
    "#f7b4be", 
    "#f7b4be", 
    "#f6a88a", 
    "#b2dbba", 
    "#c3e2df", 
    "#d3afd5"
];

const chosenColor1 = colors[Math.floor(Math.random() * colors.length)];
const chosenColor2 = colors[Math.floor(Math.random() * colors.length)];

document.body.style.background = `linear-gradient(${chosenColor1}, ${chosenColor2})`;

근데 이렇게 하니까..?

요따구로 됨...

이걸 우째해야 할까 하다가

노마드코더 챌린지에서 한 코드를 살짝 참고했음

거기는 바디에 height:100vh; width:100%로 했던데

body {
  text-align: center;
  height: 91vh;
}

이정도로만 넣어줬음

이러니까 딱 맞아!


2022.01.21

To Do List 꾸미기

할 일 입력받는 창 다시 꾸미고 추가로 체크 버튼을 넣어서 해당 일을 했는지 안 했는지 표기해줄거임

 

1️⃣ 목록을 적으면 투두리스트에 대한 박스 생기기

투두리스트에 목록이 존재하냐 안 하냐로 체크해주었음

즉 todos의 길이가 0이면 없어지고 1이상이면 생기게 해주었다.

 

2️⃣ 마우스 올리면 체크버튼과 삭제버튼이 뜨고 벗어나면 없어지게 하기 

이건 addEventListener로 각각 mouseover과 mouseout으로 관리

아 그리고 체크버튼이 눌러져 있는 상태이면 innerText 를 🗸이거로 유지되게 하였음

 

3️⃣ 체크버튼 누르면 글씨의 색이 바뀌고 밑줄 넣기

.style.color = chosenColor1로 해주고

밑줄은 text-decoration으로!

text-decoration: none | line-through | overline | underline | initial | inherit
  • none : 선을 만들지 않습니다.
  • line-through : 글자 중간에 선을 만듭니다.
  • overline : 글자 위에 선을 만듭니다.
  • underline : 글자 아래에 선을 만듭니다.
  • initial : 기본값으로 설정합니다.
  • inherit : 부모 요소의 속성값을 상속받습니다.

값 자체를 숫자로 넣어도 가능

 

아무튼 다 하면 이런 형태가 됨(css파일이랑 todo.js 엄청 수정했음)

 

 

수정한 todo.js 랑 css 는 이러함

ul {
  list-style: none;
  border: 3px solid;
}
button {
  border: 0;
  outline: 0;
  background: none;
}
#todo-list {
  width: 350px;
  margin: 0 auto;
  margin-top: 20px;
  padding-left: 0;
}
li {
  display: grid;
  grid-template-columns: 1fr 3fr 1fr;
  margin: 10px;
  height: 30px;
}
.li-span {
  text-align: left;
  margin-left: 10px;
  color: #4e4c4c;
}

todo.js

const toDoForm = document.getElementById("todo-form");
const toDoInput = document.getElementById("todo-input");
const toDoList = document.getElementById("todo-list");

toDoInput.style.background = `linear-gradient(${chosenColor1}, ${chosenColor2})`;

const TODOS_KEY = "todos";
const UNCHECKED_CLASSNAME = "unchecked"

let toDos = [];

function saveToDos() {
    localStorage.setItem(TODOS_KEY, JSON.stringify(toDos)); // localStorage에 문자열 형태로 넣어줌
}

function deleteToDo(e) {
    const li = e.target.parentElement; // 부모요소 얻기
    //const li = this.parentElement;와 같음
    
    li.remove(); // 해당 부모 요소 삭제
    toDos = toDos.filter((todo) => todo.id !== parseInt(li.id)); // 배열에서도 찾아서 삭제해줌
    // id는 문자열형태로 보이기 때문에 비교를 위해서 int로 바꿔줘야함
    saveToDos(); // 새로운 toDos로 저장
    if (toDos.length == 0) {
        toDoList.style.display = "none";
    }
}

function checkToDo(e) {
    const li = e.target.parentElement; // 부모 요소 얻기
    const checkBtn = li.children[0]; // 부모의 자식들 중 첫번째
    const span = li.children[1]; // 두번쨰
    
    if (li.className == UNCHECKED_CLASSNAME) { // li의 클래스네임 확인 체크되어있지 않은 상태였다면
        span.style.textDecoration = "line-through";
        span.style.color = chosenColor1;
        checkBtn.innerText = "🗸"; // 체크표시하고 글자밑줄, 색 바꿔줌
        li.classList.remove(UNCHECKED_CLASSNAME); // 그리고 unchecked지워줌
    }else{ // 체크되어 있는 상태였다면
        span.style.textDecoration = "none";
        span.style.color = "#4e4c4c";
        checkBtn.innerText = " "; // 원 상태로 복귀
        li.classList.add(UNCHECKED_CLASSNAME); // 다시 unchecked 추가
    }

}

function paintToDo(newTodoObj) {
    toDoList.style.display = "";

    const li = document.createElement("li"); // li태그 생성
    const span = document.createElement("span"); // span태그 생성
    const removeBtn = document.createElement("button"); // remove button 태그 생성
    const checkBtn = document.createElement("button"); // check button 태그 생성;

    span.innerText = newTodoObj.text; // span 내에 문구를 넣음
    removeBtn.innerText = "🗑"; // button 내에 x를 넣음
    checkBtn.innerText = " ";
    
    removeBtn.style.color = chosenColor1;
    removeBtn.style.fontSize = "20px";
    checkBtn.style.fontSize = "20px"
    checkBtn.style.color = chosenColor1;
    checkBtn.style.borderColor = chosenColor1;
    checkBtn.style.borderRight = "3px solid"

    span.classList.add("li-span");
    removeBtn.classList.add(HIDDEN_CLASSNAME);
    removeBtn.classList.add("removeBtn");
    checkBtn.classList.add("checkBtn");
    li.id = newTodoObj.id; // li태그에 id부여
    
    li.appendChild(checkBtn);
    li.appendChild(span); // li로 span 감싸기
    li.appendChild(removeBtn); // li로 button 감싸기
    
    li.classList.add(UNCHECKED_CLASSNAME);
    toDoList.appendChild(li); // todoList 안에 li 넣기

    checkBtn.addEventListener("click", checkToDo);
    li.addEventListener("mouseover", function() {
        removeBtn.classList.remove(HIDDEN_CLASSNAME);
        checkBtn.innerText = "🗸";
    })
    li.addEventListener("mouseout", function() {
        removeBtn.classList.add(HIDDEN_CLASSNAME);
        if(li.className == UNCHECKED_CLASSNAME) {
            checkBtn.innerText = " ";
        }else {
            checkBtn.innerText = "🗸";
        }
    })
    removeBtn.addEventListener("click", deleteToDo); // click시 해당 목록 삭제
    checkBtn.addEventListener("click", checkToDo)
    toDoList.classList.remove(HIDDEN_CLASSNAME);
    toDoList.style.borderRadius = "5px";
    toDoList.style.borderColor = chosenColor1;
}

function handleToDoSubmit(e) {
    e.preventDefault(); // 새로고침 방지
    
    const newTodo = toDoInput.value; // todoInput의 value를 가져와서 저장
    
    toDoInput.value = ""; // 초기화시켜줌

    const newTodoObj = { // 각 요소들을 분리해주기 위해 id가 부여된 객체 생성
        text: newTodo,
        id: Date.now(),
    };

    toDos.push(newTodoObj); // newTodoObj를 배열에 저장
    
    paintToDo(newTodoObj); // newTodoObj를 목록에 추가함
    saveToDos();
}

toDoForm.addEventListener("submit", handleToDoSubmit); // 엔터키 누르면 함수 호출

const savedToDos = localStorage.getItem(TODOS_KEY); // localStorge에서 아이템 얻기

if (savedToDos !== null) { // 비어있지 않다면
    const parsedToDos = JSON.parse(savedToDos); // 배열로 바꿔줌
    toDos = parsedToDos; // toDos배열에 parsedToDos배열을 대입
    parsedToDos.forEach(paintToDo); // 각 요소에 대해 paintToDo함수 실행

}

여기서 문제점!! 내가 한 상태로만 하면 새로고침 후에 내가 했다고 체크해둔 요소가 다시 초기화 됨...

이것도 localStorage 써야하나..? 잘 모르겠음 일어나서 해야지


2022.01.23

 

해결하지 못했던 체크상태 기억하기를 해두려고 함

각각 li에 id를 넣어줬으니 그걸 활용해서 localStorage에 넣은 다음 항상 체크하게 하면 되지 않을까?

 

시벌.... 체크 눌러진 애들을 localStorage에 저장하기 까진 됐는데 그걸 활용해서 밑줄 긋기가 안 됨...

일단 저장하는 건 이렇게 했음

let checkToDos = [];

function saveCheckedToDos() {
    localStorage.setItem(CHECECKTODOS_KEY, JSON.stringify(checkToDos)); // localStorage에 저장
}

function checkToDo(e) {
    const li = e.target.parentElement; // 부모 요소 얻기
    const checkBtn = li.children[0]; // 부모의 자식들 중 첫번째
    const span = li.children[1]; // 두번쨰
    
    if (li.className == UNCHECKED_CLASSNAME) { // li의 클래스네임 확인 체크되어있지 않은 상태였다면
        span.style.textDecoration = "line-through";
        span.style.color = chosenColor1;
        checkBtn.innerText = "🗸"; // 체크표시하고 글자밑줄, 색 바꿔줌
        li.classList.remove(UNCHECKED_CLASSNAME); // 그리고 unchecked지워줌

        var checkedTodoObj;
        for(var i = 0; i < toDos.length ; i++) {
            if (parseInt(li.id) === toDos[i].id){
                checkedTodoObj = toDos[i];
            }
        }
        
        checkToDos.push(checkedTodoObj); // checkToDos배열에 넣기
        
    }else{ // 체크되어 있는 상태였다면
        span.style.textDecoration = "none";
        span.style.color = "#4e4c4c";
        checkBtn.innerText = " "; // 원 상태로 복귀
        li.classList.add(UNCHECKED_CLASSNAME); // 다시 unchecked 추가
        
        checkToDos = checkToDos.filter((checktodo) => checktodo.id !== parseInt(li.id));
        
    }
    
    saveCheckedToDos();
}

 

목록들 저장한 거 처럼 밑에서 따로 처리해주려고 했는데 안 됨...일단 제출해야해서 포기하고 이렇게 마무리..

 

명언이랑 날씨정보 꾸미기

명언은 그냥 기울임꼴 넣어서 지금처럼 넣어줄 거고 날씨정보는 왼쪽 상단에 넣어줄거다.

 

font-sytle

font-style: normal | italic | oblique | initial | inherit ;
  • normal : 보통 모양입니다.
  • italic : 기울임꼴입니다.
  • oblique : 기울임꼴입니다.
  • initial : 기본값으로 설정합니다.
  • inherit : 부모 요소의 속성값을 상속받습니다.

 

position

positon: static | reletive | absolute | fixed | stickey ;
  • static : 기준 없음(배치 불가능 / 기본값)
  • relative : 요소 자기 자신을 기준으로 배치
  • absolute: 부모(조상) 요소를 기준으로 배치
  • fixed : 뷰 포트 기준으로 배치
  • stickey : 스크롤 영역 기준으로 배치

추가로 Top, Bottom, Left, Right 설정

  • top : 요소의 position 기준에 맞는 위쪽에서의 거리(위치)를 설정
  • bottom : 요소의 position 기준에 맞는 아래쪽에서의 거리(위치)를 설정
  • left : 요소의 position 기준에 맞는 왼쪽에서의 거리(위치)를 설정
  • right : 요소의 position 기준에 맞는 오른쪽에서의 거리(위치)를 설정

나는 왼쪽 상단에 넣고 싶으니까 positon: absolute; left: 0; top: 0; 이렇게 설정해주었다.

 

최종적으로 완성!

최종 코드는 여기

https://github.com/juhui88/JavaScriptStudy

 

GitHub - juhui88/JavaScriptStudy

Contribute to juhui88/JavaScriptStudy development by creating an account on GitHub.

github.com