바닐라코딩 Starter Kit - Step 5 - Calendar

🎯 TO DO

  1. 오늘의 현재 요일 표기
  2. 오늘의 현재 날짜 표기
  3. 오늘의 현재 월 표기
  4. 오늘의 현재 연도 표기
  5. 일,월,화,수,목,금,토 요일 라벨링 표기
  6. 현재 월의 1일이 무슨 요일인지 판별하고, 해당 요일 라벨링에 1일 표기하기
  7. 현재 월의 마지막 날짜까지 달력에 표기하기
  8. 우측 화살표를 클릭 했을때, 다음 달의 요일 및 날짜 표기
  9. 좌측 화살표를 클릭 했을때, 이전 달의 요일 및 날짜 표기
  10. 특정 날짜를 클릭 했을때, 상단의 요일 및 날짜 반영하기

🤔 과정

  • STEP 3 This Month 에서는 자바스크립트만으로 조작을 했었고, 각각의 칸이 시각적으로 보여서 빈공간이 있으면 별로일것같아 필요한 칸만 생기도록 했었는데, 여긴 빈칸은 투명해서 안보이므로 html 에서 table 을 애초에 6줄로 만들었다
  • 자바스크립트의 기능 구현에 초점을 둬서 스타일은 최소화 하였다. css도 자바스크립트에서 클래스로 잡기위해 만들어준 요소들이 많다
  • 해당 월의 첫날과 마지막 날이 핵심이였다. 그 중에서도 특히 첫날이 핵심이였다
  • 오른쪽 버튼을 누르면 n 의 카운트가 올라가고, 첫날도 그에 맞게 월이 바뀌게 한후 다시 달력 요소들이 그 첫날에 맞게 채워지게 했다
  • 처음엔 그냥 채워지게했더니 기존것이 남아 있어서 이상하게 뜨는 경우가 있어서, 6줄의 칸이 모두 지워지는 for문을 넣어줬다(42번 돌아감)
    • 생각해보니 어차피 달력의 첫번째 줄은 최고 안쓸때가 토요일이 1일로 시작하는 경우라 금요일까지 지우면 되고, 끝줄은 최고 안쓸떄가 1일이 일요일로 시작하는 28일인 경우라 뒤에 두줄이 안쓰일 때이고, 최고 많이 쓰이는 경우가 토요일이 1일로 시작하여 31일인 달이 와서 6번째 줄 월요일에서 31일로 끝나는 날이므로 해당 칸만 지워주면 조금더 효율적인 코드가 될거라 생각해 바꿔줌(15번 돌아감)
  • 클릭한 날짜로 위의 정보가 바뀌게 하는건 해당 유사 배열 td 오브젝트의 요소 하나마다 모두 에드이벤트리스너를 줘야해서 for문으로 처리함
  • 모든 td 칸을 주면 빈칸을 누르게되면 빈 정보가 나오는 원치않는 결과가 나올수있을거같아, 날짜가 표기되어있는 칸만 기록하는 existTd 배열을 만들어 for문 조건에 활용하여 값이 있는 td 만 에드이벤트리스너가 들어가게함
  • 다음달로 넘어가서 에드이벤트리스너가 걸려있던 td 를 다시 풀어줘야했는데 리무브이벤트리스너가 제대로 작동하지 않음
  • 아래 사진처럼 익명함수로 콜백함수를 쓰면 리무브이벤트리스너를 사용할 수 없대서 기명함수를 만들기로함
  • 기명함수에 인자를 i를 받게 해놓고, 에드이벤트리스너의 콜백함수 자리에 changeInfo(i) 이런식으로 넣어두니 클릭을 하지 않아도 실행이 됐음. 할수를 호출하는 형식으로 써서 그런것 같음(예전에도 겪었던 문제 : 링크)
  • 링크에 있는 것처럼 익명함수에 기명함수를 넣는식으로 하니 정상작동했으나 콜백함수 자체에 들어가있는건 익명함수라 리무브이벤트리스너를 쓸 수 없었음
  • 💡 에드이벤트리스너에 넣는 함수는 함수명만 적어야했기에 i를 전달해서 for 문을 이용하는 방법을 쓸수가 없었음. 클릭시 넘어오는 event 매개변수를 어떻게 활용할 수 있을까 로그를 찍고 프로토타입을 뒤져보니 요일 정보를 구현하는데 필요한 target 안에 있는 cellIndex 와 해당 요소의 텍스트를 읽는데 필요한 srcElement 안에 있는 innerText 가 있었음. 이 둘을 이용하여 10. 특정 날짜를 클릭 했을때, 상단의 요일 및 날짜 반영하기 를 구현함
  • 달을 넘길때마다 필요없는 innertextaddEventListener 를 지워주는 코드를 오른쪽 왼쪽 버튼을 누를때 나오는 콜백함수에 넣어줌

