我决定使用两个事件侦听器来一次验证一个动态表行,其中我重置了两个事件侦听器上的验证;
A)上输入[类型=数]点击,关于日期选择器输入事件的内容KEYUP
&
B),和KEYUP
我使用内联复位验证规则即:
$(".element").rules('add',{required: true});
$(".element").rules('remove',"required");
在飞行中。
我还在创建新行后动态创建新的$("#id").datepicker()
对象。我也用$(this).clone()
在处理原始行时创建动态行。我从来没有想过$.clone() API会有用,但我终于找到了一个有用的例子。
从我的理解克隆的对象不绑定到我认为依赖于API的原始行的事件侦听器。我实际上已经证明我用datepicker的嵌套输入文本克隆了一行。
我最终选择使用jQuery.remove()
函数删除输入文本框,然后重新创建它并将其绑定到datepicker API,然后为其提供一个新ID以使其正确工作。
现在的问题是changeDate事件没有绑定到新的datepicker对象,我记得我试图删除id并添加一个新的id,但元素仍然绑定到从它克隆的原始元素的事件。
例如,日历在原始元素上弹出或日历本身不显示,具体取决于您如何选择重新初始化克隆行中的嵌套日期选择器。 (请参阅follow-up question I posted regarding the onchangeDate issue)。
也看出来哪个日期选择器API你引用因为有这个功能,如Bootstrap-datepicker和jQuery-datepicker这矿是从美创,距离jQuery的日期选择器API许多API
我将展示样品如果有人遇到与我相同的问题,也可以使用这些代码。
注意:我没有在所有浏览器中测试2017年2月23日,所以我会在测试后更新。
这里是$.rules(), $.datepicker(), and $.clone()
API的一些示例代码段:
这是我如何验证和处理表中的行,而无需使用$("form").submit()
技术。
注:我的形式在我的桌子包裹所以我有一个验证的声明,但我不使用form.submit()
技术,而我处理的,动态验证规则我重置和每个事件的触发设置。
function TableEventHandlers(){
var form = $("#ITEMS");
var FormError = $('.item-failure',form);
var FormSuccess = $('.item-success',form);
//used to trigger datepicker on calendar selection
$(".hasDatepicker").on("changeDate",function(e){
e.stopImmediatePropagation();
$(this).trigger("focusout");
//alert("onchange date" + $(this).val() + e.type);
});
//Processes a single qty at a time w/o popout
$("#table_001").on("click keyup",".qty",function (e) {
e.stopImmediatePropagation();
//reset validators
$(".qty").rules('remove','min');
$(".qty").rules('remove','max');
$(".qty").rules('remove','required');
$(".dp").rules('remove','required');
$(".dp").rules('remove','UsaDate');
$(".dp").removeClass("error").tooltip("disable").tooltip("hide");
$(".qty").removeClass("error").tooltip("disable").tooltip("hide");
var row = $(this).closest('tr');
flag = true;
row.find('.dp').rules('add',{required:true,messages:{required:"Must supply a start buy date."}});
row.find('.dp').rules('add',{UsaDate:true,messages:{UsaDate:"Enter date in mm/dd/yyyy format"}});
hasQtys = false;
hadOtherQtys = false;
var actualQty = parseInt($(this).val(), 10);
var dow = $(this).data("dow");
var qty = $();
var num = 0;
var buydate = row.find(".dp");
var delday = "";
var quans = 0;
var Error = false;
//iterate thru tr check if multiple records
row.children("td").each(function(index){
qty = $(this).find(".qty");
if(qty.val() !== undefined){
num = parseInt(qty.val(), 10);
//console.log(isNaN(num)+ "index=" + index);
if(isNaN(num))
num = 0;
if(num > 0){
hasQtys = true;
if(quans > 1)
hadOtherQtys = true;
quans++;
}
//Min max validation process
console.log(num + ">"+ +parseInt(qty.attr("max"),10) + "<"+ parseInt(qty.attr("min"),10) + "not 0 = "+ num);
//qty out of range: min or Max attr or != 0?
if(num != 0 && (num > parseInt(qty.attr("max"),10) || num < parseInt(qty.attr("min"),10)))
{
if(num > parseInt(qty.attr("max"),10)){
qty.rules('add',{max: parseInt(qty.attr('max')), messages: {max: "Quantity must not be greater than " + qty.attr("max")}});
}
else{
qty.rules('add',{min: parseInt(qty.attr('min')), messages: {max: "Quantity must be greater than " + qty.attr("min")}});
}
qty.addClass("error").tooltip("enable").tooltip('show');
$('.item-failure').removeClass("hidden").show().html(qty.data("originalTitle"));
Error = true;
}
else
qty.removeClass("error").tooltip("disable").tooltip("hide");
}//eof undefined qty
});
console.log("has qtys= " + hasQtys + "has hadotherQtys = " + hadOtherQtys);
//.EmptyRow all require qtys when empty row
if(row.find(".itno").hasClass("EmptyRow") && hasQtys === false)
{
row.find(".qty").rules('add',{required: true, messages: {required: "Quantity must be entered when the item request date is new"}});
row.find(".qty").addClass("error").tooltip("enable");
Error = true;
}
else
{ //.EmptyRow has Qtys or .RecordRow
row.find(".qty").rules('remove','required');
//row.find(".qty").removeClass("error").tooltip("disable");
}
console.log("buydate valid = " + buydate.valid() + "buydate.val = " + buydate.val());
//new date is valid
if(buydate.valid() == false || buydate.val() == ""){
$('.item-failure').removeClass("hidden").show().html("You have some errors. See below.");
row.find(".dp").addClass("error").tooltip("enable");
Error = true;
}
if(Error === true)
return true;
else{
//Qtys met requirements of >= max && <= min or 0
buydate.removeClass("error").tooltip("disable");
delday = qty.data("delday");
$('.item-failure').addClass("hidden").hide();
$('.item-success').removeClass("hidden").html("Processing future order request...").show();
ProcessRequest(row,actualQty,delday, buydate.val());
}//eof valid date
});//eof qty event listener
//Iterates thru the entire row when date changed
//NOTE: no popout atm causes erroneous results
$(".dp").on("keyup focusout",function (e) {
e.stopImmediatePropagation();
//reset validators
$(".qty").rules('remove','min');
$(".qty").rules('remove','max');
$(".qty").rules('remove','required');
hasQtys = false;
hadOtherQtys = false;
var row = $(this).closest('tr');
$(this).rules('add',{required:true,messages:{required:"Must supply a start buy date."}});
$(this).rules('add',{UsaDate:true,messages:{UsaDate:"Enter date in mm/dd/yyyy format"}});
var buydate = $(this);
var num = 0;
var dow = '';
var delday = '';
var quans = 0;
flag = true;
var qty = $();
var Error = false;
//console.log("dp triggered" + e.type);
//only check date when manually entered.
if(e.type === "keyup" && ($(this).valid() === false || $(this).val() ===""))
{ console.log(e.type + $(this).valid());
$(this).addClass("error").tooltip("enable").show();
$('item-failure').removeClass("hidden").html("You have some errors. See below.").show();
Error = true;
}
//check for qtys in row before processing
row.children("td").each(function(index){
qty = $(this).find(".qty");
if(qty.val() !== undefined){
num = parseInt(qty.val(), 10);
if(isNaN(num))
num = 0;
if(num > 0){
hasQtys = true;
if(quans > 1)
hadOtherQtys = true;
quans++;
}
//Min max or 0 validation process
console.log(num + ">"+ +parseInt(qty.attr("max"),10) + "<"+ parseInt(qty.attr("min"),10) + "not 0 = "+ num);
console.log(num > parseInt(qty.attr('max'),10));;
//qty out of range: min or Max attr or != 0?
if(num != 0 && (num > parseInt(qty.attr("max"),10) || num < parseInt(qty.attr("min"),10)))
{
if(num > parseInt(qty.attr("max"),10)){
qty.rules('add',{max: parseInt(qty.attr('max')), messages: {max: "Quantity must not be greater than " + qty.attr("max")}});
}
else{
qty.rules('add',{min: parseInt(qty.attr('min')), messages: {max: "Quantity must be greater than " + qty.attr("min")}});
}
qty.addClass("error").tooltip("enable").tooltip('show');
$('.item-failure').removeClass("hidden").show().html(qty.data("originalTitle"));
Error = true;
}
}//eof undefined qty
});
//Empty rows require atleast one qty
if(row.find(".itno").hasClass("EmptyRow") && hasQtys === false){
row.find(".qty").rules('add',{required: true, messages: {required: "Quantity must be entered when the item request date is new"}});
row.find(".qty").addClass("error").tooltip("enable");
Error = true;
}
if(Error === true)
return true;
else{
//Final stage of processing multiple records
row.children("td").each(function(){
qty = $(this).find(".qty");
if(qty.val() !== undefined){
num = parseInt(qty.val(), 10);
if(isNaN(num))
num = 0;
//console.log("buydate.valid() = " +buydate.valid());
//console.log(qty.val() + "<="+ qty.attr("max") +qty.val() + ">="+ qty.attr("min"));
if(buydate.valid() == "1" && buydate.val() !== "" && ((row.find(".itno").hasClass("EmptyRow") && hasQtys === true) || row.find(".itno").hasClass("RecordRow"))){
$('.item-failure').addClass("hidden").hide();
$('.item-success').removeClass("hidden").html("Processing future order requests...").show();
console.log("processing..");
ProcessRequest(row,num,delday);
}
}//eof qty.val undefined
});//eof td children
}//eof error
});//eof event datepicker listener
form.validate({
focusInvalid: false, // do not focus the last invalid input
onkeyup: function(element) { //only allow if 'onkeyup:false' is rule!!
var element_id = $(element).attr('NAME');
if (this.settings.rules[element_id]) {
if (this.settings.rules[element_id].onkeyup !== false) {
$.validator.defaults.onkeyup.apply(this, arguments);
}
}
},
rules: {
//dynamic rules worked better in this instance
},
messages: {
// same with custom messages
},
showErrors: function(errorMap, errorList) {
// Clean up any tooltips for valid elements
$.each(this.validElements(), function (index, element) {
element = $(element);
NoError_ToolTip(element);
});
// Create new tooltips for invalid elements
$.each(errorList, function (index, error) {
element = $(error.element);
message = error.message;
Error_ToolTip(element,message);
FormError.html("You have some errors. Please check below.").show();
});
},
invalidHandler: function (event, validator) { //display error alert on form submit
FormError.html("You have some errors. Please check below.").show();
},
submitHandler: function (form) {
FormSuccess.html("Processing request...").show();
}
});
$.validator.addMethod("UsaDate", function(value, element) {
return this.optional(element) || /^\b\d{1,2}[\/]\d{1,2}[\/]\d{4}\b/.test(value);
}, "Please enter mm/dd/yyyy date format");
}
这里是我如何使用$ .clone()API从刚处理的行创建了一个新的空行:
var ProcessRequest = function(tr, count, dow){
var success = $('#table_001_processing').css("color", "green").removeClass("hidden").show().html("Processing request...");
//post vars
var recureItemNo = tr.find(".itno").html();
var itemCount = count;
var dp = tr.find("[name='BUYDATE']");
var newDate = dp.val();
tempDate = dp.data("date");
//clear all errors
tr.children("td").each(function(){
$(".qty").each(function(){
$(this).removeClass("error").tooltip("disable").tooltip("hide");
});
});
//insert new row because we create new row off emptyRow
if(tr.find(".itno").hasClass("EmptyRow") && flag == true){
console.log("this was an .EmptyRow");
var randId = (Math.floor(Math.random() * 100 * 100)+1);
var $clone = tr.clone();
$clone.insertBefore(tr);
//clear inputs
$clone.children("td").each(function(){
var $input = $(this).find("input");
$input.val("");
});
//destroy datepicker
var dp = $clone.find(".dp");
dp.remove();
//create new DatePicker(dp);
var dpId = $('#table_001 tr').length + 1;
$clone.find("td:eq(13) span").html("<input name='BUYDATE' class='dp form-control-inline' style='width:95px'; />");
var newDp = $clone.find(".dp").attr("id",dpId);
DatePicker(newDp);
}else{//update took place which means future distribution no matter what
}
//getJSON web0572 complete stuff
//add-ons become future distributions by defaults
if(tr.find(".itno").hasClass("EmptyRow"))
tr.find(".itno").removeClass("EmptyRow").addClass("RecordRow").removeClass("live").addClass("future");
if(tempDate > newDate)
tr.find(".itno").removeClass("live").addClass("future");
if(tr.find(".itno").hasClass("future"))
tr.addClass("pending");
//Call web057s2 add all of this below:
console.log("hasqtys ="+hasQtys+"itemno");
//Display successful processing
if(hasQtys == true){
console.log("processed request add or update");
if(hadOtherQtys == true && tr.find(".itno").hasClass("RecordRow"))
success.html("Item #" + recureItemNo + " successfully updated!");
else
success.html("Item #" + recureItemNo + " successfully added!");
}else{
console.log("processed request deleted row");
tr.remove();
success.html("Item #" + recureItemNo + " for " + tempDate + " successfully removed!");
}
setTimeout(function(){
success.addClass("hidden").hide().css("color","green").html("Processing...");
}, 7000);
flag = false;
}
我发现,关键是创造动态的日期选择器是当你克隆一个对象覆盖旧的id,或者甚至是克隆对象的元素本身,这个例子可以在ProcessRequest函数里看到:
NOTE: I ha d与onChangeDate事件监听器一起玩一些游戏,以便处理datepicker关闭的行。现在唯一的问题是onChangeDate事件侦听器未在新克隆的对象上触发。我试图解除绑定事件并绑定它,但没有运气atm。
var DatePicker = function(that){
if (jQuery().datepicker) {
//destroy old datepicker from clone
if(that.hasClass('hasDatepicker')){
that.datepicker('remove');
}
that.datepicker({
showOn: "button",
buttonImage: "/images/calendar.png",
buttonImageOnly: true,
buttonText: 'Select a start buying date',
changeMonth: true,
changeYear: true,
beforeShow: function() {
setTimeout(function(){
$('.ui-datepicker').css('z-index', 100100);
}, 0);
},
onSelect: function() {
$(this).removeClass("error").tooltip("disable").tooltip("hide");
$('.ui-datepicker').css('z-index', -1);
setTimeout(function(){
//allows date to catchup
},0);
},
onClose: function(){
$(this).trigger("changeDate");
},
minDate: '+1', // The min date that can be selected, i.e. 30 days from the 'now'
maxDate: '+1y' // The max date that can be selected, i.e. + 1 month, 1 week, and 1 days from 'now'
// HR MIN SEC MILLI
//new Date().getTime() + 24 * 60 * 60 * 1000)
}).datepicker();
}
}
注:我一直在尝试使用类似的技术,我使用随机的ID与日期选择器用于创建dynamic bootstrap popout confirmation dialogs但我有一些错误的地方正在采取两次点击输入。如果我修复,我会发布结果。
我有一个datepicker错误,其中第一个选择不注册可能是因为它嵌套在一个事件内?在做了一些研究之后,我发现有一个similar issue with angular where user had to select date twice。我在OnClose中设置了一个setTimeout,它似乎可以解决问题。我将对弹出窗口使用相同的方法,看看它是如何发挥作用的。我希望我能帮助别人!
UPDATE: 感谢artemisian,我能够与动态datepickers解决的问题!关于克隆日期选择器请参阅this。
var DatePicker = function(that){
if (jQuery().datepicker) {
//alert(that.attr('id'));
that.datepicker({
showOn: "button",
buttonImage: "/images/calendar.png",
buttonImageOnly: true,
buttonText: 'Select a start buying date',
changeMonth: true,
changeYear: true,
beforeShow: function() {
setTimeout(function(){
$('.ui-datepicker').css('z-index', 100100);
}, 0);
},
onSelect: function() {
$('.item-failure').addClass("hidden").hide();
$(this).removeClass("error").tooltip("disable").tooltip("hide");
$('.ui-datepicker').css('z-index', -1);
setTimeout(function(){
//allows date to catchup
},0);
},
onClose: function(){
$(this).trigger("changeDate");
},
minDate: '+1', // The min date that can be selected, i.e. 30 days from the 'now'
maxDate: '+1y' // The max date that can be selected, i.e. + 1 month, 1 week, and 1 days from 'now'
// HR MIN SEC MILLI
//new Date().getTime() + 24 * 60 * 60 * 1000)
}).datepicker();
if($(this).hasClass("newDp")){
$(this).bind("changeDate", function(e) { // this is the missing part in my opinion
e.stopImmediatePropagation();
$(this).trigger("focusout");
alert("onchange date" + $(this).val());
});
//Dynamic binding on cloned datepickers only
$(this).on("focusout",function(){
RowValidation($(this));
});
}
}
}
那么我这样做,所以我不得不重用代码:
/************ ************************************************** * 包装功能允许动态绑定 **************************************** ****************************************/
var RowValidation = function(that){
//reset validators
$(".qty").rules('remove','min');
$(".qty").rules('remove','max');
$(".qty").rules('remove','required');
hasQtys = false;
hadOtherQtys = false;
var row = that.closest('tr');
that.rules('add',{required:true,messages:{required:"Must supply a start buy date."}});
that.rules('add',{UsaDate:true,messages:{UsaDate:"Enter date in mm/dd/yyyy format"}});
var buydate = that;
var num = 0;
var dow = '';
var delday = '';
var quans = 0;
flag = true;
var qty = $();
var Error = false;
//console.log("dp triggered" + e.type);
//only check date when manually entered.
if(e.type === "keyup" && (that.valid() === false || that.val() ===""))
{ console.log(e.type + $(that.valid());
that.addClass("error").tooltip("enable").show();
$('item-failure').removeClass("hidden").html("You have some errors. See below.").show();
Error = true;
}
//check for qtys in row before processing
row.children("td").each(function(index){
qty = $(this).find(".qty");
if(qty.val() !== undefined){
num = parseInt(qty.val(), 10);
if(isNaN(num))
num = 0;
if(num > 0){
hasQtys = true;
if(quans > 1)
hadOtherQtys = true;
quans++;
}
//Min max or 0 validation process
console.log(num + ">"+ +parseInt(qty.attr("max"),10) + "<"+ parseInt(qty.attr("min"),10) + "not 0 = "+ num);
console.log(num > parseInt(qty.attr('max'),10));;
//qty out of range: min or Max attr or != 0?
if(num != 0 && (num > parseInt(qty.attr("max"),10) || num < parseInt(qty.attr("min"),10)))
{
if(num > parseInt(qty.attr("max"),10)){
qty.rules('add',{max: parseInt(qty.attr('max')), messages: {max: "Quantity must not be greater than " + qty.attr("max")}});
}
else{
qty.rules('add',{min: parseInt(qty.attr('min')), messages: {max: "Quantity must be greater than " + qty.attr("min")}});
}
qty.addClass("error").tooltip("enable").tooltip('show');
$('.item-failure').removeClass("hidden").show().html(qty.data("originalTitle"));
Error = true;
}
}//eof undefined qty
});
//Empty rows require atleast one qty
if(row.find(".itno").hasClass("EmptyRow") && hasQtys === false){
row.find(".qty").rules('add',{required: true, messages: {required: "Quantity must be entered when the item request date is new"}});
row.find(".qty").addClass("error").tooltip("enable");
Error = true;
}
if(Error === true)
return true;
else{
//Final stage of processing multiple records
row.children("td").each(function(){
qty = $(this).find(".qty");
if(qty.val() !== undefined){
num = parseInt(qty.val(), 10);
if(isNaN(num))
num = 0;
//console.log("buydate.valid() = " +buydate.valid());
//console.log(qty.val() + "<="+ qty.attr("max") +qty.val() + ">="+ qty.attr("min"));
if(buydate.valid() == "1" && buydate.val() !== "" && ((row.find(".itno").hasClass("EmptyRow") && hasQtys === true) || row.find(".itno").hasClass("RecordRow"))){
$('.item-failure').addClass("hidden").hide();
$('.item-success').removeClass("hidden").html("Processing future order requests...").show();
console.log("processing..");
ProcessRequest(row,num,delday);
}
}//eof qty.val undefined
});//eof td children
}//eof error
}