javascript - 달력

김동하·2020년 10월 14일
0

버전 1

준비

constructor로 options를 주고 즉시실행함수로 new CALENDAR()의 인자 값으로 객체 id: "calendar"를 주었다. 여기서 id: calendar는 html에서 캘린더 전체 div이다. 그리고 classname을 인자로 주는 함수를 만들어서 return 으로 태그들을 가져왔는데 getElementById(this.options.id) 즉, 전체 div 중 getElementsByClassName(className)[0]이렇게 가져왔다. 처음 보는 것이라 신기했다.

월과 주 구하기

drawMonth랑 drawWeekday는 간단하다 하드코딩으로 배열 안에 요소를 다 넣어서 변수를 만들어 배열을 forEach해서 html에 넣어주면 된다. 나중에 month는 active해줘야 하는 것까지 고려해야 함. data-month에 idx를 준다!

계속 써먹을 캘린더 함수

이제 getCalendar랑 drawDays 차례. time으로 new Date(this.date)를 했는데 여기서 this.Date()는 new Date()니까 결국 new Date(new Date())를 한 셈. 콘솔로 보니까 둘 다 똑같같데 왜 이렇게 하는 거자? 수정: day = new Date()와 day = +new Date()는 다른 값이다. this.date에는 +new Date()값을 주었다.

getCalendar는 active 객체와 전년전월 지금 월년을 리턴한다. active안에 있는 day,wekk,month,year은 내장 메소드로 구하고 days와 startWeek, formatted는 각각 time을 인자로 하는 함수가 있어야 한다. tm이란 키에 time을 value 줬는데 왜 그런지 모르겠다.

count days in month와 get started day of week by time함수에서 date는 get month and year함수에 인자를 받아온 time을 그래도 넣어서 새로운 date를 가져온다. get month and year 함수를 보면 time을 인자로 받아 return으로 현재 년월을 한다. 이제 count days in month에 return 값으로 new Date()를 하고 파라메터로는 year, month+1, 0을 하는데 요것은 저번 달에 대한 정보다. 여기에 getDate()를 하는데 getDate()는 설장한 날짜고 getDay()는 로컬날짜다. 즉, 전달의 마지막 날짜를 가져오는 것!

get started day of week by times 는 똑같이 return값으로 new Date() 파라메터는 year,month,+1 요것이 뭐냐면 현재 달의 첫 번 째 날에 대한 정보, 1일을 기준으로 보여준다. 여기에 getDay()를 하면 월요일 0 기준으로 현재 달이 언제부터 시작하는지가 나온다.

날짜 구하기

이제 drwaDays()를 하면 일단 getCalender로 calendar가져오고 변수로 latest days in prev month가 필요하다. range 함수를 만들고 거기에 startWeek를 준다. range는 number를 인자로 받는데 일~토까지 0~6으로 된 숫자 중 해당 월이 시작하는 날의 숫자가 전달될 것이다. return 값으로 new Array(number)해서 fill 하는데 map으로 index를 채워 넣으면 된다. 여기까지 하고 drawDays에서 latest days in prev month를 콜솔찍으면 예를들어 10월의 첫 날이 목요일이었다면 [0, 1, 2 ,3] 즉, 일,월,화,수가 지난 달의 마지막 주의 숫자이고 현재 달력에서는 표시되지 않을 것이다. 이제 range에 map을 한다. 그리고 return 으로 dayNumber, pMonth, pYear, current를 주는데 month와 year은 calender로 구하고 currentMonth는 flag다. 이제 dayNumbers인데 아까 구했던 count days in month에 전월(pMonth)를 넣는다. 그럼 전월의 마지막 날이 나오고 거기에 idx를 빼주면 전월 마지막 주의 날짜들이 나온다. 여기서 reverse로 마무리.

이제 drawDays() 안에는 캘린더 변수와 전월에 대한 정보를 담은 latest days in prev month가 있다. 이제 days in active month를 만들고 똑같이 range.map을 하는데 range 인자로 캘린더의 days 를 준다. 배열에 담긴 0부터 30까지 숫자가 있다. map을하고 day, idx를 주자. 일단 변수로 dayNumber와 today가 필요하다. dayNumber엔 idx+1! 왜냐면 0부터 시작하니까. today 로 new Date()생성. return을 해주는데 month,year,curretnMonth는 현재 달에 대한 정보를 준다. 그리고 dayNumber는 그대로 그리고 selected는 캘린더에서 가져온 현재 day랑 daysNumber가 같은 것으로 한다. today가 살짝 어려운데 일단 days in active month 변수에서 map함수 내에서 변수 today는 new Date()가 할당되었다. return 으로 today는 조건들이 있다 일단 today.getDate()와 dayNumber가 같아야 하고 today.getFullYear()와 캘린더의 year이 같아야 한다. 그리고 똑같이 month도 같아야 한다.