📝 코드

📙 HTML

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <link
      href="https://fonts.googleapis.com/css?family=Pacifico&display=swap"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/css?family=Varela+Round&display=swap"
      rel="stylesheet"
    />
    <link rel="icon" href="/images/favicon.ico" />
    <link rel="stylesheet" type="text/css" href="style.css" />
    <title>Calendar Project</title>
  </head>

  <body>
    <section>
      <div class="image-box">
        <img src="images/vanilla_coding_logo.png" />
      </div>
      <h1>Calendar</h1>

      <!-- 달력 Start -->
      <main>
        <section class="first-box">
          <h2 class="this-day"></h2>
          <h2 class="this-date"></h2>
          <h3 class="this-month-year"></h3>
        </section>
        <section class="second-box">
          <button class="left-button"><<</button>
          <table>
            <!--요일 줄-->
            <tr>
              <th>SUN</th>
              <th>MON</th>
              <th>TUE</th>
              <th>WED</th>
              <th>THU</th>
              <th>FRI</th>
              <th>SAT</th>
            </tr>
            <!--1주-->
            <tr>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
            </tr>
            <!--2주-->
            <tr>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
            </tr>
            <!--3주-->
            <tr>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
            </tr>
            <!--4주-->
            <tr>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
            </tr>
            <!--5주-->
            <tr>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
            </tr>
            <!--6주-->
            <tr>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
            </tr>
          </table>
          <button class="right-button">>></button>
        </section>
      </main>
      <!-- 달력 End -->
    </section>

    <script src="index.js"></script>
  </body>
</html>

📘 CSS

body {
  background-image: url("./images/bg.jpeg");
  background-repeat: no-repeat;
  background-size: cover;
  font-family: "Varela Round", sans-serif;
}

h1 {
  font-family: "Pacifico", cursive;
  text-align: center;
  font-size: 72px;
}

