web-dev-qa-db-fra.com

Sélecteur de plage de dates sur jquery ui datepicker

J'ai créé un sélecteur de plage de dates à l'aide de jquery ui, dans lequel vous pouvez utiliser le même calendrier en ligne pour effectuer les deux sélections de date.

Voir mon violon ici: http://jsfiddle.net/kVsbq/4/

JS

$(".datepicker").datepicker({
    minDate: 0,
    numberOfMonths: [12, 1],
    beforeShowDay: function (date) {
        var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
        var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
        return [true, date1 && ((date.getTime() == date1.getTime()) || (date2 && date >= date1 && date <= date2)) ? "dp-highlight" : ""];
    },
    onSelect: function (dateText, inst) {
        var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
        var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
        if (!date1 || date2) {
            $("#input1").val(dateText);
            $("#input2").val("");
            $(this).datepicker();
        } else {
            $("#input2").val(dateText);
            $(this).datepicker();
        }
    }
});

Ce que je veux être capable de faire est un sélecteur de gamme comme ceci: http://jsfiddle.net/D3wLX/1/

Si vous sélectionnez une date antérieure, celle-ci devient automatiquement la première date de la plage et les dates intermédiaires sont mises en surbrillance. À l’heure actuelle, ma solution d’interface utilisateur jQuery originale ne fait que mettre la date antérieure dans la deuxième entrée et ne met pas en évidence les dates intermédiaires.

12
Mcestone

J'ai trouvé la réponse ici:

http://www.benknowscode.com/2012/11/selecting-ranges-jquery-ui-datepicker.html

Excellent tutoriel