이제 count of days 함수를 만들차례. 일단 아까 constructor에 maxDays = 37로 만들었는데 그걸 가져온다. 왜 maxDays가 37이지? 최대 6주니까 최대 일은 42일 아닌가.. 아무튼 maxDays에서 전월 마지막 주의 날짜들의 수와 현재 월의 날짜들의 합을 더해 빼준다. 예를들어 2020년 10월 기준으로 9월 마지막 주는 4일이었고 10월은 31일이다 그러면 35일이니까 count of days의 값은 2가 된다. 이제 days in next month 변수에 range.map을 하는데 이번에는 range에 count of days를 넣는다. return 값은 다음 달에 대한 것들!

대망의 days

150줄 만에 let days를 보는데 가슴이 웅장해진다. 드디어 days다... days는 spread 연산자를 이용해 여태까지 구했던 전월 마지막 주 날짜들, 현재 월 날짜들, 다음 달 첫 번째 주의 날짜들(맞나?)를 다 합친다.

배열에는 총 37의 객체, 즉 현재 달에 표시될 정보들이 담긴다. 이제 days 템플릿 만들고 forEach해서 렌더한다. 이제 살짝 헷갈리는 부분인데 li 클래스에 객체 정보에 있는 currentMonth, today, selected, hasEvent(아직 지정안함), dayNumber, month,year 정보 혹 flag를 해줘야 한다. 여기서 중요한 것은 pre,next month에 없는 정보를 cur는 가지고 있다는 것! 그리고 data부분이랑 class부분이 나눠져있다. data-day는 css에 필요하다. (정확히 어떻게 쓰는지 모름)

아름답다

지금 년도와 오늘 그리기

까먹고 drawMonth에서 month render 할 때 idx랑 active.motnh같은지 확인하는 거 안 했다. 그거 하면 현재 month가 빛난다. 이제 draw year and current day함수가 필요하다. 간단하게 캘린더 가져오고 year span과 currentday 그리고 current week를 캘린더에서 가져오면 된다.

전년/다음년 바뀌기

이제 버튼을 누르면 월이 바뀌고 년도가 바뀌는 트리거를 사용해야 한다. prev 버튼에 이벤트 리스너 걸고 캘린더 가져오고 updateTime함수를 만든고 함수에 캘린더의 전년도 year를 인자로 준다. updateTime은 new Date(time)으로 하는데 재밌는 것은 this.date에 할당하여 this.date를 밀리세컨으로 바꾼다. 그리고 drawAll()을 하면 바뀐 this.date 기준으로 달력이 바뀌는 것! 매우 똑똑해... nextBtn도 같은 방식으로 처리한다.

월 바뀌기

month에 이벤트 리스너 그리고 캘린거 가져오고 getAttribute로 타겟의 data-month를 가져온다. 이제 클릭했을 때 newMonth가 필요하다. newMonth는 new Date()에 캘린더의 tm 즉, 밀리세컨을 주고 setMonth()에 클릭한 month를 준다. 그러면 클릭한 month 기준의 새로운 밀리세컨드를 얻게 된다(?) 그리고 updateTime에 newMonth 넣어주고 drawAll(). 모르겠는 것 : updateTime에 newMonth 넣고 콘솔 찍은면 언디파인드 나오는데 왜 그러징

일 바뀌기

일이 바뀌는 건 더 구체적이다. 타겟의 data-month/year/day를 다 가져오고 이제 updateTime해줘야 하는데 여기가 조금 헷갈린다. (사실 많이) day의 정보를 어떻게 바꿔서 updateTime에 넣어야 할까. data-set의 날짜 정보는 str이고 updateTime에 인자로 밀리세컨드를 줘야 한다. 5/1/2020 이런 식으로 만들어서 넣어주면 됨! 하지만 내 생각은 아예 밀리세컨으로 바꿔서 넣어보자.(포기) 그러고 drawAll()을 하면 오늘 날짜가 액티브되고 클릭 날짜가 왼쪽에 생긴다. 이부분 너무 어려워..

투두 추가하기

