【问题标题】:onchange + validation + enter key weird behaviouronchange + 验证 + 输入键奇怪的行为
【发布时间】:2017-03-08 13:56:36
【问题描述】:

我有一个带有日期输入字段的表单。应验证日期:仅允许从今天开始的日期 + 最多 3 年。如果日期有效,将显示一个模式,否则将显示带有错误消息的警报。

假设 somone 将日期更改为 26.10.2099:

如果有人离开输入字段(通过用鼠标单击其他地方),一切都会按预期工作:

  • 出现错误信息
  • 日期自动改回来
  • 没有模态显示

但是,如果按下 Enter 而不是离开输入字段,则会发生以下情况:

  • 没有错误信息显示
  • 日期自动改回来
  • 模态出现

我的想法是,将日期改回初始值会导致再次触发onchange事件,然后日期有效并显示模态。但第一种情况并非如此。在第二种情况下,事件有时会触发两次,但并非总是如此。

您可以在这里试用: https://jsfiddle.net/6x9n53fx/3/

或者阅读下面的代码:

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;
}

编辑:

这是我采用的最终解决方案:

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');
    }
  })

【问题讨论】:

  • 你从哪里调用 validateDatum() ?
  • datumfeld.change() 是调用 validateDatum() 的地方
  • 当你离开焦点时onchange会触发,所以你也需要使用onkeypress
  • @user2415266 如果它是你要找的,请检查我的答案吗?

标签: javascript jquery validation date


【解决方案1】:

您的代码的问题是当您按 Enter 您的值时!= 当您单击其他位置时。调试的时候可以看到dateParts

当您输入示例:28.10.2099 并点击离开时,dateParts 是 ["28", "10", "2099"] 但是当输入示例时:28.10.2099 并按 ENTER dateParts is ["28", "10", "2019"] 这里 datepicker 验证输入字段并自动将日期设置为 28.10.2019,因为您将 maxDate 设置为3y.

所以我的解决方案是绑定 keydown 并在 datepicker 自动更改值之前绕过它。

这是一个带有工作代码的链接:https://jsfiddle.net/95qbs86x/2/

更新:添加代码

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;
}

注意:某些代码可能会被删除,例如:e.which = 9; e.key = 'Tab'; return false;

但是要应用与点击离开相同的效果,应该添加这行代码:

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

一些代码取自这个答案:Disable enter key in JQuery ui datepicker

【讨论】:

  • 感谢您的回答。它有很大帮助,虽然我没有创建按键事件,而是日期选择器的内置隐藏方法。此外,我只是触发更改事件,而不是重复验证代码。我将我的最终解决方案编辑到问题中。
【解决方案2】:

你需要使用 preventDefault() 来处理这个问题,下面给出的工作演示......

            $(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.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/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>

说明:

你说得对,把日期改回它的初始值会导致再次触发 onchange 事件,那时你需要处理这个问题,所以要处理它有一个选项 preventDefault(),我附上了工作您需要的示例和参考,您可以查看这些链接.. Reference_1 , Reference_2 ,Reference_3...

【讨论】:

  • 对我不起作用。点击输入框,手动将年份改为2099,然后回车->出现模态。在不按 Enter 的情况下执行相同操作,而是单击外部某处 -> 弹出错误消息。 (第二种行为也是按下 Enter 键时应该发生的情况)
【解决方案3】:

只要您按下“Enter”,日期选择器就会触发更改事件,并在日期不适合日期选择器本身的minDatemaxDate 时立即更改日期。

所以我为keyup 事件更新了您的事件处理程序,并在您使用键盘输入任何内容后立即销毁日期选择器(=> 表示您不想在日期选择器的帮助下输入日期)然后我重置只要你按ENTER

这是最终的 JavaScript 代码:

    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;
    }

不知何故,.datepicker("destroy") 在 JSFiddle 上不起作用,但它确实存在:http://api.jqueryui.com/datepicker/#method-destroy

e.stopImmediatePropagation(); 仅在检查 ENTER 键时也有帮助,但是它只能每隔一次工作一次。有时 keyup 更快,有时 datepicker 更快。所以这就是我想销毁日期选择器的原因。

【讨论】:

    【解决方案4】:

    如果 change 事件被动态触发,您将在可以使用的事件中获得 originalEvent 属性

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

    而不是 preventDefault 虽然这两种方式在这里都是一样的小提琴https://jsfiddle.net/6x9n53fx/4/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-10
      • 1970-01-01
      • 2010-12-17
      • 1970-01-01
      • 2011-01-01
      • 1970-01-01
      • 2020-09-11
      • 1970-01-01
      相关资源
      最近更新 更多