$.datepicker._defaults.onAfterUpdate = null;
var datepicker__updateDatepicker = $.datepicker._updateDatepicker;
$.datepicker._updateDatepicker = function( inst ) {
   datepicker__updateDatepicker.call( this, inst );
   var onAfterUpdate = this._get(inst, 'onAfterUpdate');
   if (onAfterUpdate)
      onAfterUpdate.apply((inst.input ? inst.input[0] : null),
         [(inst.input ? inst.input.val() : ''), inst]);
}
$(function() {
   var cur = -1, prv = -1;
   $('#jrange div')
      .datepicker({
            //numberOfMonths: 3,
            changeMonth: true,
            changeYear: true,
            showButtonPanel: true,
            beforeShowDay: function ( date ) {
                  return [true, ( (date.getTime() >= Math.min(prv, cur) && date.getTime() <= Math.max(prv, cur)) ? 'date-range-selected' : '')];
               },
            onSelect: function ( dateText, inst ) {
                  var d1, d2;
                  prv = cur;
                  cur = (new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay)).getTime();
                  if ( prv == -1 || prv == cur ) {
                     prv = cur;
                     $('#jrange input').val( dateText );
                  } else {
                     d1 = $.datepicker.formatDate( 'mm/dd/yy', new Date(Math.min(prv,cur)), {} );
                     d2 = $.datepicker.formatDate( 'mm/dd/yy', new Date(Math.max(prv,cur)), {} );
                     $('#jrange input').val( d1+' - '+d2 );
                  }
               },
            onChangeMonthYear: function ( year, month, inst ) {
                  //prv = cur = -1;
               },
            onAfterUpdate: function ( inst ) {
                  $('<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" data-handler="hide" data-event="click">Done</button>')
                     .appendTo($('#jrange div .ui-datepicker-buttonpane'))
                     .on('click', function () { $('#jrange div').hide(); });
               }
         })
      .position({
            my: 'left top',
            at: 'left bottom',
            of: $('#jrange input')
         })
      .hide();
   $('#jrange input').on('focus', function (e) {
         var v = this.value,
             d;
         try {
            if ( v.indexOf(' - ') > -1 ) {
               d = v.split(' - ');
               prv = $.datepicker.parseDate( 'mm/dd/yy', d[0] ).getTime();
               cur = $.datepicker.parseDate( 'mm/dd/yy', d[1] ).getTime();
            } else if ( v.length > 0 ) {
               prv = cur = $.datepicker.parseDate( 'mm/dd/yy', v ).getTime();
            }
         } catch ( e ) {
            cur = prv = -1;
         }
         if ( cur > -1 )
            $('#jrange div').datepicker('setDate', new Date(cur));
         $('#jrange div').datepicker('refresh').show();
      });
});
.wrapper {
   height: 600px;
}
#jrange input {
   width: 200px;
}
#jrange div {
   font-size: 9pt;
}
.date-range-selected > .ui-state-active,
.date-range-selected > .ui-state-default {
   background: none;
   background-color: lightsteelblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="wrapper">
   <div id="jrange" class="dates">
    <input />
    <div></div>
   </div>
</div>

6
Mcestone

Votre script était exactement ce que je cherchais. J'ai bifurqué votre violon original et n'ai apporté qu'un léger ajustement à votre onSelect pour le faire fonctionner comme vous le souhaitiez.

onSelect: function(dateText, inst) {
    var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
    var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
    var selectedDate = $.datepicker.parseDate($.datepicker._defaults.dateFormat, dateText);


    if (!date1 || date2) {
        $("#input1").val(dateText);
        $("#input2").val("");
        $(this).datepicker();
    } else if( selectedDate < date1 ) {
        $("#input2").val( $("#input1").val() );
        $("#input1").val( dateText );
        $(this).datepicker();
    } else {
        $("#input2").val(dateText);
        $(this).datepicker();
    }
}

Ce qui manquait dans votre section d'origine consistait simplement en une vérification permettant de comparer la valeur de date actuellement sélectionnée à celle déjà saisie.

Voici mon violon fourchu: http://jsfiddle.net/sWbfk/

17
Jamie Layne

Mec, ton code est vraiment ce dont j'avais besoin!

Et avec la correction de Jamie Layne, j'ai décidé de l'utiliser pour créer un plugin.

Voici le lien pour jsfiddle: http://jsfiddle.net/dxLRm/35/ (lien mis à jour le 2014/01/01)

Depuis que je dois montrer du code, voici ce que j'ai:

(function ($) {
$.prototype.rangedatepicker = function (o,x,y) {
    var dp = $.datepicker,
        cl = dp.markerClassName,
        di = 'data-rdp-i',
        df = 'data-rdp-f';

    switch(o)
    {
        case 'option':
            return $(this).datepicker('option');
        case 'hide':
            return $(this).datepicker('hide');
        case 'show':
            return $(this).datepicker('show');
        case 'getInitialDate':
            return dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(di)||'');
        case 'getFinalDate':
            return dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(df)||'');
        case 'getRange':
            var ini=dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(di)||''),
                fin=dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(df)||'');
            return (!ini&&!fin)?null:[ini,fin];
        case 'getNumDays':
            var ini=dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(di)||''),
                fin=dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(df)||'');
            return (ini+0==0||fin+0==0)?0:Math.round((fin-ini)/86400000)+1;
        case 'removeRange':
            return $(this).attr(di,'').attr(df,'').datepicker('setDate',null);
        case 'destroy':
            return $(this).removeAttr(di).removeAttr(df).datepicker('destroy');
        case 'serialize':
            return this[0].id+'_initial='+this[0].getAttribute(di)+'&'+this[0].id+'_final='+this[0].getAttribute(df);
        default:
        var defaults={
            allowSelectOneDay: false,
            alwaysSetDateToFirstDay: true,
            rangeEnabled: true,
            rangeClass: 'ui-state-default ui-state-active'//'dp-highlight'
        };
            o = $.extend({}, defaults, $.datepicker._defaults, o);
        return $(this).each(function () {
            if (!$.datepicker) return;
            var t = this,
                hd = !! ((' ' + t.className + ' ').indexOf(' ' + cl + ' ') + 1);
            $(t).datepicker($.extend({}, o, {
                beforeShowDay: function (d) {
                    if (o.rangeEnabled) {
                        var d1 = dp.parseDate(o.dateFormat, t.getAttribute(di) || ''),
                            d2 = dp.parseDate(o.dateFormat, t.getAttribute(df) || ''),
                            y = (function (d) {
                                try {
                                    return o.beforeShowDay.call(t, d);
                                } catch (e) {}
                            })(d) || [true, '', null],
                            x = ((y && y[0] !== false) || !y) && d1 && ((d.getTime() == d1.getTime()) || (d2 && d >= d1 && d <= d2));
                        return (!d1||!d2)?y||[true,'',null]:[y[0]&&x, (x ? o.rangeClass || defaults.rangeClass : '') + (y[1] ? ' ' + y[1] : ''), y[2]];
                    } else {
                        return (function (d) {
                            try {
                                return o.beforeShowDay.call(t, d);
                            } catch (e) {}
                        })(d) || [true, '', null];
                    }
                },
                onSelect: function (dt, x) {
                    if (o.rangeEnabled) {
                        var i = t.getAttribute(di) || '',
                            f = t.getAttribute(df) || '',
                            d1 = dp.parseDate(o.dateFormat, i),
                            d2 = dp.parseDate(o.dateFormat, f),
                            s = dp.parseDate(o.dateFormat, dt);
                        if ((dt == i && dt == f) || (!o.allowSelectOneDay && ((dt == i && !f) || (dt == f && !i)))) {
                            t.removeAttribute(di);
                            t.removeAttribute(df);
                            $(t).datepicker('setDate', null);
                        } else if (!d1 || d2) {
                            t.setAttribute(di, dt);
                            t.removeAttribute(df);
                            o.alwaysSetDateToFirstDay && $(t).datepicker('setDate', s);
                        } else if (s < d1) {
                            t.setAttribute(df, i);
                            t.setAttribute(di, dt);
                            o.alwaysSetDateToFirstDay && $(t).datepicker('setDate', s);
                        } else {
                            t.setAttribute(df, dt);
                            o.alwaysSetDateToFirstDay && $(t).datepicker('setDate', d1);
                        }
                    } else {
                        t.removeAttribute(di);
                        t.removeAttribute(df);
                        $(t).datepicker('setDate', dp.parseDate(o.dateFormat, dt));
                    }

                    try {
                        if($(t).datepicker('getDate'))o.onSelect.call(t, dt, x);
                    } catch (e) {}
                }
            }));
        });
    }
};
})(window.jQuery);

