Dates not working in this

This is in PHP forum by mistake - maybe someone can delete the PHP one and keep this one, I don’t know how, sorry.

I put a thread out previously and got some responses. I found a help area that gave me what I wanted - kind of.

If the code below is run and NO DATE is put in, the function runs as I expect. However, if there is a date in the DATE box, then, well, the number of days does not change! That seems weird…

I would like this to put the proper number of days via the selected month if it can. Just not sure what is really going on and/or where the issue could be. Looks like this should work like I want, but… well, let the experts beat me and it up!

Oh, I really don’t have a lick of Javascript experience and am doing this for learning more than anything

Here is my code:
HTML Code:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=windows-1250">
  <meta name="generator" content="PSPad editor, www.pspad.com">
  <title></title>


  </head>
  <body>
 <form name="myform"><fieldset>
    <legend>Date Selection</legend>
    <label for="year">Year: </label><select name="eventYear" id="eventYear" size="1">
    <option value=" " selected="selected"> </option>
    <option value="2000">2000</option>
    <option value="2001">2001</option>
    <option value="2002">2002</option>
    </select>
    <label for="month">Month: </label>
    <select name="eventMonth" id="eventMonth" size="1">
    <option value=" " selected="selected"> </option>
    <option value="1">January</option>
    <option value="2">February</option>
    <option value="3">March</option>
    <option value="4">April</option>
    <option value="5">May</option>
    <option value="6">June</option>
    <option value="7">July</option>
    <option value="8">August</option>
    <option value="9">September</option>
    <option value="10">October</option>
    <option value="11">November</option>
    <option value="12">December</option>

    </select>

    <label for="day">Day: </label><select name="eventStartDate" id="eventStartDate" size="1">
    <option value=" " selected="selected"> </option>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
    <option value="10">10</option>
    <option value="11">11</option>
    <option value="12">12</option>
    <option value="13">13</option>
    <option value="14">14</option>
    <option value="15">15</option>
    <option value="16">16</option>
    <option value="17">17</option>
    <option value="18">18</option>
    <option value="19">19</option>
    <option value="20">20</option>
    <option value="21">21</option>
    <option value="22">22</option>
    <option value="23">23</option>
    <option value="24">24</option>
    <option value="25">25</option>
    <option value="26">26</option>
    <option value="27">27</option>
    <option value="28">28</option>
    <option value="29">29</option>
    <option value="30">30</option>
    <option value="31">31</option>
    </select>
    </fieldset>
  </form>
<script type="text/javascript">
  function daysInMonth(month,year)
  {
    var dd = new Date(year, month, 0);
    return dd.getDate();
  }
  function setDayDrop(dyear, dmonth, dday)
  {
    var year = dyear.options[dyear.selectedIndex].value;
    var month = dmonth.options[dmonth.selectedIndex].value;
    var day = dday.options[dday.selectedIndex].value;
    if (day == ' ')
    {
    var days = (year == ' ' || month == ' ') ? 31 : daysInMonth(month,year);
    dday.options.length = 0;
    dday.options[dday.options.length] = new Option(' ',' ');
    for (var i = 1; i <= days; i++)
      dday.options[dday.options.length] = new Option(i,i);
    }
  }

  function setDay()
  {
    var year = document.getElementById('eventYear');
    var month = document.getElementById('eventMonth');
    var day = document.getElementById('eventStartDate');
    setDayDrop(year,month,day);
  }
  document.getElementById('eventYear').onchange = setDay;
  document.getElementById('eventMonth').onchange = setDay;
</script>

  </body>
</html>

Doctype

First up, this transitional doctype is badly out of date.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

That was used 12 to 13 years ago when HTML transitioned in 1999 from HTML3 to HTML4, allowing you to use HTML3 on your page while you transition and test HTML4 techniques.

These days you can just use the following, which includes support for HTML5 too.

<!DOCTYPE HTML>

Form name

With the form, in the following line the name serves a useless purpose.

<form name="myform">

In fact, it’s worse than useless, it’s somewhat harmful because it gives the mistaken impression that it’s somehow related to the named form fields.
What the name=“myform” is actually doing is the same as the ancient style of using <a name=“myform”>, which has long been surpassed by using a unique identifier for it instead.

