最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - onchange + validation + enter key weird behaviour - Stack Overflow

programmeradmin3浏览0评论

I have a form with an input field for dates. The dates should be validated: only dates from today + maximum 3 years are allowed. If the date is valid, a modal will show, else there will be an alert with an error message.

Assuming somone changes the date to 26.10.2099:

Everything works as expected, if one leaves the input field (by clicking somewhere else with the mouse):

  • error message appears
  • date gets automatically changed back
  • no modal shows

BUT if one presses Enter instead of just leaving the input field, the following happens:

  • no error message shows
  • date gets automatically changed back
  • modal shows up

My thought is that changing the date back to its initial value causees the onchange event to be triggered again, and then the date is valid and the modal will show. But this is not the case for the first scenario. In the second scenario the event triggers twice SOMETIMES but not always.

You can tryout yourself here: /

Or read the code below:

HTML:

<div class="container">
    <form method="post" action="" enctype="multipart/form-data">
        <div class="row">
            <div class="col-sm-6">
                <div class="form-group">
                    <label for="review_date">Valid until *</label>
                    <input id="review_date" type="text" name="review_date" value="26.10.2016" class="form-control">
                    <p>Please set a validation date. (max 3 years)</p>
                </div>
            </div>
        </div>
    </form>
</div>
<!-- Modal -->
<div class="modal fade" id="info_modal" tabindex="-1" role="dialog" aria-labelledby="modal_title">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title" id="modal_title">Dialog</h4>
            </div>
            <div class="modal-body">
                <div class="form-group mbn">
                    <p class="mb10">Date successfully changed.</p>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-primary" data-dismiss="modal">Ok</button>
            </div>
        </div>
    </div>
</div>

Javascript:

var datumfeld = $("#review_date");
var initialdatum = datumfeld.val();
var i = 0;
datumfeld.datepicker({
  minDate: 0,
  maxDate: "3y",
  dateFormat: 'dd.mm.yy'
});
datumfeld.change(function() {
i++;
  console.log('onchange triggered: ' + i);
  if (validateDatum()) {
    $("#info_modal").modal({
      backdrop: "static",
      keyboard: false
    });
  }
});

function validateDatum() {
  var dateParts = datumfeld.val().split(".");
  var review_date = new Date(dateParts[2], (dateParts[1] - 1), (parseInt(dateParts[0]) + 1));
  if (((new Date(review_date - (new Date()))).getFullYear() - 1970) > 2) {
    alert("Error: The date must not exceed 3 years from now.");
    datumfeld.val(initialdatum);
    return false;
  }
  if ((review_date - (new Date)) < 0) {
    alert("Error: The date must not be in the past.");
    datumfeld.val(initialdatum);
    return false;
  }
  return true;
}

EDIT:

Here is the final solution I went with:

datumfeld.on('keydown', function(e) {
    // when keydown is ENTER (13)
    if (e.keyCode == 13) {
      // Hide the datepicker
      $(this).datepicker('hide');
      // Trigger a change event to trigger the validation
      $(this).trigger('change');
    }
  })

I have a form with an input field for dates. The dates should be validated: only dates from today + maximum 3 years are allowed. If the date is valid, a modal will show, else there will be an alert with an error message.

Assuming somone changes the date to 26.10.2099:

Everything works as expected, if one leaves the input field (by clicking somewhere else with the mouse):

  • error message appears
  • date gets automatically changed back
  • no modal shows

BUT if one presses Enter instead of just leaving the input field, the following happens:

  • no error message shows
  • date gets automatically changed back
  • modal shows up

My thought is that changing the date back to its initial value causees the onchange event to be triggered again, and then the date is valid and the modal will show. But this is not the case for the first scenario. In the second scenario the event triggers twice SOMETIMES but not always.

You can tryout yourself here: https://jsfiddle/6x9n53fx/3/

Or read the code below:

HTML:

<div class="container">
    <form method="post" action="" enctype="multipart/form-data">
        <div class="row">
            <div class="col-sm-6">
                <div class="form-group">
                    <label for="review_date">Valid until *</label>
                    <input id="review_date" type="text" name="review_date" value="26.10.2016" class="form-control">
                    <p>Please set a validation date. (max 3 years)</p>
                </div>
            </div>
        </div>
    </form>
</div>
<!-- Modal -->
<div class="modal fade" id="info_modal" tabindex="-1" role="dialog" aria-labelledby="modal_title">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title" id="modal_title">Dialog</h4>
            </div>
            <div class="modal-body">
                <div class="form-group mbn">
                    <p class="mb10">Date successfully changed.</p>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-primary" data-dismiss="modal">Ok</button>
            </div>
        </div>
    </div>
</div>

Javascript:

