DONE‎ > ‎

jQuery UI Datepicker hack

  $('input').datepicker('option', {
    /*
    // 문서에는 있지만 코드에는 없음. 이걸 사용했으면 다른 방식이 가능했을지도 모르겠다.
    ,create: function(event, ui) {
    }
    */
    // datepicker 위젯을 나타내기 전에 최초 연월에 대한 값을 가져온다.
    beforeShow: function(input, instance) {
      var date=$(input).datepicker('getDate');
      var year=date.getFullYear(), month=date.getMonth()+1
      // 현재 연월의 일별 정보를 가져오는 Ajax 호출
      var args = [{...}];
      $.ajax({
        type: 'get'
        ,url: '...'
        ,data: $.param(args)
        ,beforeSend: function(request, status) {
          // 선택 가능한 날짜에 마우스를 가져갔을 때의 동작 해제 (중복 이벤트 방지. INPUT에서 INPUT으로 바로 포커스를 옮기면 onClose를 타지 않음)
          instance.dpDiv.undelegate('.ui-datepicker-calendar td a', 'mouseenter.customHover');
          // 선택 가능한 날짜에 마우스를 가져갔을 때 (선택 불가능할 경우 span 태그가 쓰여서 여기 해당하지 않음)
          instance.dpDiv.delegate('.ui-datepicker-calendar td a', 'mouseenter.customHover', function() {
            var year=instance.selectedYear, month=instance.selectedMonth+1, ymd=year+'-'+month.zerofill(2)+'-'+$(this).text().zerofill(2);
            var slice = $(input).data(year+'-'+month.zerofill(2));
            // 가져온 정보가 해당 연월일에 대한 값을 갖고 있으면 적용
            if (typeof slice != 'undefined' && typeof slice[ymd] != 'undefined' && !$.isEmptyObject(slice[ymd])) {
              // datepicker가 thead와 tbody만 사용하고 있으므로 tfoot을 쓰기로 한다.
              var foot = $('table.ui-datepicker-calendar',instance.dpDiv).children('tfoot');
              if (foot.length>0) {
                foot.empty().append('<td colspan="7"><ul></ul></td>');
              } else {
                var foot = $('<tfoot><td colspan="7"><ul></ul></td></tfoot>').appendTo($('table.ui-datepicker-calendar',instance.dpDiv));
              }
              var cell = foot.find('td>ul');
              $.each(slice[ymd], function(index, obj) {
                cell.append('<li>'+index+'</li>');
              });
            }
          });
          // 해당 연월의 자료가 있으면 이미 API 호출했던 것이므로 호출하지 않음
          var slice = $(input).data(year+'-'+month.zerofill(2));
          if (typeof slice != 'undefined') {
            return false;
          }
        }
        ,success: function(data, status, request) {
          if (!data || !data.error) return false;
          if (data.error.code == 1) { alert(data.error.message); return false; }
          // 가져온 정보 반영해서 달력 새로 그리기
          $(input).data(year+'-'+month.zerofill(2), data.timeSlice);
          $(input).datepicker('refresh');
        }
        ,complete: function(request, status) {
        }
        ,dataType: 'json'
      });
    }
    ,onClose: function(dateText, instance) {
      // 선택 가능한 날짜에 마우스를 가져갔을 때의 동작 해제
      instance.dpDiv.undelegate('.ui-datepicker-calendar td a', 'mouseenter.customHover');
    }
    // 변경된 연월의 정보를 가져온다.
    ,onChangeMonthYear: function(year, month, instance) {
      var input=this;
      var args = [{...}];
      $.ajax({
        type: 'get'
        ,url: '...'
        ,data: $.param(args)
        ,beforeSend: function(request, status) {
          // 해당 연월의 자료가 있으면 이미 API 호출했던 것이므로 호출하지 않음
          var slice = $(input).data(year+'-'+month);
          if (typeof slice != 'undefined') return false;
        }
        ,success: function(data, status, request) {
          if (!data || !data.error) return false;
          if (data.error.code == 1) { alert(data.error.message); return false; }
          // 가져온 정보 반영해서 달력 새로 그리기
          $(input).data(year+'-'+month.zerofill(2), data.timeSlice);
          $(input).datepicker('refresh');
        }
        ,complete: function(request, status) {
        }
        ,dataType: 'json'
      });
    }
    // 개별 일자를 그리기 전에 선택 가부를 반환
    ,beforeShowDay: function(date) {
      var input=this;
      var year=date.getFullYear(), month=date.getMonth()+1, ymd=year+'-'+month.zerofill(2)+'-'+date.getDate().zerofill(2);
      var slice = $(input).data(year+'-'+month.zerofill(2));
      // 가져온 값에 해당 일자가 있으면 선택 가능
      if (typeof slice!='undefined' && typeof slice[ymd]!='undefined' && !$.isEmptyObject(slice[ymd])) {
        return [true, ''];
      }
      return [false, ''];
    }
    ,onSelect: function(dateText, instance) {
    }
  });

위의 코드는 jQuery UI Datepicker를 직접 수정하지 않고 선택 가능한 일자에 대한 정보를 Ajax로 호출해 달력에 반영하는 방법이다. 핵심은 가져온 값을 갖고 있다가 beforeShowDay 콜백에서 그 정보에 따라 선택 가능 여부를 반환하는 것이다.

beforeShow에서 정의하는 각 일자(td a)에 대한 mouseenter 이벤트 정의는 부가적인 사항으로, 해당 일자에 대해 추가정보를 달력 하단에 출력한다. 화면상의 위치에 따라 달력이 위로 올라붙을 때는 하단에 추가되는 내용 때문에 문제가 있을 수 있는데 이에 대해서는 따로 고려하지 않았다.
Comments