Vous devriez accéder au violon et lire la liste des choses à faire!

Toute idée ou morceau de code est apprécié!

1
Ismael Miguel

Je cherchais également un moyen d'étendre le plugin Datepicker jQuery pour utiliser le style Bootstrap.

Bootstrap-Date Range Picker de Dan Grossman montre quelques exemples réels avec du code de travail. 

Voici le projet GitHub ainsi.

Enfin, voici une capture d'écran de la simplicité et de la puissance du design:

 screenshot

1
Blairg23

Merci, j'ai besoin de ce genre de code. Voici mon code: 

<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/resources/demos/style.css">
<script     src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

 <div id="Datepicker"></div>
<p>
<label><b>Checkin:</b></label> <label id="checkinDate"></label>
<label><b>Checkout:</b></label> <label id="checkoutDate"></label>
</p>

/** Display Checkin Datepicker and Checkout DatePicker */
<script>
datePicker();
function datePicker(){
   $(document).ready(function(){
      $( "#Datepicker" ).datepicker({
         dateFormat: "MM d, yy",
     minDate: 0,
     maxDate: "+3M +0D", 
         beforeShowDay: dateRange,
     onSelect: DRonSelect
      });
   });
}

function dateRange(date){
   var date1 = $.datepicker.parseDate("MM d, yy", $("#checkinDate").text());
   var date2 = $.datepicker.parseDate("MM d, yy", $("#checkoutDate").text());
   var isHighlight = date1 && ((date.getTime() == date1.getTime()) || (date2 && date >= date1 && date <= date2));
      $(document).ready(function(){
  // $("td.dp-highlight").text("Y");

});
   return [true, isHighlight ? "dp-highlight" : ""];
}

function DRonSelect(dateText, inst) {
   var date1 = $.datepicker.parseDate("MM d, yy", $("#checkinDate").text());
   var date2 = $.datepicker.parseDate("MM d, yy", $("#checkoutDate").text());
      if (!date1 || date2) {
         $("#checkinDate").text(dateText);
     $("#checkoutDate").text("");
         $("#Datepicker").datepicker();
      } 
      else {
         if ( $.datepicker.parseDate("MM d, yy", $("#checkinDate").text()) >= 
$.datepicker.parseDate("MM d, yy", dateText)) {
            $("#checkinDate").text(dateText);
            $("#checkoutDate").text("");
            $("#Datepicker").datepicker();
         }
         else {
        $("#checkoutDate").text(dateText);
            $("#Datepicker").datepicker();
         }
      }   
}
</script>

Mon code est un exemple de code provenant des autres, mais il diffère par la sélection de date, la plage de dates et les points saillants. J'ai créé et enregistre un code dans JSFIDDLE

https://jsfiddle.net/kk585b4g/

1
Marvin

En cherchant moi-même un sélecteur de dates, j'ai trouvé cette page. J'ai essayé la plupart des idées suggérées et même démontré ici et transformé tout cela en une extension facile à utiliser et à intégrer: https://github.com/BuroRaDer/DateRangePicker . Essayez la page de démonstration pour voir comment cela fonctionne. Je suppose que je pourrais en faire une véritable extension jQuery, mais pour l’instant je suis content de la façon dont cela fonctionne.