var datumfeld = $("#review_date");
var initialdatum = datumfeld.val();
var i = 0;
datumfeld.datepicker({
  minDate: 0,
  maxDate: "3y",
  dateFormat: 'dd.mm.yy'
});
datumfeld.change(function() {
i++;
  console.log('onchange triggered: ' + i);
  if (validateDatum()) {
    $("#info_modal").modal({
      backdrop: "static",
      keyboard: false
    });
  }
});

function validateDatum() {
  var dateParts = datumfeld.val().split(".");
  var review_date = new Date(dateParts[2], (dateParts[1] - 1), (parseInt(dateParts[0]) + 1));
  if (((new Date(review_date - (new Date()))).getFullYear() - 1970) > 2) {
    alert("Error: The date must not exceed 3 years from now.");
    datumfeld.val(initialdatum);
    return false;
  }
  if ((review_date - (new Date)) < 0) {
    alert("Error: The date must not be in the past.");
    datumfeld.val(initialdatum);
    return false;
  }
  return true;
}

EDIT:

Here is the final solution I went with:

datumfeld.on('keydown', function(e) {
    // when keydown is ENTER (13)
    if (e.keyCode == 13) {
      // Hide the datepicker
      $(this).datepicker('hide');
      // Trigger a change event to trigger the validation
      $(this).trigger('change');
    }
  })
Share Improve this question edited Nov 4, 2016 at 8:46 Danmoreng asked Oct 26, 2016 at 9:14 DanmorengDanmoreng 2,3671 gold badge20 silver badges32 bronze badges 4
  • from where u r calling validateDatum() ? – user1844933 Commented Oct 26, 2016 at 9:21
  • datumfeld.change() is where validateDatum() is called – Danmoreng Commented Oct 26, 2016 at 9:23
  • The onchange will fire when you leave the focus from it, so you need to use onkeypress too – user1844933 Commented Oct 26, 2016 at 9:29
  • @user2415266 Could you please check my answer if it's what you looking for? – nstung Commented Oct 28, 2016 at 13:53
Add a ment  | 

4 Answers 4

Reset to default 4 +100

The problem from your code is when you press Enter your value != when you click else where. You can see it when you debug dateParts

When you enter example: 28.10.2099 and click away dateParts is ["28", "10", "2099"] But when enter example: 28.10.2099 and press ENTER dateParts is ["28", "10", "2019"] Here datepicker is validate the input field and set automaticly date to 28.10.2019 because you set maxDate to 3y.

So my solution is to bind keydown and bypass datepicker before it change the value automaticly.

Here is a link with working code: https://jsfiddle/95qbs86x/2/

Update: added code

var datumfeld = $("#review_date");
var initialdatum = datumfeld.val();
var i = 0;

// bind keydown event
datumfeld.on('keydown', function(e) {
  // when keydown is ENTER (13)
  if (e.keyCode == 13) {

    // trigger tab to hide datepicker
    var e = $.Event("keydown");
    e.which = 9; //tab 
    e.keyCode = 9;
    e.key = 'Tab';
    $(this).trigger(e);

    return false;
  }
}).datepicker({
  minDate: 0,
  maxDate: "3y",
  dateFormat: 'dd.mm.yy',
  onClose: function(){
    this.blur();
  }
}).on('change', function(e) {
  i++;
  console.log('onchange triggered: ' + i);
  if (validateDatum()) {
    $("#info_modal").modal({
      backdrop: "static",
      keyboard: false
    });
  }
});


function validateDatum() {
  var dateParts = datumfeld.val().split(".");
  console.log(dateParts);
  var review_date = new Date(dateParts[2], (dateParts[1] - 1), (parseInt(dateParts[0]) + 1));

  if (((new Date(review_date - (new Date()))).getFullYear() - 1970) > 2) {
    alert("Error: The date must not exceed 3 years from now.");
    datumfeld.val(initialdatum);
    return false;
  }
  if ((review_date - (new Date)) < 0) {
    alert("Error: The date must not be in the past.");
    datumfeld.val(initialdatum);
    return false;
  }
  return true;
}

Notice: Some code could be remove like: e.which = 9; e.key = 'Tab'; return false;

However to applying the same effect as click away, this line of code should be added:

onClose: function(){
  this.blur();
}

Some code was taken from this answere: Disable enter key in JQuery ui datepicker

