Friday, April 12, 2013

Collection : Javascript : Using select tags for date selection

Now a days generally most of sites use calendar dialog boxes for selecting a date in forms. We get all functionality for that including leap years and 30 or 31 days depending on the month. But how can we get these all when we are using simple drop downs or select is the question. Well we can use javascript to validate the fields.
In this post we will use jquery for validating 3 dropdowns which are year, month and date. As I was working on PHP the example will hold PHP code.

In this example consider that we are working on employee joining form and we want to add a joining date.
In html
<div id="joiningDate">
  <?php 
    $joining_date = select_tag("employees[joining_date][month]", unserialize(MONTHS), date('F'), array('class' => 'month'));
    $joining_date .= select_tag("employees[joining_date][date]", unserialize(DAYS), date('j'), array(class' => 'date'));
    $joining_date .= select_tag("employees[joining_date][year]", array("2012", "2013", "2014", "2015", '2016', '2017'), date('Y'), array('class' => 'year'));
    echo $joining_date;
  ?>
</div>
Note : For select_tag please check Collection : PHP : select_tag

Define some constants for long arrays.
Constants
defined('MONTHS') or define("MONTHS", serialize(array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')));
defined('DAYS') or define('DAYS', serialize(array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31')));
Finally the javascript function which will actually play the magic in html. Check the comments for explanation. In Javascript file
$(document).ready(function(){
  validateDateFields('#joiningDate');
});

function validateDateFields(divID){
  // Jquery objects of date and month fields in new function
  var dateField = $(divID).find('select.date');
  var monthField = $(divID).find('select.month');

  // Months hash for adding or removing the options tags 
  var monthsHash = {
    'January' : 1, 
    'February' : 2, 
    'March' : 3, 
    'April' : 4, 
    'May' : 5, 
    'June' : 6, 
    'July' : 7, 
    'August' : 8, 
    'September' : 9, 
    'October' : 10, 
    'November' : 11, 
    'December' : 12
  };
  // Months with 31 days
  var fullMonth = [1, 3, 5, 7, 8, 10, 12];
  
  // Current values of the fields
  var dateVal = dateField.val();
  var monthVal = monthField.val();
  var yearVal = $(divID).find('select.year').val();
  
  // Check current year if leap year
  var isLeap = new Date(yearVal, 1, 29).getMonth() == 1;
  // Date field child node with options 29, 30 and 31
  var dateOption29 = dateField.children('option[value=29]');
  var dateOption30 = dateField.children('option[value=30]');
  var dateOption31 = dateField.children('option[value=31]');
  // defining other variable 
  var appendOptions;
    
  if(monthsHash[monthVal]==2){
    // First : If selected month in 2 or February
    if(isLeap){
      // If the selected year is leap year
      if(dateOption29.length == 0){
        // If date field child nodes do not have any option with value 29
        dateField.append('<option value=29>29</option>');        
      }
    }else{
      // If its not a leap year remove date field child node option with value 29
      dateOption29.remove();
    }
    // In month 2 or february by default remove date field child options with value 30 and 31
    dateOption30.remove();
    dateOption31.remove();
  }else if($.inArray(monthsHash[monthVal], fullMonth) > -1){
    // If the selected month is of 31 days 
    if(dateOption31.length == 0){
      // If option with value 31 do not exist 
      if(dateOption29.length == 0){
        // If option with value 29 do not exist 
        appendOptions += '<option value=29>29</option> <option value=30>30</option>';
      }else if(dateOption30.length == 0){
        // If option with value 30 do not exist 
        appendOptions += '<option value=30>30</option>';
      }
      appendOptions += '<option value=31>31</option>';
      // append options which do not exist in date field child nodes
      dateField.append(appendOptions);
    }
  }else{
    // If the selected month is of 30 days 
    if(dateOption29.length == 0){
      // If option with value 29 do not exist 
      dateField.append('<option value=29>29</option> <option value=30>30</option>');
    }else if(dateOption30.length == 0){
      // If option with value 30 do not exist 
      dateField.append('<option value=30>30</option>');
    }else if(dateOption31.length > 0){
      // If option with value 31 exist
      dateOption31.remove();
    }
  }
  
  // Add other code for updating any values here.
}