main {
  height: 30rem;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.first-box {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.second-box {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 12rem;
  width: 20rem;
}

table {
}

.left-button {
  background-color: transparent;
  border: 0px;
}

.right-button {
  background-color: transparent;
  border: 0px;
}

td {
  text-align: center;
}

.this-day {
}

.this-date {
}

.this-month-year {
}

📕JS

//변수
const date = new Date();
let n = 0;
let firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
let lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
const td = document.querySelectorAll('td');
const days = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'];
const months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
let existTd = [];
const thisDay = document.querySelector('.this-day');
const thisDate = document.querySelector('.this-date');
const thisMonthYear = document.querySelector('.this-month-year');
const leftButton = document.querySelector('.left-button');
const rightButton = document.querySelector('.right-button');


//초기화면 구성
thisDay.textContent = days[date.getDay()];
thisDate.textContent = `${date.getDate()}`;
thisMonthYear.textContent = `${months[date.getMonth()]} ${date.getFullYear()}`;

for (let i = 0; i < lastDay.getDate(); i++) {
    td[firstDay.getDay() + i].textContent = i + 1;
    existTd.push(firstDay.getDay() + i);
}


//이벤트에 연결하는 함수
function nextMonth() {
    n++
    existTd = [];
    firstDay = new Date(date.getFullYear(), date.getMonth() + n, 1);
    lastDay = new Date(date.getFullYear(), date.getMonth() + n + 1, 0);
    thisDay.textContent = days[firstDay.getDay()];
    thisDate.textContent = `${firstDay.getDate()}`;
    thisMonthYear.textContent = `${months[firstDay.getMonth()]} ${firstDay.getFullYear()}`;

    for (let i = 0; i <= 5; i++) {
        td[i].textContent = '';
    }
    for (let i = 28; i <= 36; i++) {
        td[i].textContent = '';
    }

    for (let i = 0; i < lastDay.getDate(); i++) {
        td[firstDay.getDay() + i].textContent = i + 1;
        existTd.push(firstDay.getDay() + i);
    }


    for (let i = 0; i <= 5; i++) {
        td[i].removeEventListener('click', changeInfo);
    }

    for (let i = 28; i <= 36; i++) {
        td[i].removeEventListener('click', changeInfo);
    }

    for (let i = existTd[0]; i < existTd[0] + existTd.length; i++) {
        td[i].addEventListener('click', changeInfo)
    }
}

function previousMonth() {
    n--
    existTd = [];
    firstDay = new Date(date.getFullYear(), date.getMonth() + n, 1);
    lastDay = new Date(date.getFullYear(), date.getMonth() + n + 1, 0);
    thisDay.textContent = days[firstDay.getDay()];
    thisDate.textContent = `${firstDay.getDate()}`;
    thisMonthYear.textContent = `${months[firstDay.getMonth()]} ${firstDay.getFullYear()}`;

    for (let i = 0; i <= 5; i++) {
        td[i].textContent = '';
    }
    for (let i = 28; i <= 36; i++) {
        td[i].textContent = '';
    }

    for (let i = 0; i < lastDay.getDate(); i++) {
        td[firstDay.getDay() + i].textContent = i + 1;
        existTd.push(firstDay.getDay() + i);
    }

    for (let i = 0; i <= 5; i++) {
        td[i].removeEventListener('click', changeInfo);
    }

    for (let i = 28; i <= 36; i++) {
        td[i].removeEventListener('click', changeInfo);
    }

    for (let i = existTd[0]; i < existTd[0] + existTd.length; i++) {
        td[i].addEventListener('click', changeInfo)
    }
}

function changeInfo(e) {
    thisDay.textContent = days[e.target.cellIndex];
    thisDate.textContent = e.srcElement.innerText;
}

//이벤트
rightButton.addEventListener('click', nextMonth);
leftButton.addEventListener('click', previousMonth);

for (let i = existTd[0]; i < existTd[0] + existTd.length; i++) {
    td[i].addEventListener('click', changeInfo);
}

🎉 결과

깃허브
결과

carousel 과제 할때 요소를 클릭할때 그 자체를 정보로 받는 코드를 구현하고 싶었다. 근데 그 당시엔 이벤트 매개변수나 프로토타입에 대한 공부가 전혀 안되어있어서 그 당시 수준으로 구현이 불가능해서 우회하는 방법을 썼었다. calendar 과제를 할땐 starter kit - step 4 - javascript Koans 를 통해 프로토타입에 대한 맛보기 공부를 한 후라 그 당시 구현못했던 코드를 구현할 수 있었다. 성장한 느낌이 너무나 뿌듯했다.
또 이 과제를 할때 이제까지 했던 과제들에 비해 제일 고민하던 시간이 길었는데 결국 해결하는 순간 너무나 짜릿했다.
근데 이 과제를 하면서 아직 스코프나 this 등에 대한 확신이 스스로 없는게 느껴졌다. 더 깊은 이해를 위한 공부를 하려고 한다.

좋은 웹페이지 즐겨찾기