You need to use preventDefault() to handle this issue, working demo given below...

            $(document).ready(function() {
                var datumfeld = $("#review_date");
                var initialdatum = datumfeld.val();
                var i = 0;
                datumfeld.datepicker({
                  minDate: 0,
                  maxDate: "3y",
                  dateFormat: 'dd.mm.yy'
                });
                datumfeld.change(function(e) {
                i++;
                  console.log('onchange triggered: ' + i);
                  if (validateDatum()) {
                    e.preventDefault();
                    $("#info_modal").modal({
                      backdrop: "static",
                      keyboard: false
                    });
                  }
                });

                function validateDatum() {
                  var dateParts = datumfeld.val().split(".");
                  var review_date = new Date(dateParts[2], (dateParts[1] - 1), (parseInt(dateParts[0]) + 1));
                  if (((new Date(review_date - (new Date()))).getFullYear() - 1970) > 2) {
                    alert("Error: The date must not exceed 3 years from now.");
                    datumfeld.val(initialdatum);
                    return false;
                  }
                  else if ((review_date - (new Date)) < 0) {
                    alert("Error: The date must not be in the past.");
                    datumfeld.val(initialdatum);
                    return false;
                  } else {
                    return true;
                  }
                }

                function handleSubmit() {
                    console.log("form submitted....");
                    return false;
                }
            });
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis./ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://code.jquery./ui/1.12.0/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="https://code.jquery./ui/1.12.0/jquery-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn./bootstrap/3.0.0/css/bootstrap.min.css">
<script type="text/javascript" src="https://ajax.googleapis./ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://code.jquery./ui/1.12.0/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="https://code.jquery./ui/1.12.0/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://maxcdn.bootstrapcdn./bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn./bootstrap/3.3.7/css/bootstrap.min.css">
<div class="container">
            <form method="post" action="" enctype="multipart/form-data" onsubmit="return handleSubmit();">
                <div class="row">
                    <div class="col-sm-6">
                        <div class="form-group">
                            <label for="review_date">Valid until *</label>
                            <input id="review_date" type="text" name="review_date" value="26.10.2016" class="form-control">
                            <p>Please set a validation date. (max 3 years)</p>
                        </div>
                    </div>
                </div>
            </form>
        </div>
        <!-- Modal -->
        <div class="modal fade" id="info_modal" data-controls-modal="info_modal" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="modal_title" data-keyboard="false">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                        <h4 class="modal-title" id="modal_title">Dialog</h4>
                    </div>
                    <div class="modal-body">
                        <div class="form-group mbn">
                            <p class="mb10">Date successfully changed.</p>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-primary" data-dismiss="modal">Ok</button>
                    </div>
                </div>
            </div>
        </div>

Explaination :

You are right about changing the date back to its initial value causes the onchange event to be triggered again, at that time you need to handle this issue, so to handle it there is an option preventDefault(), I have attached working example which you require for this and for reference you can checkout these links.. Reference_1 , Reference_2 ,Reference_3...

The datepicker triggers a change event as soon as you press "Enter" and changes the date back as soon as the date does not fit in minDate or maxDate of the datepicker itself.

So I updated your event handler for keyup events and destroy the datepicker as soon as you type anything with your keyboard (=> means you don't want to enter the date with the help of the datepicker) and I reset it as soon as you press ENTER.

This is the final JavaScript code:

    var datumfeld = $("#review_date");
    var initialdatum = datumfeld.val();
    var i = 0;

    datumfeld.datepicker({
      minDate: 0,
      maxDate: "3y",
      dateFormat: 'dd.mm.yy'
    });

    datumfeld.on('keyup change', function(e) {
    e.preventDefault();
    if(e.key && e.key === "Enter") {
        e.stopImmediatePropagation();
      datumfeld.datepicker({
        minDate: 0,
        maxDate: "3y",
        dateFormat: 'dd.mm.yy'
      });
    } else if(e.key) {
        datumfeld.datepicker("destroy");
    }
    if(!e.key || e.key === "Enter") {
      i++;
      console.log('onchange triggered: ' + i);
      if (validateDatum()) {
        $("#info_modal").modal({
          backdrop: "static",
          keyboard: false
        });
      }
    }
    });

    function validateDatum() {
      var dateParts = datumfeld.val().split(".");
      var review_date = new Date(dateParts[2], (dateParts[1] - 1), (parseInt(dateParts[0]) + 1));
      if (((new Date(review_date - (new Date()))).getFullYear() - 1970) > 2) {
        alert("Error: The date must not exceed 3 years from now.");
        datumfeld.val(initialdatum);
        return false;
      }
      if ((review_date - (new Date)) < 0) {
        alert("Error: The date must not be in the past.");
        datumfeld.val(initialdatum);
        return false;
      }
      return true;
    }

Somehow the .datepicker("destroy") does not work on JSFiddle, but it definitely exists: http://api.jqueryui./datepicker/#method-destroy

The e.stopImmediatePropagation(); helped as well when only checking for the ENTER keyup, however it only works every second time then. Sometimes the keyup is faster, sometimes datepicker is. So that's why I wanted to destroy the datepicker.

if change event is fired dynamically you will get originalEvent property in event you can use

if(typeof e.originalEvent!='undefined') return;

instead preventDefault although both ways are same fiddle here https://jsfiddle/6x9n53fx/4/

发布评论

评论列表(0)

  1. 暂无评论