Démos en direct: 

Tous les deux sont des sites Drupal utilisant le module Calendrier de disponibilité dans lequel il est maintenant intégré.

0
fietserwin

Je cherchais une version qui fonctionnerait même si elle n’était pas en ligne. Je voulais pouvoir cliquer sur les champs de saisie pour lancer la plage datepicker. Tous les exemples de datepicker que j'ai pu trouver étaient en ligne (y compris les versions de mcestone et Jamie Layne ci-dessus, qui constituent la base de ce code forké).

Voici le violon: http://jsfiddle.net/boson/pjffdtz2/

La partie difficile semble être que datepicker gère plusieurs entrées lorsqu'il n'est pas en ligne. Datepicker ne gérera pas facilement deux entrées si vous voulez ouvrir Datepicker avec focus - il y avait certainement un «truc». Si vous associez le sélecteur de date à une entrée masquée (display: none), créez cette entrée masquée avant les entrées visibles, puis faites en sorte que vos entrées visibles affichent le sélecteur de date sur un événement de clic: tout va bien.

J'ai donc pris la réponse initiale et apporté quelques modifications mineures:

  • En html, créez une entrée masquée associée au sélecteur de date. Répertoriez-le avant les entrées visibles.
  • En html, utilisez l'événement click des entrées visibles pour afficher le sélecteur de date associé à l'entrée masquée.
  • Dans le sélecteur de date Javascript onSelect, mettez temporairement le sélecteur de date en mode en ligne jusqu'à ce que les deux dates soient cliquées. Cela vous permet de cliquer sur plusieurs dates avant la fermeture de datepicker (la plage "À" date et la date "De") - ceci transforme essentiellement datepicker en un multiclick datepicker.
  • Dans le sélecteur de date onClose, désactivez le mode en ligne. Cela permet à quelqu'un de cliquer sur les champs de saisie pour ouvrir à nouveau le sélecteur de date. 
  • Dans beforeShow, déplacez le sélecteur de date de quelques pixels vers le bas pour que vous puissiez voir les champs de saisie et le sélecteur de date.

Voici le code:

$(function() {
  $(".rangepicker").datepicker({
    minDate: 0,
    numberOfMonths: [2, 1],
    beforeShow: function (input, inst) {
      var rect = input.getBoundingClientRect();
      setTimeout(function () {
	      inst.dpDiv.css({ top: rect.top + 40, left: rect.left + 0 });
      }, 0);
    },
    beforeShowDay: function(date) {
      var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
      var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
      var isHighlight =
         date1 && ((date.getTime() == date1.getTime()) || (date2 && date >= date1 && date <= date2));
      return [true, isHighlight ? "dp-highlight" : ""];
    },
    onSelect: function(dateText, inst) {
      var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
      var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
      var selectedDate = $.datepicker.parseDate($.datepicker._defaults.dateFormat, dateText);

      if (!date1 || date2) {
        $("#input1").val(dateText);  
        $("#input2").val("");        
      } else if (selectedDate < date1) {
          $("#input2").val($("#input1").val()); 
          $("#input1").val(dateText);  
      } else {
          $("#input2").val(dateText);  
      }
      $(this).data('datepicker').inline = true;           
      $(this).datepicker();
    },
    onClose: function() {
      // Since we went inline as soon as the date input was clicked
      // (to leave the datepicker up for both dates selection),
      // turn inline back off again so date input click will once again
      // display the datepicker
      $(this).data('datepicker').inline = false;
    }
  });
});
.dp-highlight .ui-state-default {
          background: #484;
          color: #FFF;
        }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<input type="text" id="input1_1" class="rangepicker" style="display: none">
<p>
    Dates:
    <label><b>To:</b></label>
    <input type="text" id="input1" onclick="$('.rangepicker').datepicker('show');">
    <label><b>From:</b></label>
    <input type="text" id="input2" onclick="$('.rangepicker').datepicker('show');">
    <button id="done">Done</button>
</p>

Il reste beaucoup de choses à améliorer. Nécessite une meilleure validation des entrées. Souhaitons particulièrement que le bouton 'Terminé' de Datepicker fonctionne en mode en ligne, mais Datepicker n’a pas été conçu pour ce scénario (il serait certainement agréable d’avoir un indicateur de bouton Terminé terminé dans Datepicker). Donc, pour le moment, à côté des champs de saisie, un bouton de fromage fait qui ne fait rien (à part encourager l'utilisateur à ne pas mettre l'accent sur le sélecteur de date pour le fermer).

0
boson