So use a unique identifier on the form instead. That will also allow CSS and scripting to more easily work with the form too.
Also, use an identifier that’s more descriptive than just “myform”. Right now there’s no information about what the form is supposed to do. Giving it an identifier of “addNewEvent” helps to improve on that.

<form id="addNewEvent">

Labels

The for attribute is now no longer needed when using labels in todays web browsers.

<label for="year">Year: </label>
<select name="eventYear" id="eventYear" size="1">
    ...
</select>

Another problem is that the for attribute is broken, because the for=“year” doesn’t have a matching id=“year” with which to relate it to.

So instead of using explicit association which is easily broken, you can instead use implicit association where you wrap the label element around the field that it’s for, which also means that you don’t need to clutter your HTML code with for attributes and unique identifiers throughout your form fields.


<label>Year: 
    <select name="eventYear" size="1">
        ...
    </select>
</label>

Size

The size attribute is misleading because it is only useful when you are also using the multiple attribute.
where the size indicates how many options are shown at the same time on the screen.

<select name="eventYear" size="1">
    ...
</select>

The size attribute only leads to confusion if you use it without the multiple attribute, so it should be removed.


<select name="eventYear">
    ...
</select>

Empty option

It’s also a common convention to use an empty option attribute, instead of one with a space.

<option value=" " selected="selected"> </option>

