ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JavaScript] 일반적인 달력 만들기 - 날짜 이동, 날짜 구분, 오늘 표시
    만학도 프로젝트/JavaScript Calendar 2020. 8. 7. 00:16

    지난 포스트 마지막에 정리한 남은 일들을 다시 한번 훑어보면 

     

    1. 지난달 다음 달 오늘 날짜로 돌아가는 기능 만들기

    2. 지난 달 부분과 다음 달 부분을 조금 투명하게 하기

    3. 오늘 날짜 표기하기

     

    총 3가지였습니다.

     

    그럼 1번부터 하나씩 차근차근 해결해보죠.

     

    생각해보면 일단 달력을 그리는 로직은 모두 잘 만들어 뒀으니깐, 지난달, 다음 달 오늘로 Date객체의 정보만 수정해주면 됩니다.

    그래서 그동안 만든 코드에서 date부분만 남기고 나머지를 함수로 만들어 줍시다.

    // Date 객체 생성
    const date = new Date();
    
    const renderCalendar = () => {
      const viewYear = date.getFullYear();
      const viewMonth = date.getMonth();
    
      // year-month 채우기
      document.querySelector('.year-month').textContent = `${viewYear}년 ${viewMonth + 1}월`;
    
      // 지난 달 마지막 Date, 이번 달 마지막 Date
      const prevLast = new Date(viewYear, viewMonth, 0);
      const thisLast = new Date(viewYear, viewMonth + 1, 0);
    
      const PLDate = prevLast.getDate();
      const PLDay = prevLast.getDay();
    
      const TLDate = thisLast.getDate();
      const TLDay = thisLast.getDay();
    
      // Dates 기본 배열들
      const prevDates = [];
      const thisDates = [...Array(TLDate + 1).keys()].slice(1);
      const nextDates = [];
    
      // prevDates 계산
      if (PLDay !== 6) {
        for (let i = 0; i < PLDay + 1; i++) {
          prevDates.unshift(PLDate - i);
        }
      }
    
      // nextDates 계산
      for (let i = 1; i < 7 - TLDay; i++) {
        nextDates.push(i)
      }
    
      // Dates 합치기
      const dates = prevDates.concat(thisDates, nextDates);
    
      // Dates 정리
      dates.forEach((date, i) => {
        dates[i] = `<div class="date">${date}</div>`;
      })
    
      // Dates 그리기
      document.querySelector('.dates').innerHTML = dates.join('');
    }
    
    renderCalendar();

    이렇게 함수로 빼뒀으니 재사용하기가 훨씬 편해졌죠?

    그래서 이 함수를 활용해서, 지난 달, 다음 달, 그리고 오늘로 이동하는 세 가지 함수를 아래와 같이 만들어 줄 수 있습니다.

    let date = new Date();
    
    
    // ...( 생략 )...
    
    const prevMonth = () => {
      date.setMonth(date.getMonth() - 1);
      renderCalendar();
    }
    
    const nextMonth = () => {
      date.setMonth(date.getMonth() + 1);
      renderCalendar();
    }
    
    const goToday = () => {
      date = new Date();
      renderCalendar();
    }

    한 가지 주의해야 될 부분은 goToday 함수에서 date 값을 재할당해줘야 해서, 가장 첫 번째 줄에 있는 dete변수를 const가 아니라 let으로 수정해 줘야 합니다 :)

     

    그러고 나서 이 함수를 버튼에 이벤트로 넣어줘야겠죠? 

    addEventListener를 달아도 되긴 하지만 그냥 간단하게 HTML 태그에 onclick 속성으로 추가해 주겠습니다.

    <!-- ... 생략 ... --->
      <div class="nav">
        <button class="nav-btn go-prev" onclick="prevMonth()">&lt;</button>
        <button class="nav-btn go-today" onclick="goToday()">Today</button>
        <button class="nav-btn go-next" onclick="nextMonth()">&gt;</button>
      </div>
    <!-- ... 생략 ... --->

    이제 코드를 실행시키면 nav 버튼에 따라서 클릭했을 때 날짜 이동이 잘 되는 걸 확인할 수 있습니다 :)

     

    그럼 이제 두 번째 문제를 해결해 봅시다.

    이전 달과 다음 달 부분의 투명도를 조금 조절해줘서 이번 달 날짜들과의 구분을 조금 지어줘야 하는데요.

    스타일을 주기 위해서 date를 그리는 부분에 html 태그를 조금 수정해 주도록 하겠습니다.

    // ...( 생략 )...
    
      // Dates 정리
      const firstDateIndex = dates.indexOf(1);
      const lastDateIndex = dates.lastIndexOf(TLDate);
      dates.forEach((date, i) => {
        const condition = i >= firstDateIndex && i < lastDateIndex + 1
                          ? 'this'
                          : 'other';
    
        dates[i] = `<div class="date"><span class="${condition}">${date}</span></div>`;
      })
    
    // ...( 생략 )...

     

    dates 배열을 모두 만들고서 forEach로 HTML을 만드는 부분인데요.

    지난달 부분을 알아내기 위해서 첫날의 index(firstDateIndex),를 찾았고, 

    다음 달 부분을 알아내기 위해서 마지막 날의 index(lastDateIndex)를 찾아줬습니다.

     

    그러고 나서 삼항 연산자를 통해서 이번 달에 해당하는 부분은 this, 그리고 나머지는 other라는 문자열로 구분해서

    날짜 부분을 span 태그로 감싸서 class로 지정해줬는데요.

     

    이렇게 한 이유는, date에 class를 줄 경우에 투명도를 조절하게 되면, 달력의 격자를 그리고 있는 테두리 부분도 같이 투명도가 같이 조절이 되기 때문에 글자만 투명도를 주기 위함입니다 :)

     

    그래서 style.css로 가서 other 부분에 opacity를 .3정도로 돌려주면,

    /*
      ... 생략 ...
    */
    
    .other {
      opacity: .3;
    }

    이렇게 지난달과 다음 달 부분은 투명하게 보이면서 이번 달 날짜들과 구분되는 모습을 확인할 수가 있습니다.

     

    이제 진짜 마지막 오늘 날짜 표시만 하면 되는데요!

    오늘 날짜는 달력이 그려지고 난 다음에 처리를 해야 되니깐 renderCalendar 마지막 부분에 정리를 해줍시다.

    // ... 생략 ... 
    
      // 오늘 날짜 그리기
      const today = new Date();
      if (viewMonth === today.getMonth() && viewYear === today.getFullYear()) {
        for (let date of document.querySelectorAll('.this')) {
          if (+date.innerText === today.getDate()) {
            date.classList.add('today');
            break;
          }
        }
      }
      
    // ... 생략 ... 

    일단 지금 보이는 날짜가 다른 날짜일 수도 있기 때문에

     

    1.  new Date()를 통해 오늘 날짜에 맞는 date객체를 새로 만들어주고,

    2. viewMonth와 viewYear가 today의 데이터와 같은지 비교를 한 다음

    3. 만약 2번이 충족된다면 this라는 클래스를 가진 태그들을 모두 찾아내서 반복문을 돌려줍니다.

    4. 그러고 나서 해당 태그가 가지고 있는 날짜는 문자열이기 때문에 + 단항 연산자를 통해 숫자로 변경한 뒤, 오늘 날짜와 비교하고

    5. 4번이 충족된다면 해당 태그에 today라는 클래스를 추가하고 break로 반복문을 종료해 주는 코드라고 볼 수 있습니다.

     

    5번에서 break를 하는 이유는 오늘 날짜는 하나밖에 없기 때문에 찾으면 더 이상 뒤의 반복을 할 필요가 없기 때문입니다 :)

     

    자 그러면 이제 오늘 날짜가 걸린 span 태그에는 today라는 클래스가 추가되었을 텐데요.

    style.css로 돌아가서 today에 대한 스타일을 정리해주면 되겠죠?!

    today의 글자 색을 하얗게 바꿔주고 가상 클래스로 뒷 배경을 깔아주시면 됩니다.

    /*
      ... 생략 ...
    */
    
    .today {
      position: relative;
      color: #FFFFFF;
    }
    
    .today::before {
      position: absolute;
      top: 50%;
      left: 50%;
      z-index: -1;
      display: block;
      width: 30px;
      height: 30px;
      background-color: #FF0000;
      border-radius: 50%;
      transform: translate(-50%, -50%);
      content: '';
    }

    오늘 날짜까지 잘 출력이 되는 모습을 확인할 수가 있습니다.

     

    이렇게 해서 일반적인 달력 만들기는 마무리하고,

    다음 포스트는 또 영상으로 찾아뵐게요~ :)

     

    생각보다 복잡하게 만든 것 같긴 한데,

    더 좋은 방향이나 조언 있으시면 댓글 꼭 남겨주시면, 성장에 더 힘을 얻도록 하겠습니다!

     

    감사합니다 :)

    [2021-9-11 추가내용]

    위 코드에서 한 가지 버그를 발견했습니다!

    궁금하신분은 [JavaScript] 일반적인 달력 만들기 - 날짜 이동 버그 수정하기! 글을 참고해 주세요 :)

    오류가 있었던 점 대단히 죄송합니다.

    댓글

Designed by BigTop.