JavaScript通用表单处理与Ajax交互组件实现
作者:小括号时间:2026-10-29 00:26:44浏览:266
本文介绍一个基于JavaScript和jQuery的通用表单处理组件。该组件封装了Ajax同步提交、按钮动作注入、复选框状态同步、日期与时间选择器初始化等功能。通过自定义验证效果和拦截器,实现表单数据的自动收集、错误提示及页面局部刷新,适用于需要统一交互逻辑的Web应用。
通用表单处理组件概述
这是一个用于处理各类表单、链接及按钮交互的通用JavaScript组件。
具体实现代码如下:
/**
* 通用表单处理 JavaScript 脚本
* 作者:Anthony.chen
*/
Ajax 请求配置与按钮动作处理/**
* 将按钮动作 [btn_action] 数据推入表单
* 如果存在预执行脚本,则先运行该脚本
*/
"use strict";
// 默认情况下所有 Ajax 请求同步执行
var ajaxSynchronized = true;
// 默认情况下所有 Ajax 请求完成后自动解除阻塞
var ajaxAutoUnblock = true;
var ajax_action_button = function (e){
var btn = $(this);
// 添加预执行脚本
var pre_script;
if(pre_script = btn.attr("pre_script")){
var ret = eval(pre_script);
if(ret==false){
return false;
}
}
var btn_action = btn.attr('value');
if(btn_action){
$(this).closest('form').data('btn_action',
{ name:'btn_action',value:btn_action }
);
}
};
表单数据更新与特殊控件处理/**
* 使用表单元素、键和值更新 FormElement 中的额外表单数据
*/
var ajax_update_post_data = function(formEle, k, v){
var form = $(formEle);
var post_data = form.data('post_data');
if( post_data == undefined ){
post_data = {};
}
if( v == undefined ){
delete post_data[k];
}else{
post_data[k] = v;
}
form.data('post_data',post_data);
return true;
};
/**
* 布尔复选框是特殊的复选框,需要保持未选中状态
* 并通过 Ajax 表单提交,该表单位于父级中
*/
var bool_checkbox = function(){
var ipt = $(this);
var formEle = ipt.closest("form");
var _check = ipt.prop("checked");
if(_check){
ajax_update_post_data(formEle,ipt.attr('name'));
}else{
ajax_update_post_data(formEle,ipt.attr('name'),'f');
}
};
数字、日期与时间选择器初始化/**
* 初始化数字旋转器
*/
var spin_number = function(){
var spin = $(this);
var config = {
lock:true,
imageBasePath: '{webpath}/css/images/spin/',
btnCss: null,
txtCss: null,
btnClass:'spin_btn',
};
var interval = spin.attr('interval');
if(interval){
config.interval = interval;
}else{
config.interval = 1;
}
var min = spin.attr('min');
if( min ){
config.min = min;
}
var max = spin.attr('max');
if( max ){
config.max = max;
}
spin.spin(config);
return true;
};
/**
* 初始化日期输入框
*/
var date_input = function(){
var ipt = $(this);
var config = {
offset:[4,0],
selectors:true,
lang: '{lang}',
firstDay : 1,
format: 'yyyy-mm-dd',
};
var min = ipt.attr('min');
if( min ){
config.min = min;
}
var min = ipt.attr('min');
if( min ){
config.min = min;
}
ipt.dateinput(config);
return true;
};
/**
* 初始化时间选择器
*/
var time_picker = function(){
var ipt = $(this);
var config = { };
var step = ipt.attr("step");
if( step ){
config.step = step;
}
ipt.timePicker( config );
return true;
};
Ajax 表单提交核心逻辑/**
* 通用 Ajax 表单提交函数
* 如果设置了 btn_action,则将数据添加到表单
* 如果设置了 returi,则重定向到 returi
* 如果设置了 reload,则重新加载
* 否则显示阻塞消息
*
* 表单将被验证。
*/
var ajax_form_post = function(e){
var form = $(this);
var pre_script;
if(pre_script = form.attr("pre_script")){
var ret = eval(pre_script);
if(ret==false){
return false;
}
}
var errHint = form.find(".formError").first();
if(errHint.size() == 0){
errHint = $("#pageError");
}
errHint.text('').hide();
// 清理页面错误
if(!e.isDefaultPrevented()){
// 隐藏所有 .formError
$.blockUI({ message:"{__('L_PROCESSING')}" });
var formArray = form.serializeArray();
var btn_action_data;
var btn_action;
if(btn_action_data = form.data('btn_action')){
formArray.push(btn_action_data);
form.removeData('btn_action');
btn_action = btn_action_data.value;
}else{
btn_action = '';
}
console.log('btn action:'+btn_action);
// 添加额外数据
var post_data;
if(post_data = form.data('post_data')){
for (var k in post_data ){
//if post_data[k] is array,need more to do
formArray.push( { name:k ,value: post_data[k] } );
}
form.removeData('post_data');
}
$.post(form.attr('action'), formArray,function(json){
if($(window).data('blockUI.isBlocked') == 1){
$.unblockUI();
}
if(json.code === true){
var returi = "";
var retData = "{__('L_PROCESSED')}!";
if(json.data){
retData = json.data;
}
//TODO 添加支持以允许保存并停留
if(btn_action =='reqonly'){
if(returi = form.attr('returi')){
$(window).data('blockUI.returi',returi);
ajaxAutoUnblock = false;
}
$.blockUI({ message:retData, css:{ cursor:'pointer',padding:'4px',border:'3px solid #CC0000',}, overlayCSS:{ cursor:'pointer' } });
$(".blockUI").addClass("blockwarn");
} // 如果设置了 returi,则返回到 uri
else if(returi = form.attr('returi')){
window.location = returi;
// 否则如果设置了 reload,则重新加载
}else if(form.attr('reload')!=undefined){
window.location.reload();
}else{
$.blockUI({ message:retData });
$.unblockUI();
}
}
else{
if(typeof (json.data.errmsg) == 'string'){
errHint.html(json.data).show();
//$.blockUI({ message:json.data, css:{ cursor:'pointer',padding:'4px',border:'3px solid #CC0000',}, overlayCSS:{ cursor:'pointer' } });
//$(".blockUI").addClass("blockwarn");
}else{
errHint.html("{Html::text(__('E_FORM'))}").show();
for(var p in json.data){
var msg = json.data[p];
//处理隐藏值,None hidden input 应该具有引用隐藏值的 refid
//显示服务器消息到 ref
var ele = form.find("[type=hidden][name="+p+"]");
if(ele.length){
delete json.data.p;
refid = ele.attr("id");
refname = form.find("[hidden-id="+refid+"]").attr("name");
json.data[refname]=+msg;
}
// 多选框
var ele = form.find("[type=checkbox][name='"+p+"[]']");
if(ele.length){
delete json.data.p;
refname = p+'[]';
json.data[refname]=+msg;
}
//@END
}
}
/*
* 检查隐藏值
*/
form.data("validator").invalidate(json.data);
} },'json');
e.preventDefault();
}else{
errHint.html("{Html::text(__('E_FORM'))}").show();
}
};
隐藏字段验证与重置/***
* 重置输入
*/
var ajax_post_form_hidden = function(){
var form = $(this);
form.find("[hidden-id]").each(function(){
// 清除引用消息
var input = $(this);
var refid = input.attr("hidden-id");
var field = $("#" + refid + "");
// 设置清除 Errmsg
// 监听 ID 元素上的 change 事件,移除真实输入的错误消息
field.change(function(){
// 隐藏输入
var hinput = $(this);
// 真实输入
var rinput = $("[hidden-id="+hinput.attr("id")+"]").first();
form.data('validator').reset(rinput)
});
});
};
var validate_hidden_id = function(input) {
var refid = input.attr("hidden-id");
var field = $("#" + refid);
var msg = field.attr('msg');
if( !msg ){
msg = "{__('E_NOT_EMPTY')}";
}
return field.val() ? true : msg;
};
var data_equals_validate = function(input) {
var field;
var name = input.attr("data-equals");
field = this.getInputs().filter("[name=" + name + "]");
return input.val() == field.val() ? true : [name];
};
链接与按钮的 Ajax 交互/**
* 通过链接发送 Ajax 请求
* 如果设置了 confirm,则在发送请求前确认
* 支持 returi 和 reload
* 否则显示阻塞消息
*/
var ajax_link_req = function(){
var l = $(this);
var hint = l.attr('hint');
if(hint){
var errHint = $(l.attr('hint'));
errHint.text('').hide();
}
// 如果设置了确认消息,则应从客户端确认
if(l.attr('confirm')){
if(!confirm(l.attr('confirm'))){
return false;
}
}
$.blockUI({ message:"{__('L_PROCESSING')}" });
var pre_script;
if(pre_script = l.attr("pre_script")){
var ret = eval(pre_script);
if(ret==false){
return false;
}
}
var block = l.attr('block');
if(block != undefined){
ajaxAutoUnblock = false ;
}
$.get(l.attr('href'),function(json){
if(json.code == true){
var retData = "{__('L_PROCESSED')}!";
var returi;
// 如果成功执行每个函数
var successFunc = l.attr('success');
if(successFunc){
l.each(window[successFunc]);
}
if(json.data){
retData = json.data;
}
// 如果需要警告前
if( l.attr('value') == 'reqonly'){
alert(retData);
}else if(returi = form.attr('returi')){
window.location = returi;
}
else if(l.attr('reload')!=undefined){
window.location.reload();
}
else{
$.blockUI({ message:retData, css:{ cursor:'pointer',padding:'4px',border:'3px solid #CC0000',}, overlayCSS:{ cursor:'pointer' } });
$(".blockUI").addClass("blockwarn");
}
}else{
//$.unblockUI();
//仅支持文本 errmsg
if(hint){
errHint.text(json.data).show();
}else{
alert(json.data);
}
}},'json');
return false;
};
/**
* 支持基于按钮的导航
* 仅跳转到新 href
*/
var btn_nav = function(){
var input = $(this);
var href = input.attr("href");
if(href){
window.location = href;
}else{
alert("Href not set");
}
return false;
};
/**
* 支持基于按钮的 Ajax 获取方法请求
* 支持 returi 和 reload
*/
var btn_req = function(){
var input = $(this);
var href = input.attr("href");
var hint = input.attr('hint');
if(hint){
var errHint = $(hint).first();
if(errHint.size() == 0){
errHint = $("#pageError");
}
errHint.text('').hide();
}
var block = input.attr('block');
if(block != undefined){
ajaxAutoUnblock = false
}
$.get(href,function(json){
if(json.code == true){
var returi;
if(returi = input.attr('returi')){
window.location = returi;
}
else if(input.attr("reload")!=undefined){
window.location.reload();
}else{
var retData = "{__('L_PROCESSED')}!";
if(json.data){
retData = json.data;
}
$.blockUI({ message:retData,css:{ cursor:'pointer' },overlayCSS:{ cursor:'pointer' } });
}
}else{
if(hint){
$.unblockUI();
errHint.html(json.data.errmsg).show();
}else{
$.blockUI({ message:json.data.errmsg, css:{ cursor:'pointer',padding:'4px',border:'3px solid #CC0000',}, overlayCSS:{ cursor:'pointer' } });
$(".blockUI").addClass("blockwarn");
}
}
},'json');
return false;
};
复选框与多级选择列表处理/**
* 通用 Ajax 复选框
* 默认操作被阻止,并通过 URL 提交真实请求
*/
var ajax_checkbox = function(){
event.preventDefault();
var action = $(this);
var url = action.attr('url');
var _check = action.prop("checked");
console.log(_check);
var op ;
if(_check){
op = "1";
}else{
op = "0";
}
$.get(url + op ,function(json){
if(json.code == true){
if(_check){
action.prop("checked",true);
}else{
action.prop("checked",false);
}
return true;
}else{
return false;
}
},'json');
};
/**
* 创建根级选择列表
*/
var picklistinit = function(){
var _select = $(this);
var _hidden_id = _select.attr('hidden-id');
var _un = _select.attr('un');
var _lovchildren = _select.data('lovtree').c;
var _rowvalue = _select.data('rowvalue');
$("<OPTION>").append("{__('L_SELECT')}").appendTo(_select);
for(var _kid in _lovchildren){
var _lov = _lovchildren[_kid]['lov'];
$("<OPTION>").val(_lov.lov_id).append(_lov.v).attr('k',_lov.id).attr('is_leaf',_lov.is_leaf).appendTo(_select);
}
_select.change(picklistchange);
// 选择列表
if(_rowvalue){
_select.find("[value="+_rowvalue[0]+"]").prop("selected",true);
_select.change();
}
return true;
};
/**
* 选择选择列表
*/
var picklistchange = function (){
var _select = $(this);
var _hidden_id = _select.attr('hidden-id');
var _un = _select.attr('un');
// 移除所有后续项
var _lovtree = _select.data('lovtree');
var _rowvalue = _select.data('rowvalue');
_select.nextAll().remove();
// 当前选择列表的值
var _selected = _select.find(':selected');
if(_selected.attr('is_leaf')=="{DB::T}"){
$("#"+_hidden_id).val(_select.val());
_select.after("<img src='/s.gif' class='sprite_global successimg'/>");
}else{
var _val = _select.val();
var _k = _selected.attr('k');
// 获取子列表
if(_lovtree.c[_k].c == undefined){
return false;
}
var _c_lovtree = _lovtree.c[_k];
var _c_select = $('<SELECT>').data('lovtree',_c_lovtree).
data('rowvalue',_rowvalue).
attr('hidden-id',_hidden_id).attr('un',_un).
attr('name',_un+'_'+_k);
$("<OPTION>").append("{__('L_SELECT')}").appendTo(_c_select);
// 构建选项列表
for(var _kid in _c_lovtree.c){
var _lov = _c_lovtree.c[_kid]['lov'];
$("<OPTION>").val(_lov.lov_id).append(_lov.v).attr('k',_lov.id).attr('is_leaf',_lov.is_leaf).appendTo(_c_select);
// 插入之后
_select.after(_c_select);
// 更改事件
}
_c_select.change(picklistchange);
if(_rowvalue){
_c_select.find("[value="+_rowvalue[_k]+"]").prop("selected",true);
_c_select.change();
}
};
查找窗口与文件上传功能var lookup_new = function(){
var lookup = $(this);
var pre_script;
if(pre_script = lookup.attr("pre_script")){
var ret = eval(pre_script);
if(ret==false){
return false;
}
}
var url = lookup.attr("url");
if(!url){
alert('url not set');
return false;
}
var height = lookup.attr('h');
if(!height){
height = 600;
}
var width = lookup.attr('w');
if(!width){
width = 800;
}
window.open(url,"pselect","scrollbars=yes,menubar=no,height="+height+",width="+width+",resizable=yes,toolbar=no,location=no,status=no");
return false;
};
/**
* 为隐藏值查找新值
*/
var parent_lookup = function(){
var lookup = $(this);
var pid = opener.$("#" + lookup.attr('pid'));
if(!pid.length){
alert(lookup.attr('pid')+ " not found");
return false;
}
var pname = opener.$( "#" + lookup.attr('pname'));
if(!pname.length){
alert(lookup.attr('pname')+ " not found");
return false;
}
var aft_script;
// 运行当前后脚本
if(aft_script = lookup.attr('aft_script')){
window.eval(aft_script);
}
pid.val($(this).attr("refid"));
// 仅 opener 操作可触发更改事件
pid.change();
pname.val($(this).attr("refvalue"));
pname.change();
// 父级 after_script
if(aft_script = pname.attr('aft_script')){
opener.window.eval(aft_script);
}
if(aft_script = pid.attr('aft_script')){
opener.window.eval(aft_script);
}
window.close();
};
/**
* 默认上传完成
*/
//var uploadComplete = function(event, id, fileName, responseJSON) {
var uploadComplete = function(e, data) {
// 将由 jquery 上传器替换
var _fileUpload = $(this);
//console.log(_fileUpload);
//console.log(data.result);
if(_fileUpload.attr('reload')!=undefined){
window.location.reload();
}
};
/**
* 文件上传函数,以下属性控制上传操作
* 'endpoint' 作为上传 url
* 'sid' 作为会话 id
* 'complete' 可选配置自定义上传完成函数
*/
var genericUpload = function(dom){
var endpointurl = $(this).attr('endpoint');
var sid = $(this).attr("sid");
var completeFunc = 'uploadComplete';
// 设置自定义完成函数
var cusComplete = $(this).attr('complete');
if(cusComplete){
completeFunc = cusComplete;
}
$.fileupload({
url: endpointurl,
autoUpload:true,
dataType:'json',
formData: [{ 'sessionid': sid }],
paramName: 'Filedata',
}).bind('fileuploaddone',window[completeFunc]);
};
高级验证效果与事件绑定/**
* 与输入匹配的错误
* 仅在此处识别匹配的错误
*/
var advance_validate = function(errors, event) {
var conf = this.getConf();
// 循环错误
$.each(errors, function(index, error) {
// 将错误类添加到输入 Dom 元素
var input = error.input;
input.addClass(conf.errorClass);
// 获取错误容器的句柄
var msg = input.data("msg.el");
// 如果不存在则创建 Error 数据,并为输入添加错误类
// "msg.el" 数据链接到错误消息 Dom 元素
if (!msg) {
//msg = $(conf.message).addClass(conf.messageClass).insertAfter(input);
msg = $(conf.message).addClass(conf.messageClass).appendTo(input.parent());
input.data("msg.el", msg);
}
// 清空容器
msg.css({visibility: 'hidden'}).find("span").remove();
// 填充消息
$.each(error.messages, function(i, m) {
$("<span/>").html(m).appendTo(msg);
});
// 确保宽度不是整个主体宽度,以便可以正确定位
if (msg.outerWidth() == msg.parent().width()) {
msg.add(msg.find("span"));
}
// 插入正确的位置(相对于字段)
msg.css({ visibility: 'visible'}) .fadeIn(conf.speed);
msg.parent().addClass("colError");
});
};
var advance_inputs = function(inputs) {
var conf = this.getConf();
inputs.removeClass(conf.errorClass).each(function() {
var msg = $(this).data("msg.el");
if (msg) {
msg.hide();
msg.parent().removeClass("colError");
}
});
if($(".colError").size() == 0){
var form = $(this);
var errHint = form.find(".formError").first();
if(errHint.size() == 0){
errHint = $("#pageError");
errHint.text('').hide();
}
}
};
/**
* 当 refname 包含被选中时
*/
var checkall = function() {
var check = $(this);
var refname = check.attr('refname');
if(refname){
if(check.prop("checked")){
$("input[name*='"+refname+"']").prop("checked",true);
}else{
$("input[name*='"+refname+"']").prop("checked",false);
}
}
var refclass = check.attr('refclass');
if(refclass){
if(check.prop("checked")){
$("input."+refclass).prop("checked",true);
}else{
$("input."+refclass).prop("checked",false);
}
};
/**
* 设置只读复选框
*/
var readonlyCheck = function(e){
e.preventDefault();
return false;
};
/**
* 选择列表禁用
*/
var readonlySelect = function(){
$(this).prop("disabled", true);
};
$(document).ready(function() {
$(document).ajaxStart(function(){
// 清理 Ajax 请求页面级错误
$("#pageError").text('').hide();
// 清理表单错误
$(".formError").text('').hide();
// 阻止所有 ajax 处理
if(ajaxSynchronized){
$.blockUI({ message:"{__('L_PROCESSING')}" });
}
});
$(document).ajaxStop(function(){
if(ajaxSynchronized){
if($(window).data('blockUI.isBlocked') == 1){
if(ajaxAutoUnblock){
$.unblockUI();
}else{
ajaxAutoUnblock = true;
}
}
}else{// 从异步模式改回默认同步模式
ajaxAutoUnblock = true;
ajaxSynchronized = true;
}
});
$(document).ajaxError(function(event, request, settings){
alert('Ajax Request Error! URL='+settings.url);
if(ajaxSynchronized){
if($(window).data('blockUI.isBlocked') == 1){
if(ajaxAutoUnblock){
$.unblockUI();
}else{
ajaxAutoUnblock = true;
}
}
}else{
ajaxAutoUnblock = true;
ajaxSynchronized = true;
}
});
// 强制解除 blockUI
$(document).click(function(){
if($(window).data('blockUI.isBlocked') == 1){
$.unblockUI();
var returi = $(window).data('blockUI.returi');
if(returi){
window.location = returi;
}
}});
$.tools.validator.addEffect("advanced", advance_validate,advance_inputs);
$.tools.validator.fn("[data-equals]", { "{lang}":"{__('E_NOTEQUAL')}" }, data_equals_validate );
$.tools.validator.fn("[hidden-id]",validate_hidden_id);
$(".ajax_form_post").validator({ lang:'{lang}',effect:'advanced' }) .submit( ajax_form_post );
$(".ajax_form_post").each(ajax_post_form_hidden);
$(".spin_number").each(spin_number);
$(".date_input").each(date_input);
$(".time_picker").each(time_picker);
$('.ajax_link_req').click(ajax_link_req);
// 隐藏 ID 的客户端验证
$(".require_validate").validator({ lang:'{lang}',effect:'advanced' });
$(".btn_nav").click( btn_nav );
$(".btn_req").click( btn_req );
$("button.btn_action").click(ajax_action_button);
$(".lookup_new").click(lookup_new);
$(".parent_lookup").click(parent_lookup);
$(".ajax_checkbox").click(ajax_checkbox);
$(".bool_checkbox").click(bool_checkbox);
$(".checkall").click(checkall);
$("img[rel]").overlay();
$("input[tip]").tooltip({ position:"center right"});
// 最后我们将进行本地化
$.tools.validator.localize("{lang}", {
'*' : "{__('E_ALL')}",
':email' : "{__('E_EMAIL')}",
':number' : "{__('E_DECIMAL')}",
':url' : "{__('E_URL')}",
'[max]' : "{__('E_MAX_LENGTH')}",
'[min]' : "{__('E_MIN_LENGTH')}",
'[required]' : "{__('E_NOT_EMPTY')}",
});
});作者最新文章
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