One of the reasons for this is that when you use scripting to get the value, " " is considered to be truthy, whereas “” is considered to be falsy.
So when using “” you can use if (year) instead of if (year === ’ ')


<option value="" selected="selected"></option>

So that’s the HTML code all tidied up. Let’s now move on to learning about the scripting.

Form identifier

Now that the form has an identifier, that can be made good use of in the scripting. Not only to get the form, but to work with the named elements within the form itself too.


var form = document.getElementById('addNewEvent');
form.elements.eventYear.onchange = setDay;
form.elements.eventMonth.onchange = setDay;

Auto days when selected

To fix the problem where days are only adjusted if there’s nothing selected, let’s look at the setDayDrop function from before.

function setDayDrop(dyear, dmonth, dday) {
    var year = dyear.options[dyear.selectedIndex].value;
    var month = dmonth.options[dmonth.selectedIndex].value;
    var day = dday.options[dday.selectedIndex].value;
    if (day == '') {
        var days = (year == '' || month == '') ? 31 : daysInMonth(month, year);
        dday.options.length = 0;
        dday.options[dday.options.length] = new Option('', '');
        for (var i = 1; i <= days; i++) {
            dday.options[dday.options.length] = new Option(i, i);
        }
    }
}

There are many minor issues with that code. I invite you to jslint.com to learn about those issues.

The major issue that’s getting in your way though is the if condition. Get rid of that, and get rid of the year or month condition too. That can be applied within the daysInMonth function instead.

In terms of setting the days, you only now need to decide what to do is 31 is selected and someone choose February. I suggest that you set the day to the smaller of the available and currently selected day.


dday.selectedIndex = Math.min(day, days);

That leaves the setDayDrop function looking like this:


function setDayDrop(dyear, dmonth, dday) {
    var year = dyear.options[dyear.selectedIndex].value;
    var month = dmonth.options[dmonth.selectedIndex].value;
    var day = dday.options[dday.selectedIndex].value;
    var days = daysInMonth(month, year);
    dday.options.length = 0;
    dday.options[dday.options.length] = new Option('', '');
    for (var i = 1; i <= days; i++) { 
        dday.options[dday.options.length] = new Option(i, i);
    }
    dday.selectedIndex = Math.min(day, days);
}

With the daysInMonth function, it’s currently like this:

function daysInMonth(month,year) {
    var dd = new Date(year, month, 0);
    return dd.getDate();
}

That can be made to work regardless of what the month or year is set to, by setting them to a default value if the don’t contain anything.


function daysInMonth(month, year) {
    month = month || 1;
    year = year || 1;
    
    // The month variable uses a value from 0-11, but we are using a value from 1-12
    // From the next month we get the last day of this month by going to day 0
    var dd = new Date(year, month, 0);
    return dd.getDate();
} 

Or perhaps in a way that doesn’t require as many comments:


function daysInMonth(month, year) {
    month = month || 1;
    year = year || 1;

    month = month - 1; // From human month to JavaScript Date month (0 - 11)
    
    var dd = new Date(year, month + 1, 0);
    return dd.getDate();
} 

Paul, thanks for the critique. Always wanting to learn more and since this was an excersize in trying to learn more about Javascript it has been helpful.

I modified my code with your suggestions but still not working. Please let me know what I am not seeing here as I am a very novice with Javascript but want to learn more as I am needing this in things I am now asked to do.

Here is my NEW form and code:

<!DOCTYPE HTML>
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=windows-1250">
  <meta name="generator" content="PSPad editor, www.pspad.com">
  <title></title>
  
  
  </head>
  <body>
 <form id="addNewEvent">
 <fieldset>
    <legend>Date Selection</legend>
    <label>Year: </label>
      <select name="eventYear">
        <option value="" selected="selected"> </option>
        <option value="2000">2000</option>
        <option value="2001">2001</option>
        <option value="2002">2002</option>
        </select>
      <label>Month: </label>
        <select name="eventMonth" id="eventMonth" size="1">
        <option value="" selected="selected"> </option>
        <option value="1">January</option>
        <option value="2">February</option>
        <option value="3">March</option>
        <option value="4">April</option>
        <option value="5">May</option>
        <option value="6">June</option>
        <option value="7">July</option>
        <option value="8">August</option>
        <option value="9">September</option>
        <option value="10">October</option>
        <option value="11">November</option>
        <option value="12">December</option>
         
      </select>
     
    <label>Day: </label>
    <select id="eventStartDate">
      <option value="" selected="selected"> </option>
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
      <option value="4">4</option>
      <option value="5">5</option>
      <option value="6">6</option>
      <option value="7">7</option>
      <option value="8">8</option>
      <option value="9">9</option>
      <option value="10">10</option>
      <option value="11">11</option>
      <option value="12">12</option>
      <option value="13">13</option>
      <option value="14">14</option>
      <option value="15">15</option>
      <option value="16">16</option>
      <option value="17">17</option>
      <option value="18">18</option>
      <option value="19">19</option>
      <option value="20">20</option>
      <option value="21">21</option>
      <option value="22">22</option>
      <option value="23">23</option>
      <option value="24">24</option>
      <option value="25">25</option>
      <option value="26">26</option>
      <option value="27">27</option>
      <option value="28">28</option>
      <option value="29">29</option>
      <option value="30">30</option>
      <option value="31">31</option>
    </select>
    </fieldset>
  </form> 
  
<script type="text/javascript"> 

function daysInMonth(month, year) {
    month = month || 1;
    year = year || 1;
 
    // The month variable uses a value from 0-11, but we are using a value from 1-12
    // From the next month we get the last day of this month by going to day 0
    var dd = new Date(year, month, 0);
    return dd.getDate();
}
function setDayDrop(dyear, dmonth, dday) {
  var year = dyear.options[dyear.selectedIndex].value;
  var month = dmonth.options[dmonth.selectedIndex].value;
  var day = dday.options[dday.selectedIndex].value;
  var days = daysInMonth(month, year);

  dday.options.length = 0;
  dday.options[dday.options.length] = new Option('', '');
  for (var i = 1; i <= days; i++) { 
      dday.options[dday.options.length] = new Option(i, i);
  }
  dday.selectedIndex = Math.min(day, days);
}
 
function setDay() 
{
  var year = document.getElementById('eventYear');
  var month = document.getElementById('eventMonth');
  var day = document.getElementById('eventStartDate');
  
  setDayDrop(year,month,day);
}
var form = document.getElementById('addNewEvent');
form.elements.eventYear.onchange = setDay;
form.elements.eventMonth.onchange = setDay;
 /* document.getElementById('eventYear').onchange = setDay;
  document.getElementById('eventMonth').onchange = setDay; */
</script>

  </body>
</html>

The final piece is to make good use of the onchange event.

When scripting is used to attach an event to an element, triggering that event causes the this keyword to refer to the element that triggered that event.
That means that you can use this.form from within the setDay function to easily gain a reference to the form, and from there to any of the named form elements via the elements collection.


function setDay() {
    var form = this.form,
        year = form.elements.eventYear,
        month = form.elements.eventMonth,
        day = form.elements.eventStartDate;
  
    setDayDrop(year, month, day);
}

Paul,

Thanks for the explanation but when I replaced the setDay routine with your latest post, it still does not work.

What am I doing wrong - I know it is me???

E

Do you have a test page that demonstrates the problem? Because everything looks fine from here.