Library/jQeury

jquery/Javascript DatePicker를 보고 만든 WeekPicker(1)

청렴결백한 만능 재주꾼 2021. 3. 13. 02:26
반응형

최종 완성본 링크

 

어느덧 흘러 흘러 jquery  & HTML & css & javascript 까지 오게 되었다.

 

자 달력은 

 

www.youtube.com/watch?v=FBO_7H39tqs

이거 개발자의 품격님의 유튜브와

Datepicker란 jquery의 내장 기능과 

각종 인터넷을 참고하였다

 

밑밥 : 만들기에 급급했습니다. 

 

 

실행 화면:

왼쪽)월요일시작, 몇 주인지 나오게 오른쪽)일요일 시작, week정보 없이

 

 

우선 실행하기 .

옵션은 대충 저런식으로 하고 넣지 않아도 됨. datepicker를 참고하여 만듬

 $(document).ready(function(){
            option = {
            firstDay: 0, //0 : sunday시작 , 1: monday 시작
             showWeek: true, // 몇 번째 주인지 나옴
            weekHeader: '#', 
            monthNames: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
            value: '2021-W09', // 처음 기본 값
            displayTemplate: function(weekName, firstDate, lastDate) {  //어떻게 표시할지 사용자가 지정 가능
                let format = 'yyyy/MM/dd'
                startPoint = formatDate(firstDate, format)                
                endPoint = formatDate(lastDate, format)

                return startPoint+'~'+ endPoint ;
            }
        };
       
    
        $("#date").Weekpicker(option)

 

 

 

첫번째,  위와 같이 호출이 되면 실행이 될 제이쿼리 함수.

let exWPs = []; exWPID = 0; exWPset = {};
$.fn.Weekpicker = function (option) {
    let current_year = (new Date()).getFullYear();  // 현재 년도
    let current_month = (new Date()).getMonth();  // 현재 달
    let id = new Date().getMilliseconds() + new Date().getMinutes(); //유니크하진 않지만 랜더 되었을 때 identity를 관리 가능
    let fixedOption = _customSetting(option) //Option이 들어온다면 갈무리함.기본 값 추가
    
    $(this).parent().append(_htmlGenerate(id, fixedOption)).trigger("create"); // 이 함수가 실행 된 부모에 달력을 넣음/형제가 되는 것임

    $("#yearChange" + id).val(current_year);  // 현재 년월을 기본 값으로 세팅
    $("#monthChange" + id).val(current_month);
    
    changeYearMonth(current_year, current_month, id, fixedOption.showWeek, fixedOption.firstDay);
    // 본격적으로 달력을 랜더하기 위한 함수 실행
    
    $(this).addClass('ex-weekpicker ex-weekpicker-input '+id).attr("onclick","showWeekPicker("+id+")")
	// 이 기능이 추가된 요소를 클릭하면 달력이 펴지게 지정
    
    $(window).on('click', function (e) {
        if ($(e.target).hasClass('ex-weekpicker') ||
            $(e.target).parent().hasClass('ex-weekpicker') ||
            $(e.target).parent().parent().hasClass('ex-weekpicker') ||
            $(e.target).parent().parent().parent().hasClass('ex-weekpicker') ||
            $(e.target).parent().parent().parent().parent().hasClass('ex-weekpicker')||
            $(e.target).parent().parent().parent().parent().parent().hasClass('ex-weekpicker'))return;
        exCloseWeekPicker();
    });
    // 다른 곳을 클릭할 때 달력이 접힘.
    
    exWPset[id]=""
    console.log(exWPset)
    //위 2줄은 현재 진행중인 set기능 추가를 위한 것 무시
    
    
    // 휠 스크롤로 위 아래 다른 달로 이동
    $("#ex-weekpicker-" + id).on("mousewheel", function(e){
        let E = e.originalEvent;
        let deltaY = E.wheelDelta
        let direction = Math.round(deltaY/120)
        
        y = Number($("#yearChange" + id).val());
        m = Number($("#monthChange" + id).val());
        if (direction > 0){
            m = m + 1
            if (m == 12){
                y = y + 1
                m = 0
            }
        } else if(direction < 0){
            m = m - 1
            if (m === -1){
                y = y - 1
                m = 11
            }
        }
        
        $("#yearChange" + id).val(y);
        $('#monthChange' + id).val(m);
        changeYearMonth(y, m, id, fixedOption.showWeek, fixedOption.firstDay)
    })
}

 

참 멋스럽지 않은 코드이다.  jquery로써 호출만 된다면 여기서 부터 시작이 된다. 먼저 실행이 되면 위에서부터 읽으니까 순서대로 코드를 나열하겠다.

 

 

두번째,  맨 처음 넣은 option이 정리 되는 함수. 이 함수를 통과하면 fixedOption으로 관리됨.(변수명 센스 부족)

function _customSetting(option) {
    let monthNames = {0: "JAN", 1: "FEB", 2: "MAR", 3: "APR", 4: "MAY", 5: "JUN",
    6: "JUL", 7: "AUG", 8: "SEP", 9: "OCT", 10: "NOV", 11: "DEC" };//default
    let dayNames = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] //default
    let sunday = ""
    if (!option.monthNames) {
        option.monthNames = monthNames
    }
    if (option.dayNames){dayNames = option.dayNames}else{option.dayNames = dayNames}
    if (option.firstDay == 1){
        sunday = dayNames.shift();
        dayNames.push(sunday)
        option.dayNames = dayNames
    }
    
    return option
}

Option에 값이 들어오지 않을 경우 기본값을 세팅해주고 들어올 경우 그걸로 해주고 하는 것.

 

 

 

세번째, 메인입니다. html를 쫘악 깔고 순서를 정리하는 곳

function _htmlGenerate(id, option) {
    let prevText = "Prev";
    let nextText = "Next";

    let dpDiv = $("<div/>", {
        id: "ex-weekpicker-"+id,
        style: 'display:none;border:5px ridge;z-index:9999;float:none;position:absolute;',
    }).addClass('ex-weekpicker ex-weekpicker-ui-weekpicker');

    let prev, monthOption, next, yearInput, todayButton, selectFormat

    prev = $('<button/>', {
        type: 'button',
        value: prevText,
        id: 'prevButton' + id,
        class: 'ex-weekpicker-controlButton-left',
    }).attr('onclick', 'changeMonth(-1,' + id + ', ' + option.showWeek + ', ' + option.firstDay + ')').append($('<i />', {
        class: 'ex-weekpicker fas fa-chevron-left',
    }));

    monthOption = $('<select />', {
        id: "monthChange" + id,
        class: "ex-weekpicker-form-controller ex-weekpicker-controlButton",
    }).attr('onchange', 'changeMonth(0,' + id + ', ' + option.showWeek + ', ' + option.firstDay + ')');

    next = $('<button/>', {
        type: "button",
        value: nextText,
        id: 'nextButton' + id,
        class: "ex-weekpicker-controlButton-right"
    }).attr('onclick', 'changeMonth(1,' + id + ',' + option.showWeek + ', ' + option.firstDay + ')').append($('<i/>', {
        class: 'ex-weekpicker fas fa-chevron-right',
    }));

    yearInput = $('<input />', {
        type: "text",
        id: "yearChange" + id,
        class: "ex-weekpicker-yearchange"
    }).attr('onchange', 'changeYear(' + id + ', ' + option.showWeek + ', ' + option.firstDay + ')');

    // todayButton = $('<button>This month</button>', {
    //     type: 'button',
    //     value: 'goToday',
    //     id: 'todayFindButton',
    //     class: 'ex-weekpicker-form-control',
    //     style: 'float:right;',
    // }).attr('onclick', 'goToToday(' + id + ', ' + option.showWeek + ', ' + option.firstDay + ')').append($('<i/>', {
    //     class: 'ex-weekpicker fa fa-calendar',
    //     value: 'Today',
    // }));

    selectFormat = $('<select />', {
        id: "formatOption"+id,
    })
    .append("<option value='YYYY-WW'>YYYY-WW</option>")
    .append("<option value='MM/DD/YYYY~MM/DD/YYYY'>MM/DD/YYYY~MM/DD/YYYY</option>");
    
    for (let i = 0; i < 12; i++) {
        monthOption.append("<option value=" + i + ">" + option.monthNames[i] + "</option>")
    };

    format = $('<p>Format:</p>',{
        style:'font-size:20px;'
    });

    controlPanel = $('<div>',{
        id:"ex-weekpicker-controlpanel"+id,
        class:"ex-weekpicker-controlpanel",
    });

    format.append(selectFormat);
    controlPanel.append(prev);
    controlPanel.append(yearInput);
    controlPanel.append(monthOption);
    controlPanel.append(next);
    dpDiv.append(controlPanel)
    //dpDiv.append(todayButton);
    // dpDiv.append(format);
    


    weekDay = $('<table />', {
        class: "ex-weekpicker table table-borderd"
    })


    let tableRow = $('<tr />', { id: "table-head-day",class:"ex-weekpicker" });
    let tableHead = $('<thead />',{class:"ex-weekpicker"});
    if (option.showWeek) {
        if (option.weekHeader) {
            $('<td>' + option.weekHeader + '</td>').appendTo(tableRow)
            for (let a = 0; a < option.dayNames.length; a++) {
                $('<td>' + option.dayNames[a] + '</td>').appendTo(tableRow)
            }
        } else {
            $('<td>#</td>').appendTo(tableRow)
            for (let a = 0; a < option.dayNames.length; a++) {
                $('<td>' + option.dayNames[a] + '</td>').appendTo(tableRow)
            }
        }


    } else {
        for (let a = 0; a < option.dayNames.length; a++) {
            $('<td>' + option.dayNames[a] + '</td>').appendTo(tableRow)
        }
    }
    tableHead.append(tableRow);
    weekDay.append(tableHead);
    weekDay.append($('<tbody />', { id: "tb_body" + id , class:"ex-weekpicker"}));

    dpDiv.append(weekDay);
    let warper = $('<div />', { id: "warper"+id, class:"ex-weekpicker" }).append(dpDiv);

    if(!exWPs.includes(id)){exWPs.push(id)}

    return warper;
}

 

 

이제부터 함수 여러개 // 윤년 체크하는 것들 부터 주의 첫번째 날 구하는 것// 다 유튜브 개발자의 품격 출처

여기서 내가 add  한 것은 거기 유튜브에서는 이번달을 뽑는 것이였는데 나는 빈칸에 전달의 날짜가 나오게 하는 것을 했다. 

 

그러니까 1월 1일이 금요일인데 그 앞에 '월화수목'이 비어 있는 것이 아니라 12월 27일 28일 29일 30일 31일이 채워져있는 것을 말한다.

function changeYearMonth(year, month, id, showWeek, firstDay) {
    let month_day = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

    if (month == 1) {
        if (checkLeapYear(year)) month_day[1] == 29;
    }
    let first_day_of_week = getFirstDayOfWeek(year, month, firstDay);

    let arr_calendar = [];
    //전 달의 끝 날짜를 집어 넣는 작업
    for (let i = (month == 0 ? month_day[month_day.length - 1] : month_day[month - 1]) - first_day_of_week + 1 ; i <= (month == 0 ? month_day[month_day.length - 1] : month_day[month - 1]); i++) {
        arr_calendar.push(String(i));
    };

    for (let i = 1; i <= month_day[month]; i++) {
        arr_calendar.push(String(i));
    };

    let remain_day = 7 - (arr_calendar.length % 7);

    //이 달 끝에 다음 달의 앞 날짜를 당겨오는 작업
    if (remain_day < 7) {
        for (let i = 0; i < remain_day; i++) {
            arr_calendar.push(String(i + 1))
        }
    };

    renderCalendar(arr_calendar, id, showWeek);
}


function renderCalendar(data, id, showWeek) {
    let h = [];
    for (let i = 0; i < data.length; i++) {
        let nthWeek = Math.floor(i / 7)+1;
        if (i == 0) {
            if (showWeek) {
                h.push('<tr name="'+nthWeek+'thweek'+id+'" onclick="getWeekNumber(' + nthWeek + ',' + id + ');" class="ex-weekpicker-weekSelect">');
                h.push('<td name="week" style="cursor:pointer;">' + nthWeekOfYear(nthWeek, id) + '</td>');
            } else {
                h.push('<tr name="'+nthWeek+'thweek'+id+'" onclick="getWeekNumber(' + nthWeek + ',' + id + ');" class="ex-weekpicker-weekSelect">');
            }
        } else if (i % 7 == 0) {
            if (showWeek) {
                h.push('</tr>');
                h.push('<tr name="'+nthWeek+'thweek'+id+'" onclick="getWeekNumber(' + nthWeek + ',' + id + ');" class="ex-weekpicker-weekSelect">');
                h.push('<td name="week" style="cursor:pointer;">' + nthWeekOfYear(nthWeek, id) + '</td>');
            } else {
                h.push('</tr>');
                h.push('<tr name="'+nthWeek+'thweek'+id+'" onclick="getWeekNumber(' + nthWeek + ',' + id + ');" class="ex-weekpicker-weekSelect">');
            }
        }
        h.push('<td name="day" style="cursor:pointer;" id="'+ data[i] +'">' + data[i] + '</td>');
    };
    h.push('</tr>');

    $('#tb_body' + id).html(h.join("")).trigger("create");
}

function checkLeapYear(year) {
    if (year % 400 == 0) {
        return true;
    } else if (year % 100 == 0) {
        return false;
    } else if (year % 4 == 0) {
        return true;
    } else {
        return false;
    }
}

function getFirstDayOfWeek(year, month, firstDay) {
    month += 1
    if (month < 10) month = "0" + month;
    if (firstDay == 1) {
        if (Number((new Date(month + "-01-" + year)).getDay()) == 0) {
            return 6
        } else {
            return (new Date(month + "-01-" + year)).getDay() - 1
        }
    }

    return (new Date(month + "-01-" + year)).getDay();
}

 

 

여기까지 htmlgenerate된 html에 changeYearMonth로 하나의 리스트를 만들어서 rendercalendar에 넣어 html에 차례대로 넣는 작업이다. 

 

 

 

 

 

여기까지가 실행이다. 이제 버튼을 누를때 등 이벤트에 관련된 함수들을 나열하겠다.

 

 

 

 

 

onclick에 배정이 되어 있는 함수이다. 월을 바꾸던 년을 바꾸던 loadCalendar로 가서 또다시 changeYearMonth로 간다.

function changeMonth(diff, id, showWeek, firstDay) {
    if (diff == 0) {
        this.year = Number($("#yearChange" + id).val())
        this.month = Number($("#monthChange" + id).val())
    } else {
        this.year = Number($("#yearChange" + id).val())
        this.month = Number($("#monthChange" + id).val())
        this.month = this.month + Number(diff);

        if (this.month == -1) {
            this.year = this.year - 1;
            this.month = 11;
        } else if (this.month == 12) {
            this.year = this.year + 1;
            this.month = 0;
        }
    }

    loadCalendar(id, showWeek, firstDay);
}

function changeYear(id, showWeek, firstDay) {
    this.year = Number($("#yearChange" + id).val())
    this.month = Number($("#monthChange" + id).val())

    loadCalendar(id, showWeek, firstDay);
}

function loadCalendar(id, showWeek, firstDay) {
    $("#yearChange" + id).val(this.year);
    $('#monthChange' + id).val(this.month);

    changeYearMonth(this.year, this.month, id, showWeek, firstDay)
}

 

 

 

그리고 몇주차인지 계산하는 함수를 넣겠다. 사실 몇주차인지 계산하는게 없어서 내가 만들었다.

 

원래 getWeekNumber에서 최종 input에 표시된다. 그래서 여기서 form에 대한 설정을 받아 처리했는데 그냥 콜백함수로 사용자가 지정할 수 있도록 하였다. 

function getWeekNumber(nthWeek, id) {
    let yearValue = $('#yearChange'+ id).val()
    //let selectedFormat = $("#formatOption"+ id).val()
    let findInputElement = $("."+id)
    weekDuration = formattingDate(nthWeek, id, yearValue)
    startdate = new Date(weekDuration[0])
    enddate = new Date(weekDuration[1])

    if (option.displayTemplate) {
        let displayText = option.displayTemplate(
            nthWeekOfYear(nthWeek, id),
            startdate,
            enddate);
        $(findInputElement).val(displayText);
        $("#ex-weekpicker-" + id).toggle()
    }
    else{
        $(findInputElement).val(yearValue+"-W"+nthWeekOfYear(nthWeek, id) )
        $("#ex-weekpicker-" + id).toggle()
    }
}


function nthWeekOfYear(nthWeek, id) {
    year = $("#yearChange" + id).val()
    month = Number($("#monthChange" + id).val()) + 1;

    let first_day = new Date("01-" + (7 - (new Date("01-01-" + year).getDay()) + 1) + "-" + year)

    let day_gap = (new Date(month + "-01-" + year) - first_day) / 1000 / 60 / 60 / 24;
    var nthWeekOfThisYear = Math.ceil(((day_gap + 1) / 7) + nthWeek)
    
    if(month==12){
        if(nthWeekOfThisYear>=53){
            if($(this).children("td #1")){
                return 1
            }
        }
    }
    return nthWeekOfThisYear
}

 

여기까지 기능적인 부분이였고 눌렀을 때 없어지고 생기고 하는 것들 함수 보이겠다.

function exCloseWeekPicker(){
    for(x = 0 ; x<exWPs.length ; x++){
        $("#ex-weekpicker-"+exWPs[x]).hide();
    }
} 
//아이디 관리를 하는 exWPs에서 있나없나 검사하고 있으면 싹다 닫게

function showWeekPicker(id){
    $("#ex-weekpicker-" + id).toggle()
}

$(document).keyup(function (e) {
    if (e.key === "Escape") { // escape key maps to keycode `27`
        exCloseWeekPicker();
    }
});

$(window).on('click', function (e) {
        if ($(e.target).hasClass('ex-weekpicker') ||
            $(e.target).parent().hasClass('ex-weekpicker') ||
            $(e.target).parent().parent().hasClass('ex-weekpicker') ||
            $(e.target).parent().parent().parent().hasClass('ex-weekpicker') ||
            $(e.target).parent().parent().parent().parent().hasClass('ex-weekpicker')||
            $(e.target).parent().parent().parent().parent().parent().hasClass('ex-weekpicker'))return;
        exCloseWeekPicker();
    });

 

 

그럼 여기까지 !

반응형