Programming/JavaScript

[Javascript] 날짜와 관련된 <input> 요소의 type(date, month, week) 활용하기 (유효성, 포맷전환, 데이터 핸들링)

 

See the Pen onChangeInputType by BEGINNERWW (@beginnerww) on CodePen.

 

 

 

 

목적

      - 기간설정으로 파라미터값을 전달하여 범위에 해당하는 데이터값을 가져온다.

 

과정

     1. <select> 태그에 일간, 주간, 월간, 연간을 선택할 수 있게 option을 주고 값이 변할때마다 이벤트를 실행한다.

     2. select 태그의 값이 변할때마다 onChange이벤트로 기간 범위 템플릿을 변경해준다.

        (function onChangeCal(event))

     3. 범위값이 변경될때 마다 유효성 검사를 실시한다.

        (시작일 : startValidity(event) / 종료일 : endValidity(event))

 

<select name="condition" id="calendar" onchange="onChangeCal(event)">
	<option value="year">연도별</option>
	<option value="month" selected>월별</option>
	<option value="week">주별</option>
	<option value="day">일별</option>
</select>
<span id ="cal">
	<input type="month" class="input_type" id="startDate" style="width: 200px;" onChange="startValidity(event)"> 
	<span class="hypen"> ~ </span> 
    <input id="endDate" type="month" class="input_type" style="width: 200px;"  onChange="endValidity(event)">
</span>

 

<select> 태그의 기본 값을 설정하려면 <option> 태그의 속성으로 selected를 주면 default 값으로 설정된다.

Jquery로 selectbox 제어하기

 

테스트하기에 월별로 설정하는게 (데이터가 제일 적게나와서) 좋아서 default 값을 월별로 지정해주었다.

 

값 변경에 따른 이벤트 onchange="onChangeCal(event)"

 

selectBox의 값이 변경되면 input 태그 type도 변경되도록 함수를 작성하였다.

input 태그에는 날짜관련 type이 date, week, month 세개나 있었고,

기본 ui도 깔끔하게 되어있어서 css 때문이라면 굳이 jquery datepicker나 bootstrap datepicker를 사용하지 않아도된다.

(유효성이라던지 기타 날짜 설정 등등의 자바스크립트를 적용은 따로 해야되지만)

 

시작일 유효성 function startValidity(e)

 

옵션 선택에 따른 분기 처리를 하였다. (day와 month는 과정이 동일하나 약간의 다름으로 인해 분기처리.)

1. input type = date, week, month 는 선택한 값을 문자열로 반환해주기 때문에 오늘 이후의 날짜를 선택할 수 없도록

   오늘 날짜와의 비교를 위해 선택값을 new Date(e.target.value)를 통해 date 객체로 만들어주었다.

2. var startValidity = Math.floor(now.valueOf()/(24*60*60*1000)-startD.valueOf()/(24*60*60*1000));

   - 현재 날짜와 선택한 시작일 값의 차를 가지고 오늘 이후로 선택되지 않도록 하였다 (차가 0이면 오늘)

// 오늘과 날짜 비교
startD.getFullYear() == now.getFullYear() && startD.getMonth() == now.getMonth()
startD.getTime() <= now.getTime()

3. 시작일을 오늘로 선택시 종료일 또한 오늘 이후의 날짜를 선택하지 못하도록 자동으로 value값을 넣어주었다.

   (.toISOString()으로 반환값에 맞는 문자열 형식으로 변경 후 slice 하여 자릿수를 맞추어 적용)

//Date 객체를 받아 week number를 반환 
function getWeekFromISODate(dt) { 
	dt.setHours(0,0,0,0); 
	dt.setDate(dt.getDate() + 3 - (dt.getDay() + 6) % 7); 
	var week1 = new Date(dt.getFullYear(), 0, 4); 

	var weekNumber = 1 + Math.round(((dt.getTime() - week1.getTime())/ 86400000 - 3 + (week1.getDay() + 6) % 7) / 7); 
	
	return dt.getFullYear() +'-W'+ weekNumber; 
} 

// YYYY-W00 형태의 ISO 8601 week number를 받아 해당 주의 월요일 Date 객체를 반환 
function getStartDateFromISOWeek(ISOweek) { 
	var y = parseInt(ISOweek.substring(0, 4)); 
	var w = parseInt(ISOweek.substring(6, 8)); 
	
	var simpleDate = new Date(y, 0, 1 + (w - 1) * 7); 
	var dayOfWeek = simpleDate.getDay(); 
	var ISOweekStart = simpleDate; 
	
	if (dayOfWeek <= 4) {
		ISOweekStart.setDate(simpleDate.getDate() - simpleDate.getDay() + 1);
	} else { 
		ISOweekStart.setDate(simpleDate.getDate() + 8 - simpleDate.getDay()); 
	} 
	
	return ISOweekStart; 
}

4. input type = "week" 는 value값이 'yyyy-W00' 형식이라 date객체로 생성하면 invalid date가 된다.

   따로 ISO문자열 형식으로 변환, date형식으로 변환하는 함수를 작성하여 적용하였다.

 

종료일 유효성 function endValidity(e)

 

1. 종료일 선택은 시작일 선택 이후 선택 가능하고, 시작일 이전, 금일 이후로 선택 불가능하도록 하였다.

2. limit - selectBox option 값이 day인 경우 30일, week인 경우 3개월, month인 경우 12개월로 설정했다.

 

** 전체코드는 코드펜을 참고해주세요

 