addBtn에 이벤트 리스너를 건다. 이벤트 필드 벨류를 변수로 정하고 date fomatted가 필요한데 getFormatted 함수는 new Date(this.date)을 str 즉, 일/월/달로 만드는 것! date를 인자로 받아서 리턴으로 인자의 getDate, getMonth,getFullyear를 스트링 만든다. 갑자기 헷갈리는데 new Date(this.date)를 찍어보면 'Wed Oct 14 2020 15:42:01 GMT+0900 (대한민국 표준시)' 이렇게 나온다 이걸 어떻게 불러야하지 this.date는 밀리세컨이고 그걸 new Date()에 넣어 저 정보를 뽑은 것이다. 저 정보를 get formatted 함수에 넣으면 일/월/달 이렇게 바꿔주는 것! 이제 오랫동안 잊고 있었던 eventList가 생각나야 한다. eventList는 html에 ul인데 여기에 getItem을 한다 원래 배열인데 객체? 왜 그러지 아무튼
eventList[dateFomatted] 라는 아름다운 공식이 만들어진다. 만약 저기에 해당하는 정보다 없으면 eventList[dateFomatted] = [] 이렇게 빈 배열을 만든다. 가슴이 웅장해진다.. push,set,darwAll 3단 콤보하면 투두 거의 마무리!

투두 렌더

eventList를 새롭게 가져와야 하는데 active에 있던 formatted를 이용한다. 즉 this.eventList[calendar.active.formatted].. 아름답다.. forEach로 템플릿에 추가하면 된다.

이벤크 있는 날 표시

투두에 무언가 추가된 날에 날짜 위에 표시를 해줘야 한다. 다시 drawDays()로 가서 days에 map을 한다. 정확한 이유는 모르겠으나 밑에 days를 forEach하는 게 있어서 days.map은 새로운 변수를 return해야한다. formatted란 변수를 만들고 getForamtted 함수를 쓴다. 함수의 인자 new Date()에서 인자를 스트링으로 만들어서 준다. 월/일/년도로 쭉 뽑아낸다. 그리고 day의 다른 이름 변수이자 mpa의 리턴값에 hasEvent를 만들고 this.eventList[formatted]를 할당한다. 아름다워..return newDayReddot..

버전 2

준비

필요한 데이터를 객체 안에 넣고 사용한다. 일단 필요한 것이 today와 activeDate, month 바꿀 때 사용할 getMonth가 있어야 한다. 그리고 fist day랑 last day를 구해야 하는데

getFirstDay: (yy, mm) => new Date(yy, mm, 1)

이런 식으로 화살표 함수를 만들어서 init 객체에서 사용할 수 있게 한다. 이제 지난 달이랑 다음 달을 구하는 함수를 만든다. new Date()의 새로운 객체를 만들고 거기에 setDate, setMonth를 이용한다. 그리고 activeDate를 다시 d에 할당. d를 리턴한다. this.monForChange를 인자로 주어서 ++해주면 함수 실행 시 다음 달로 간다

  nextMonth: function() {
        let d = new Date()
        d.setDate(1)
        d.setMonth(++this.monForChange)
        this.activeDate = d;
        return d
    },

addZero와 activeDTag만 만들면 끝.

load date, load year and month

load date 함수는 date와 day를 받는다. 디스플레이 해줘야 하니까 html에서 querySelect하고 date에는 today를 getDate하고 day 는 숫자로 나오니까 요일 리스트에서 가져온다. 이제 load year and month에 today을 준다. 5개의 변수가 필요한데 year, month, firt day, last day, mark day다. month, year도 쿼리에서 가져와서 화면에 표시한다. 만약 today와 인자로 받은 fulldate 의 getmonth, getyear가 같으면 mark day는 today.getDate()다.

이제 테이블을 만들어야 하는데 약간 헷갈리다. 일단 trtd 변수를 만들고 이중 for문을 돌린다. 변수로는 시작 날인 startCount(null)와 countDay(0)이 필요하다. 첫 번째 for문은 가로줄(요일 , i)이다. for 문 안에 각 요일에 대한 세로줄(j) 이렇게 작성한다. j 문에서 일단 if로 첫 날 검사를 한다. i=0, startCount가 없고, j가 firstDay.getDay()와 같다면 startCount = 1이다. 즉 이번 달의 날짜들, 그리고 start Count가 아닐 때는 trtd에 그냥 를 추가한다. 즉, 현재 달력에서 지난 달의 날들이다. 그리고 if문 밖에 를 닫는다. 이제 startCount가 맞을 때를 구한다. 먼저 fullDate를 다시 구해야 한다.

이렇게 나오도록 변형한다. 그리고 td에 class에 day추가하고 date-data와 data-fdate를 추가하면 된다. 삼항연산자로 markToday랑 countday비교해서 today를 추가한다. startCount가 맞으면 삼항연산자로 conutday를 ++한다. 이제 마지막이 넘어가는 날들을 지워야 하는데 counday가 lastDay.getDate()인지 확인하고 맞다면 startCount를 0으로 한다. startCount가 1이면 이번 달 날짜들, 0이면 전달 혹 다음달

다음 달, 전 달

load year and month 함수에 아까 만들었던 nextMonth와 preMonth 함수를 인자로 주면 알아서 바뀐다.

profile
프론트엔드 개발

0개의 댓글