0

I'm trying to build a calculator that calculates various dates based on a user's input. The user selects the date of when a document was issued and then 2 things need to happen:

  1. The expiry date is calculated by adding X number of days to the date issued and displayed to the user
  2. The recommended application date is calculated based on the expiry date minus X number of days

The first one I got working (not in a very elegant way but it works for this part) The second part (recommended application date), I can't get to work. I've done a lot of research and can't find a solution. (I'm a beginner)

I'm thinking maybe "onchange" is perhaps not the right event but I can't find information on another event that applies to this situation. Or is it an issue with how the functions are structured? Or something else completely? I have also tried moment.js and a few other libraries but what I have in the codepen below is what have gotten me the furthest thus far.

The codepen with everything is here: https://codepen.io/locheia1985/pen/XWBMGXZ

And here is the code:

function setSecondDate() {
  var days = document.getElementById('select').value;
  var date = new Date(document.getElementById('date1').value);
  date.setDate(date.getDate() + parseInt(days));
  document.getElementById('date2').valueAsDate = date;
}

function setThirdDate() {
  var days3 = document.getElementById('select2').value;
  var date2 = new Date(document.getElementById('date2').value);
  date2.setDate(date2.getDate() - parseInt(days3));
  document.getElementById('date3').valueAsDate = date3;

  console.log(date2);
}
body
 {
  font-family:sans-serif;
  background: #dde1e7;
  min-height: 100vh;
}
.center,.content
{
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
}
.click-me
{
  display:block;
  width:160px;
  height:50px;
  background: #dde1e7;
  box-shadow: -3px -3px 7px #fffdfdcc,3px 3px 5px rgba(94, 104, 121, 0.342);
  text-align:center;
  font-size:22px;
  line-height:50px;
  cursor:pointer;
}
#click
{
  display:none;
}
.click-me:hover
{
  color: #0a5878;
}
.content
{
  visibility:hidden;  
  width: 330px;
  height: auto;
  background: #dde1e7;
  padding: 30px 35px 40px;
  box-sizing: border-box;
  border-radius: 5px;
}
#temp
{
  position:absolute;
  right:10px;
  top:20px;
  font-size:25px;
  background: #dde1e7;
  padding: 3px;
  padding-left: 11px;
  padding-right: 11px;
  border-radius: 50%;
  cursor:pointer;
}
#click:checked~.content
{
  opacity:1;
  visibility:visible;
}
.text
{
  font-size: 30px;
  color: #000000;
  font-weight: 600;
  text-align: center;
  letter-spacing: 2px;
}
form
{
  margin-top: 40px;
}
label
{
  display: block;
  margin-top: 30px;
  font-size: 16px;
  font-weight: 500;
}
input
{
  display: block;
  height: 43px;
  width: 100%;
  background-color: rgba(255,255,255,0.07);
  border-radius: 3px;
  border: none;

  margin-top: 8px;

  font-size: 14px;
  font-weight: 300;
}
::placeholder
{
  color: #4b4e4d;
  padding-left: 10px;
}
button
{
  width: 100%;
  margin-top: 35px;
  padding: 12px;
  background: #dde1e7;
  border: none;
  font-size: 17px;
  font-weight: 600;
  margin-bottom: 32px;
}
<div class="center">
  <input type="checkbox" id="click">
  <label for="click" class="click-me">Passport</label>
  <div class="content">

    <label for="click" id="temp">x</label>

    <form>

      <select style="display: none" id="select">
        <option value="150">150 days</option>
      </select>

      <select style="display: none" id="select2">
        <option value="50">150 days</option>
      </select>

      <label for="date1">Date Issued:</label>
      <input type="date" onchange="setSecondDate();" id="date1">

      <label for="date2">Expiry Date:</label>
      <input type="date" onchange="setThirdDate();" id="date2">

      <label for="date3">Recommended Application Date:</label>
      <input type="date" id="date3">



    </form>
  </div>
</div>

Any help would be appreciated

