Le sélecteur de dates en javascript

calendriers.jpg

Edit du 17/11/2011, passage en version 0.3.1 pour patcher internet explorer. test ok sur IE8 et IE 9, les autres versions n’ont pas été testées.

L’objet de ce codage est de permettre l’intégration simple (#feignasse) sur des sites web d’un sélecteur de date plus sexy que 3 listes déroulantes. Je me suis inspiré du "datetimepicker" que l’on trouve dans les objets comdlg [1] Voici la version 0.3 .1du sélecteur de dates en javascript By gnieark. Les modifications par rapport à la précédente sont:

  • Le code se greffe sur un <input type="text"> permettant aux personnes ayant désactivé javascript de quand même avoir un moyen de renseigner la date.
  • Il détecte si l’écran est tactile[2]. Si c’est le cas, ce sont 3 listes déroulantes (jour, mois, année) qui sont générées. Le datetimepicker n’étant pas idéal pour les gros doigts.
  • On peut naviguer dans le temps (Doctor Who tu es là?) par le mois et l’année pour aller plus vite.
  • Lors du déroulement, il ne décale plus tout ce qui est autours de lui, mais se place par dessus (CSS Powa!).

J’ai pris en compte une partie des remarques de Franck, de Zigazou et les trucs qui me chiffonnaient pour cette évolution.

Présentation du sélecteur de date:

Le voici dans une iframe (URL de la page: http://blog-du-grouik.tinad.fr/public/datetimepicker/)

Intégration et code source:

Creez un script javascript sur votre page ou ajoutez ceci au js existant:

J’ai volontairement mis des noms de function assez tordus afin d’éviter les collisions avec d’autres codes lors de l’intégration. De plus, seules les array lesJours, lesJoursFull,lesMois sont des variables globales. Vous avez peu de chance que ce code parasite vos autres scripts javascript.

/*
     * Codé par Gnieark http://blog-du-grouik.tinad.fr/ Novembre 2011 version 0.3.1
     * Ditribué sans aucune garantie.
     * Vous pouvez faire ce que vous voullez de ce code, l'utiliser, le revendre,
     * le modifier etc... à condition de laisser intact le présent avertissement et la paternité.
     * même en cas de "minification" du code.
     */
if(!Element.setAttribute){Element.prototype.setAttribute=function(attribut,valeur){switch(attribut)
{case"onClick":case"onMouseOut":case"onMouseOver":eval('this.'+attribut.toLowerCase()+'=function(){'+valeur+'}');return;break;case"type":var newObject=document.createElement(this.tagName);newObject.type=valeur;if(this.size)newObject.size=this.size;if(this.value)newObject.value=this.value;if(this.name)newObject.name=this.name;if(this.id)newObject.id=this.id;if(this.className)newObject.className=this.className;this.parentNode.replaceChild(newObject,this);return;break;case"class":var correctAttribut="className";break;default:var correctAttribut=attribut;break;}
eval('this.'+correctAttribut+'="'+valeur+'"');}}
lesJours=new Array('dim','lun','mar','mer','jeu','ven','sam');lesJoursFull=new Array('dimanche','lundi','mardi','mercredi','jeudi','vendredi','samedi');lesMois=new Array('janvier','fevrier','mars','avril','mai','juin','juillet','aout','septembre','octobre','novembre','decembre');function createElem(type,attributes)
{var elem=document.createElement(type);for(var i in attributes)
{elem.setAttribute(i,attributes[i]);}
return elem;}
function dtpickerDateOfInput(inputDate)
{try{var laDateArray=inputDate.getAttribute("value").split("-");var laDate=new Date(laDateArray[0],laDateArray[1]-1,laDateArray[2]);}catch(err){return false;}
return laDate;}
function dtpickerDateToInput(inputDate,ladate)
{inputDate.setAttribute("value",ladate.getFullYear()+"-"+(ladate.getMonth()+1)+"-"+ladate.getDate());}
function dtpickerChgMonth(inputDate,increment)
{var lastdate=dtpickerDateOfInput(inputDate);dtpickerChgSelectedDate(inputDate,new Date(lastdate.getFullYear(),lastdate.getMonth()+increment,lastdate.getDate()));}
function dtpickerChgYear(inputDate,increment)
{var lastdate=dtpickerDateOfInput(inputDate);dtpickerChgSelectedDate(inputDate,new Date(lastdate.getFullYear()+increment,lastdate.getMonth(),lastdate.getDate()));}
function dtpickerChgSelectedDate(inputDate,newDate)
{var oldDate=dtpickerDateOfInput(inputDate);var oldDateMonth=oldDate.getMonth();var oldDateYear=oldDate.getFullYear();var oldDateJourDuMois=oldDate.getDate();var newDateMonth=newDate.getMonth();var newDateYear=newDate.getFullYear();var newDateJourDuMois=newDate.getDate();var inputDateId=inputDate.getAttribute("id");if((oldDateMonth!=newDateMonth)||(oldDateYear!=newDateYear)){var name=inputDate.getAttribute("name");var tableBody=dtpickerSetTableBody(newDate,inputDate);document.getElementById("datetimePickerTbody"+name).parentNode.replaceChild(tableBody,document.getElementById("datetimePickerTbody"+name));tableBody.setAttribute("id","datetimePickerTbody"+name);}else{document.getElementById(inputDateId+"-td"+oldDateYear+"-"+oldDateMonth+"-"+oldDateJourDuMois).setAttribute("class","dayUnSelected");document.getElementById(inputDateId+"-td"+newDateYear+"-"+newDateMonth+"-"+newDateJourDuMois).setAttribute("class","daySelected");dtpickerDateToInput(inputDate,newDate);}
document.getElementById(inputDateId+"thTitre").innerHTML=lesJoursFull[newDate.getDay()]+" "+newDateJourDuMois+" "+lesMois[newDateMonth]+" "+newDateYear;}
function dtpickerSetTableBody(selectedDate,inputDate)
{var tBody=createElem("tbody");var selectedJour=selectedDate.getDate();var selectedMois=selectedDate.getMonth();var selectedAnnee=selectedDate.getFullYear();var inputDateId=inputDate.getAttribute("id");var trDays=createElem("tr");tBody.appendChild(trDays);for(var i=0;i<7;i++)
{var tdLeJour=createElem("td",{});tdLeJour.innerHTML=lesJours[i];trDays.appendChild(tdLeJour);}
tBody.appendChild(trDays);var unDuMois=new Date(selectedDate.getFullYear(),selectedDate.getMonth(),1);var unDuMoisSuivant=new Date(unDuMois.getFullYear(),unDuMois.getMonth()+1,1);var startDate=new Date(unDuMois.getFullYear(),unDuMois.getMonth(),unDuMois.getDate()-unDuMois.getDay());var increment=0;var currentDate=startDate;var currentDateMonth=currentDate.getMonth();var currentDateYear=currentDate.getFullYear();var currentDateJourDuMois=currentDate.getDate();while(currentDate<=unDuMoisSuivant)
{var trDates=createElem("tr");for(var dayNumber=0;dayNumber<7;dayNumber++)
{if((currentDateJourDuMois==selectedJour)&&(currentDateMonth==selectedMois)){var tdclass="daySelected";}else{var tdclass="dayUnSelected";}
if(currentDateMonth!=selectedMois){var tdclass="dayWrongMonth";}
var tdJour=createElem("td",{"onClick":"dtpickerChgSelectedDate(document.getElementById('"+inputDateId+"'), new Date("+currentDateYear+","+currentDateMonth+","+currentDate.getDate()+"));","class":tdclass,"id":inputDateId+"-td"+currentDateYear+"-"+currentDateMonth+"-"+currentDateJourDuMois,"title":lesJoursFull[currentDate.getDay()]+" "+currentDateJourDuMois+"-"+(currentDateMonth+1)+"-"+currentDateYear});tdJour.innerHTML=currentDateJourDuMois;trDates.appendChild(tdJour);increment++;currentDate=new Date(startDate.getFullYear(),startDate.getMonth(),startDate.getDate()+increment);currentDateMonth=currentDate.getMonth();currentDateYear=currentDate.getFullYear();currentDateJourDuMois=currentDate.getDate();}
tBody.appendChild(trDates);}
var trTitre=createElem("tr",{"class":"dtPickerNavTr"});var tdMonth=createElem("td",{"colSpan":4});var emPrevious=createElem("em",{"onClick":"dtpickerChgMonth(document.getElementById('"+inputDateId+"'), -1)","class":"dtPickerEmButton","title":"Mois précédent"});emPrevious.innerHTML="&lt;";tdMonth.appendChild(emPrevious);var emMonth=createElem("em",{});emMonth.innerHTML="&nbsp;"+lesMois[selectedMois]+"&nbsp;";tdMonth.appendChild(emMonth);var emNext=createElem("em",{"onClick":"dtpickerChgMonth(document.getElementById('"+inputDateId+"'), +1)","class":"dtPickerEmButton","title":"Mois suivant"});emNext.innerHTML="&gt;";tdMonth.appendChild(emNext);trTitre.appendChild(tdMonth);var tdYear=createElem("td",{"colSpan":3});var emPrevious=createElem("em",{"onClick":"dtpickerChgYear(document.getElementById('"+inputDateId+"'), -1)","class":"dtPickerEmButton","title":selectedAnnee-1});emPrevious.innerHTML="&lt;";tdYear.appendChild(emPrevious);var emYear=createElem("em",{});emYear.innerHTML="&nbsp;"+selectedAnnee+"&nbsp;";tdYear.appendChild(emYear);var emNext=createElem("em",{"onClick":"dtpickerChgYear(document.getElementById('"+inputDateId+"'), +1)","class":"dtPickerEmButton","title":selectedAnnee+1});emNext.innerHTML="&gt;";tdYear.appendChild(emNext);trTitre.appendChild(tdYear);tBody.appendChild(trTitre);dtpickerDateToInput(document.getElementById(inputDateId),selectedDate);return tBody;}
function dateTimePickerChangeSelect(inputDate)
{var name=inputDate.getAttribute("name");var newDate=new Date(document.getElementById("datetimePickerSelectAnnee"+name).value,document.getElementById("datetimePickerSelectMois"+name).value,document.getElementById("datetimePickerSelectJour"+name).value);dtpickerDateToInput(inputDate,newDate);document.getElementById("datetimePickerSelectAnnee"+name).value=newDate.getFullYear();document.getElementById("datetimePickerSelectMois"+name).value=newDate.getMonth();document.getElementById("datetimePickerSelectJour"+name).value=newDate.getDate();}
function dateTimePicker(inputDate,reduire)
{var ladate=dtpickerDateOfInput(inputDate);if(ladate==false){ladate=new Date();}
var leMois=ladate.getMonth();var lAnnee=ladate.getFullYear();var leJour=ladate.getDate();var name=inputDate.getAttribute("name");var inputDateId=inputDate.getAttribute("id");inputDate.setAttribute("type","hidden");try{document.createEvent("TouchEvent");var isTactile=true;}catch(e){var isTactile=false;}
document.write('<div id="datetimePicker'+name+'" class="dateTimePicker"></div>');if(isTactile)
{var selectJour=createElem("select",{"id":"datetimePickerSelectJour"+name,"onChange":"dateTimePickerChangeSelect(document.getElementById('"+inputDateId+"'));"});for(var i=1;i<32;i++)
{if(i==leJour){var optionJour=createElem("option",{"value":i,"selected":"selected"});}else{var optionJour=createElem("option",{"value":i});}
optionJour.innerHTML=i;selectJour.appendChild(optionJour);}
document.getElementById('datetimePicker'+name).appendChild(selectJour);var selectMois=createElem("select",{"id":"datetimePickerSelectMois"+name,"onChange":"dateTimePickerChangeSelect(document.getElementById('"+inputDateId+"'));"});for(var i=0;i<12;i++)
{if(leMois==i){var optionMois=createElem("option",{"value":i,"selected":"selected"});}else{var optionMois=createElem("option",{"value":i});}
optionMois.innerHTML=lesMois[i];selectMois.appendChild(optionMois);}
document.getElementById('datetimePicker'+name).appendChild(selectMois);var selectAnnee=createElem("select",{"id":"datetimePickerSelectAnnee"+name,"onChange":"dateTimePickerChangeSelect(document.getElementById('"+inputDateId+"'));"});for(var i=1900;i<2100;i++)
{if(lAnnee==i){var optionAnnee=createElem("option",{"value":i,"selected":"selected"});}else{var optionAnnee=createElem("option",{"value":i});}
optionAnnee.innerHTML=i;selectAnnee.appendChild(optionAnnee);}
document.getElementById('datetimePicker'+name).appendChild(selectAnnee);}else{if(reduire){var tableJours=createElem("table",{"id":"datetimePickerTable"+name,"onMouseOver":"document.getElementById('datetimePickerTbody"+name+"').setAttribute('class','dtPickerOn');","onMouseOut":"document.getElementById('datetimePickerTbody"+name+"').setAttribute('class','dtPickerOff');"});}else{var tableJours=createElem("table",{"id":"datetimePickerTable"+name});}
var lignesTitres=createElem("thead",{});var trTitre=createElem("tr",{});var thTitre=createElem("th",{"colSpan":7,"id":inputDateId+"thTitre"});thTitre.innerHTML=lesJoursFull[ladate.getDay()]+" "+leJour+" "+lesMois[leMois]+" "+lAnnee+"&nbsp;";trTitre.appendChild(thTitre);lignesTitres.appendChild(trTitre);tableJours.appendChild(lignesTitres);var tableBody=dtpickerSetTableBody(ladate,inputDate);tableBody.setAttribute("id","datetimePickerTbody"+name);if(reduire){tableBody.setAttribute("class","dtPickerOff");}else{tableBody.setAttribute("class","dtPickerOn");}
tableJours.appendChild(tableBody);document.getElementById('datetimePicker'+name).appendChild(tableJours);}}

Voir le code non minifié --- datepicker.js

Créez ou ajoutez ceci au CSS de votre page:

.dateTimePicker{ height: 15px; font-size: 10px;}
.dateTimePicker table {
 position: absolute;
 margin: 0px;
 padding: 0px;
 background-color:#FFF0F5;
 width: 200px;
 
}
.dateTimePicker table tbody{  	
    position: relative;
    
}
.dateTimePicker table tbody tr td{  	
  text-align: center;
  
}
.dateTimePicker table tbody tr td.dayUnSelected{
  background-color: #A3AED3;
  cursor:  pointer; 
}
.dateTimePicker table tbody tr td.daySelected{
  color : #666;
  background-color: #DDDDDD;
  cursor:  pointer;
  border:1px solid #005BAA;
}
.dateTimePicker table tbody tr td.dayWrongMonth{
  background-color: #fff;
  cursor:  pointer;
}
.dateTimePicker table tbody tr td.dayUnSelected:hover{
  color : #fff;
}
.dateTimePicker table tr th{
  color : #fff;
  background-color: #59627A;
  cursor:  pointer;
}
.dtPickerOff {
  display: none;
}
.dtPickerEmButton{
  cursor:  pointer;
}
.dtPickerEmButton:hover{
   color : #fff;
  background-color: #59627A; 
}
.dtPickerOn{}
.dateTimePicker table tbody tr.dtPickerNavTr td{border:1px solid #005BAA;}

Intégration sur la page

Sur votre page web: Insérez un input de type text. c’est lui qui prendra la valeur de la date au format mysql: AAAA-MM-JJ. De cette manière si un visiteur n’active pas le javascript il pourra quand meme répondre.

De plus au niveau de l’intégration dans une <form> que la méthode soit en GET ou en POST, vous ne vous souciez pas du datetime picker en javascript pour récupérer la date. Ce dernier se charge de masquer le "input" en le transformant en <input type="hidden"> et de mettre à jour son attribut "value" lors des changements de date par l’utilisateur.

Juste après l’input vous appellez la function javascript dateTimePicker(elementInputDate, déroulé); où:

  • elementInputDate désigne l’input qui reçoit la date que l’on soite transformer. On peut l’appeller par son id comme ceci:document.getElementById("dtPicker")
  • déroulé est un boolean. true, il sera plié et se déroulera lors du survol du curseur; false: il est déplié en permanance.
  • Le sélecteur de date s’initialisera sur la date qu’il trouve dans l’input. S’il n’y arrive pas (champs vide ou mauvais format), il prendra la date actuelle.

Exemple:

<input type="text" id="dtPicker" name="ladate" maxlength="9" value="2011-09-02"/>
	<script type="text/javascript">
	<!--
	  dateTimePicker(document.getElementById("dtPicker"), true);
	-->
	</script>

L’input qui reçoit la date doit avoir un id unique sur la page (normal). Son "name" doit aussi être unique par rapport aux autres datetimepicker de la même date.

Ah j’oubliais; sous un écran tactile la sélection de dates par des listes déroulantes se présente ainsi: dtpicker-listes.jpeg

Notes

[1] Rah l’époque de Visual studio 6.0

[2] Testé sur Android 3 et 4 via le SDK

Ajouter un commentaire

Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.

La discussion continue ailleurs

URL de rétrolien : https://blog-du-grouik.tinad.fr/trackback/696

Fil des commentaires de ce billet

Page top