if(selectOpt == "month"){
  startDate = startDate + '-01';

  var arr = [1,3,5,7,8,10,12];
  var endM = endDate.split('-');
  if(endM[1] == 2){
    var dayCnt = new Date(endM[0],2,0).getDate();
    if(dyaCnt == 29){
   		endDate = endDate + '-29';
    }else{
    	endDate = endDate + '-28';
    }
  }

  var cnt;
  endDate = arr.reduce((acc, cur) => {
    var acc = [];
    if(cur != endM[1]){
    	cnt = acc.push(cur);
  	}
  	return endDate + '-31';
  })

  if(cnt.length == 7 && endM[1] !=2){
  	endDate = endDate + '-30';
  }
}

** 날짜를 DB로 전달시에 (date_format을 설정안하고 yyyy-mm-dd 형식으로 전달할때)
   월에 따른 마지막 날짜를 추가하여 yyyy-mm-dd 형식으로 반환하는 코드이다.

** 쿼리 조회시 date_format을 사용하여 yyyy-mm, yyyy-W00 등으로 파라미터를 전달할수 있어 지워진 코드....

 

 


시작일/종료일 유효성 검사 switch문을 사용하여 가독성 높이기👇👇👇 

더보기

/*
* @description 시작일 유효성
* @param 
* @return 
*/
function startValidity(e){
var selectOpt = $("#calendar option:selected").val();
var startD = new Date(e.target.value);
if(selectOpt == "week"){
startD = getStartDateFromISOWeek(e.target.value);
}
    var startValidity = Math.floor(now.valueOf()/(24*60*60*1000)-startD.valueOf()/(24*60*60*1000));

if(startValidity < 0 ){
$('#startDate').val('');
return   $mslk('msg').err('오늘 날짜 이후로는 조회가 불가능합니다.');
}

if(startD.getTime() <= now.getTime()){
switch(selectOpt){
case 'day' :
if(startD.getFullYear() == now.getFullYear() && startD.getMonth() == now.getMonth() && startD.getDay() == now.getDay()){
startD = startD.toISOString().slice(0, 10);
$('#endDate').val(startD);
return   $mslk('msg').msg('종료일은 최대 금일까지 선택 가능합니다.');
}
break;
case 'week' :
document.getElementById("startDate").stepUp(1);
var next = document.getElementById("startDate").value;
var nextISO = getStartDateFromISOWeek(next);

if(now.getTime() < nextISO.getTime()){
document.getElementById("startDate").stepDown(1);
startD = getWeekFromISODate(startD);
$('#endDate').val(startD);
return   $mslk('msg').msg('종료일은 최대 금주까지 선택 가능합니다.');
}
break;
case 'month' : 
if(startD.getFullYear() == now.getFullYear() && startD.getMonth() == now.getMonth()){
startD = startD.toISOString().slice(0, 7);
$('#endDate').val(startD);
return   $mslk('msg').msg('종료일은 최대 금월까지 선택 가능합니다.');
}
break;
default :
return;
}
}

$('#endDate').val('');
}

/*
* @description 종료일 유효성
* @param 
* @return 
*/
function endValidity(e){
var selectOpt = $("#calendar option:selected").val();
var endD = new Date(e.target.value);
    var endValidity = Math.floor(now.valueOf()/(24*60*60*1000)-endD.valueOf()/(24*60*60*1000));
var $startDate = $('#startDate').val();
var startD, limitD, msg;


if(selectOpt == "week"){
endD = getStartDateFromISOWeek(e.target.value);
endValidity = Math.floor(now.valueOf()/(24*60*60*1000)-endD.valueOf()/(24*60*60*1000));
$startDate = getStartDateFromISOWeek($startDate);

if($startDate.getTime() > endD.getTime()){
$('#endDate').val('');
return   $mslk('msg').err('종료일은 시작일 이전으로 선택 불가능합니다.');
}

}else{
$startDate = $startDate.split('-');
if(selectOpt == "day"){
startD = new Date($startDate[0], $startDate[1]-1, $startDate[2]);
}
if(selectOpt == "month"){
startD = new Date($startDate[0], ($startDate[1]-1));
}
if(startD.getTime() > endD.getTime()){
$('#endDate').val('');
return   $mslk('msg').err('종료일은 시작일 이전으로 선택 불가능합니다.');
}
}

if($startDate == "" || $startDate == null){
$('#endDate').val('');
return   $mslk('msg').err('종료일은 시작일 선택 후 선택 가능합니다.');
}

if(endValidity < 0){
$('#endDate').val('');
return   $mslk('msg').err('금일 이후로 선택 불가능합니다.');
}

switch(selectOpt){
case 'day' :
limitD = new Date(startD.getFullYear(),startD.getMonth()+1,startD.getDate());
msg = '종료일은 시작일로부터 30일까지 선택가능합니다.'
$('#endDate').attr('max', limitD);
break;
case 'week' :
startD = new Date($startDate[0], $startDate[1]-1, $startDate[2]);
limitD = new Date(startD.getFullYear(),startD.getMonth()+3,startD.getDate());
limitD = getWeekFromISODate(limitD);
msg = '종료일은 시작일로부터 3개월까지 선택가능합니다.'

$('#endDate').attr('max', limitD);
break;
case 'month' :
limitD = new Date(startD.getFullYear(),startD.getMonth()+12,startD.getDate());
msg = '종료일은 시작일로부터 12개월까지 선택가능합니다.'

$('#endDate').attr('max', limitD);
break;
default :
break;
}

    endValidity = Math.floor(endD.valueOf()/(24*60*60*1000)-limitD.valueOf()/(24*60*60*1000));

    if(endValidity > 0){
$('#endDate').val('');
return   $mslk('msg').err(msg);
}
}


출처 👇👇👇