pilchard
  • 12,414
  • 5
  • 11
  • 23
  • 1
    Adding days to a `Date` has been asked and answered multiple times since the beginning of this website; One of the questions was the appropriately named [How to add days to Date?](https://stackoverflow.com/q/563406/215552) Subtracting days is simply adding a negative number of days. Event handlers are triggered only by user actions, or by specific JavaScript code. So, at the end of `setSecondDate`, if you want to run `setThirdDate`, just call that function: i.e., `setThirdDate();` – Heretic Monkey Jan 10 '23 at 22:52
  • Note that `new Date(document.getElementById('date1').value);` could be replaced by `document.getElementById('date1').valueAsDate;` to match how you are setting the value; otherwise you may end up parsing the string value of the date, which could be risky. – Heretic Monkey Jan 10 '23 at 22:55
  • ++Everything Heretic Monkey says, plus I would have only one function `setFutureDates` that calculates and sets the values for both fields. Also you don't need the expiry date to calculate the recommended application date — expiry = selectedDate + X and recommended = selectedDate + (X - Y) – Stephen P Jan 10 '23 at 23:01
  • 1
    Note that in `new Date(document.getElementById('date1').value);` the value is parsed as UTC, whereas the *setDate* functions are using local. That means that dates may appear to be ±1 day depending on the user's regional settings. Far better to explicitly parse the value as UTC or local manually (a library can help) and keep all functions in the same timezone/offset context—local or UTC. – RobG Jan 11 '23 at 01:53
  • 1
    @HereticMonkey—using *valueAsDate* has the same issue as the OP code: the value is parsed by the built–in parser as UTC (because it's formatted as YYYY-MM-DD), whereas it's likely the OP expects it to be parsed as local. – RobG Jan 11 '23 at 01:54

1 Answers1

0

I think additional to the typo the onChange event only fires when a user interacts with the component - not when you programmatically set it. To get that setThirdDate there are some options but the clearest way is to just do the work you would have wanted done on the second event at the same time.

A refactor of this that fixes the typo and sets both dates is:

to change:

 <input type="date" onchange="setSecondDate();" id="date1">

to

 <input type="date" onchange="handleDateIssuedChanged();" id="date1">

and add (this also renames the setters to be clearer):

function handleDateIssuedChanged() {
  setExpiryDate();
  setRecommendedDate();
}

Full modified code:

<html>

<head>

<style>

body
 {
  font-family:sans-serif;
  background: #dde1e7;
  min-height: 100vh;
}
.center,.content
{
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
}
.click-me
{
  display:block;
  width:160px;
  height:50px;
  background: #dde1e7;
  box-shadow: -3px -3px 7px #fffdfdcc,3px 3px 5px rgba(94, 104, 121, 0.342);
  text-align:center;
  font-size:22px;
  line-height:50px;
  cursor:pointer;
}
#click
{
  display:none;
}
.click-me:hover
{
  color: #0a5878;
}
.content
{
  visibility:hidden;  
  width: 330px;
  height: auto;
  background: #dde1e7;
  padding: 30px 35px 40px;
  box-sizing: border-box;
  border-radius: 5px;
}
#temp
{
  position:absolute;
  right:10px;
  top:20px;
  font-size:25px;
  background: #dde1e7;
  padding: 3px;
  padding-left: 11px;
  padding-right: 11px;
  border-radius: 50%;
  cursor:pointer;
}
#click:checked~.content
{
  opacity:1;
  visibility:visible;
}
.text
{
  font-size: 30px;
  color: #000000;
  font-weight: 600;
  text-align: center;
  letter-spacing: 2px;
}
form
{
  margin-top: 40px;
}
label
{
  display: block;
  margin-top: 30px;
  font-size: 16px;
  font-weight: 500;
}
input
{
  display: block;
  height: 43px;
  width: 100%;
  background-color: rgba(255,255,255,0.07);
  border-radius: 3px;
  border: none;

  margin-top: 8px;

  font-size: 14px;
  font-weight: 300;
}
::placeholder
{
  color: #4b4e4d;
  padding-left: 10px;
}
button
{
  width: 100%;
  margin-top: 35px;
  padding: 12px;
  background: #dde1e7;
  border: none;
  font-size: 17px;
  font-weight: 600;
  margin-bottom: 32px;
}

</style>

<script>



function setExpiryDate() {
    var days = document.getElementById("select").value;
  var date = new Date(document.getElementById("date1").value);
  date.setDate(date.getDate() + parseInt(days));
  document.getElementById("date2").valueAsDate = date;
}


function setRecommendedDate() {
    var days3 = document.getElementById("select2").value;
  var date2 = new Date(document.getElementById("date2").value);
  date2.setDate(date2.getDate() - parseInt(days3));
  document.getElementById("date3").valueAsDate = date2;
  
  console.log(date2);
}

function handleDateIssuedChanged() {
  setExpiryDate();
  setRecommendedDate();
}

</script>



</head>

<body>
  <div class="center">
    <input type="checkbox" id="click">
    <label for="click" class="click-me">Passport</label>
    <div class="content">
      
        <label for="click" id="temp">x</label>

    <form>
    
    <select style="display: none" id="select">
         <option value="150">150 days</option>
       </select>
       
       <select style="display: none" id="select2">
         <option value="50">150 days</option>
       </select>

        <label for="date1">Date Issued:</label>
Expiry Date: Recommended Application Date:

(The other and likely not-what-you-want pattern is to trigger the second event https://stackoverflow.com/a/8789494/7632432 )

Cadmium
  • 623
  • 3
  • 9
  • I would change your "(possibly clearer)" to "the other _more direct and proper_ way is to..." because, while explicitly sending the event can be done, it is The Wrong Thing™ to do. `date3` is not changing because `date2` changed, `date2` and `date3` are both changing because `date1` changed, so the one-and-only event involved should be date1.onchange – Stephen P Jan 11 '23 at 01:22
  • Agreed, can update it. – Cadmium Jan 11 '23 at 01:28
  • Thanks so much everyone that replied. I'll try the solutions out and see whether I can get it to work :) – Nicole Louw Jan 13 '23 at 02:24