jQuery UI 模態表單瘋狂踩坑
官網的例子
首先把官網的例子貼上:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>jQuery UI Dialog - Modal form</title> <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> <link rel="stylesheet" href="/resources/demos/style.css"> <style> label, input { display:block; } input.text { margin-bottom:12px; width:95%; padding: .4em; } fieldset { padding:0; border:0; margin-top:25px; } h1 { font-size: 1.2em; margin: .6em 0; } div#users-contain { width: 350px; margin: 20px 0; } div#users-contain table { margin: 1em 0; border-collapse: collapse; width: 100%; } div#users-contain table td, div#users-contain table th { border: 1px solid #eee; padding: .6em 10px; text-align: left; } .ui-dialog .ui-state-error { padding: .3em; } .validateTips { border: 1px solid transparent; padding: 0.3em; } </style> <script src="https://code.jquery.com/jquery-1.12.4.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <script> $( function() { var dialog, form, // From http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#e-mail-state-%28type=email%29 emailRegex = /^[a-zA-Z0-9.!#$%&‘*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/, name = $( "#name" ), email = $( "#email" ), password = $( "#password" ), allFields = $( [] ).add( name ).add( email ).add( password ), tips = $( ".validateTips" ); function updateTips( t ) { tips .text( t ) .addClass( "ui-state-highlight" ); setTimeout(function() { tips.removeClass( "ui-state-highlight", 1500 ); }, 500 ); } function checkLength( o, n, min, max ) { if ( o.val().length > max || o.val().length < min ) { o.addClass( "ui-state-error" ); updateTips( "Length of " + n + " must be between " + min + " and " + max + "." ); return false; } else { return true; } } function checkRegexp( o, regexp, n ) { if ( !( regexp.test( o.val() ) ) ) { o.addClass( "ui-state-error" ); updateTips( n ); return false; } else { return true; } } function addUser() { var valid = true; allFields.removeClass( "ui-state-error" ); valid = valid && checkLength( name, "username", 3, 16 ); valid = valid && checkLength( email, "email", 6, 80 ); valid = valid && checkLength( password, "password", 5, 16 ); valid = valid && checkRegexp( name, /^[a-z]([0-9a-z_\s])+$/i, "Username may consist of a-z, 0-9, underscores, spaces and must begin with a letter." ); valid = valid && checkRegexp( email, emailRegex, "eg. [email protected]" ); valid = valid && checkRegexp( password, /^([0-9a-zA-Z])+$/, "Password field only allow : a-z 0-9" ); if ( valid ) { $( "#users tbody" ).append( "<tr>" + "<td>" + name.val() + "</td>" + "<td>" + email.val() + "</td>" + "<td>" + password.val() + "</td>" + "</tr>" ); dialog.dialog( "close" ); } return valid; } dialog = $( "#dialog-form" ).dialog({ autoOpen: false, height: 400, width: 350, modal: true, buttons: { "Create an account": addUser, Cancel: function() { dialog.dialog( "close" ); } }, close: function() { form[ 0 ].reset(); allFields.removeClass( "ui-state-error" ); } }); form = dialog.find( "form" ).on( "submit", function( event ) { event.preventDefault(); addUser(); }); $( "#create-user" ).button().on( "click", function() { dialog.dialog( "open" ); }); }); </script> </head> <body> <div id="dialog-form" title="Create new user"> <p class="validateTips">All form fields are required.</p> <form> <fieldset> <label for="name">Name</label> <input type="text" name="name" id="name" value="Jane Smith" class="text ui-widget-content ui-corner-all"> <label for="email">Email</label> <input type="text" name="email" id="email" value="[email protected]" class="text ui-widget-content ui-corner-all"> <label for="password">Password</label> <input type="password" name="password" id="password" value="xxxxxxx" class="text ui-widget-content ui-corner-all"> <!-- Allow form submission with keyboard without duplicating the dialog button --> <input type="submit" tabindex="-1" style="position:absolute; top:-1000px"> </fieldset> </form> </div> <div id="users-contain" class="ui-widget"> <h1>Existing Users:</h1> <table id="users" class="ui-widget ui-widget-content"> <thead> <tr class="ui-widget-header "> <th>Name</th> <th>Email</th> <th>Password</th> </tr> </thead> <tbody> <tr> <td>John Doe</td> <td>[email protected]</td> <td>johndoe1</td> </tr> </tbody> </table> </div> <button id="create-user">Create new user</button> </body> </html>
試了一下,一切都很美好。
From表單DIV的位置
首先看到CSS寫的很粗,比如第一條:
label, input { display:block; }
直接就定義了所有label和input標簽的樣式。看著body裏是2個DIV,於是想把2個div外面再包一層div,像下面這樣:
<body> <div id="myui"> <div id="dialog-form" title="Create new user"></div> <div id="users-contain" class="ui-widget"></div> </div> </body>
然後所有的css樣式前面都加上自己的這個div,就不會和別的樣式沖突了,比如這樣:
#myui label, #myui input { display:block; }
問題:修改後,模態框裏的css樣式都沒有應用上。比如lable和input沒有變成塊級標簽,導致沒有換行
原因:這裏 id="dialog-form" 這個div的位置變了,跑到了 id="myui" 這個div的外面了。造成這個情況的原因是,jQuery UI就是這麽做的,把 id="dialog-form" 這個div挖出來,出來好之後append到一個位置,而默認位置是 body 標簽。
dialog = $( "#dialog-form" ).dialog({
appendTo: ‘#myui‘,
autoOpen: false,
height: 400,
width: 350,
modal: true,
buttons: {
"Create an account": addUser,
Cancel: function() {
dialog.dialog( "close" );
}
},
close: function() {
form[ 0 ].reset();
allFields.removeClass( "ui-state-error" );
}
});
實際有2個提交的按鈕
在提交表單之前,先要知道,實際這個例子中有2個按鈕可以用來提交的。其中一個隱藏了。
寫在form裏的submit按鈕:
<!-- Allow form submission with keyboard without duplicating the dialog button -->
<input type="submit" tabindex="-1" style="position:absolute; top:-1000px">
實際位置應該是跑到屏幕外面去了。這裏把style屬性去掉就能看到了。這個按鈕的作用是方便我們用鍵盤的回車鍵操作的。
另外jQuery UI幫我們生成的按鈕是在 dialog = $( "#dialog-form" ).dialog()
裏用buttons屬性生成的,這裏可以自定義以及添加更多的按鈕:
buttons: {
"Create an account": addUser,
Cancel: function() {
dialog.dialog( "close" );
}
},
另外這裏的確認按鈕不是submit,而是觸發一個addUser進行表單驗證的方法。addUser裏驗證通過後會生成前端的標簽,在前端添加一行數據,但是submit提交。
表單無法提交
演示的代碼是一個純前端的實現,如果要提交到後端,那麽就是在form標簽裏加上action屬性。然後先把submit按鈕的style屬性去掉,用submit先提交。
問題:後端收不到發來的請求
原因:前端阻止了submit事件的默認操作,具體就是下面這句
form = dialog.find( "form" ).on( "submit", function( event ) {
event.preventDefault(); // 這句的意思就是取消事件的默認操作
addUser();
});
解決方法:這裏直接把阻止的代碼註釋掉就好了
現在可以使用回車鍵進行submit提交了,但是確認按鈕並不能提交
問題2:確認按鈕不是submit
原因:這個按鈕綁定的是addUser,而addUser裏也沒有submit提交
解決方法:把確認按鈕的click綁定為觸發form的submit事件,這樣這個確認按鈕的效果就和submit一樣了,而且submit裏也是要先執行addUser()進行驗證的。把 dialog = $( "#dialog-form" ).dialog()
裏用buttons屬性改成了下面這樣,這個buttons的參數是可支持 Object 和 Array 兩種形式,這裏我改用了Array的形式:
buttons: [
{text: "確認", click: function () {
form.submit();
}},
{text: "取消", click: function () {
dialog.dialog(‘close‘);
}
}
],
請求的表單內容為空
現在後端可以收到請求了,但是收到的都是沒有值的空的請求。
問題:後端內收到請求,但是請求的值都是空值
原因:前端在發送submit請求前,清空了表單的內容。在 dialog = $( "#dialog-form" ).dialog()
裏最後有一個close屬性,值是一個方法:
close: function() {
form[ 0 ].reset();
allFields.removeClass( "ui-state-error" );
}
close屬性是在對話框關閉的時候要執行的內容,這裏面清空了form表單的內容。
具體步驟是,submit事件會首先觸發addUser()進行表單驗證。addUser()裏面會先用 checkLength() 驗證長度,然後用 checkRegexp() 進行正則的驗證。如果驗證失敗會返回false組織之後事件的發生。如果驗證通過則會先執行關閉模態框的操作:
dialog.dialog( "close" );
這裏就要關閉模態框了,然後返回true,之後才是執行submit提交的動作。但是關閉模態框的時候,數據就清空了。服務的收到的就是被清空後的空表單發來的請求,並且是在通過了前端的數據驗證之後的。
解決方法:應該可以先不關閉模態框,等submit提交之後再關閉。不過實現起來也不方便。表單清空的功能還是有用的,但是只要在下次打開前清空就好了。這裏可以用一個open方法替代原來的close方法。
另外一個坑:官網的頁面裏是有一個create方法的,本來想用這個的。但是發現沒有用,然後去源碼裏找了一下(jquery-ui.js):
$.widget( "ui.dialog", {
version: "1.12.1",
options: {
appendTo: "body",
autoOpen: true,
buttons: [],
classes: {
"ui-dialog": "ui-corner-all",
"ui-dialog-titlebar": "ui-corner-all"
},
closeOnEscape: true,
closeText: "Close",
draggable: true,
hide: null,
height: "auto",
maxHeight: null,
maxWidth: null,
minHeight: 150,
minWidth: 150,
modal: false,
position: {
my: "center",
at: "center",
of: window,
collision: "fit",
// Ensure the titlebar is always visible
using: function( pos ) {
var topOffset = $( this ).css( pos ).offset().top;
if ( topOffset < 0 ) {
$( this ).css( "top", pos.top - topOffset );
}
}
},
resizable: true,
show: null,
title: null,
width: 300,
// Callbacks
beforeClose: null,
close: null,
drag: null,
dragStart: null,
dragStop: null,
focus: null,
open: null,
resize: null,
resizeStart: null,
resizeStop: null
},
上面的Callbacks應該就是所有的方法了,並沒有create。然後才試的open。
優化-調整模態框大小
還是這個生成模態框的函數 dialog = $( "#dialog-form" ).dialog()
,裏面的 height 和 width 屬性,可以設置模態框的默認大小
問題:這裏模態框有可能會出現滾輪
原因:模態框 id="dialog-form" 這個div裏有這個樣式 overflow: auto ,所以溢出會出現滾動條。
解決方法:把overflow樣式覆蓋掉應該就可以了,不過根本問題是溢出。我這裏造成溢出的標簽是form裏的input標簽。樣式是這樣的:
input.text { margin-bottom:12px; width:95%; padding: .4em; }
這裏我調整了 width 的寬度就好了。
優化-直接隱藏模態框
例子中沒有這個情況,但是我用的時候頁面加載的時候會首先顯示出模態框,等加載完畢後模態框會隱藏掉。
問題:加載頁面的時候會有一瞬出現模態框
原因:因為我把js代碼的位置放到的最後,所以模態框的標簽的代碼讀完的時候會顯示出來,等js代碼讀取完的時候才會隱藏
解決方法:應該把生成模態框的js主要是 dialog = $( "#dialog-form" ).dialog()
方法提到前面就好了。不過我用了另外一個方法,給我的模態框的div標簽加上了這個類:
<div class="ui-helper-hidden" id="dialog-form" title="Create new user">
jquery-ui.css 裏,這個類是這樣的:
.ui-helper-hidden {
display: none;
}
就是簡單的隱藏掉,試過之後依然可以顯示出來。
修改後的版本
替換掉了模板語言的部分,我用的修改後的版本如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<style>
#jqui-dialog label, #jqui-dialog input { display:block; }
#jqui-dialog form input { margin-bottom:12px; width: 92%; padding: .4em; }
#jqui-dialog fieldset { padding:0; border:0; margin-top:25px; }
#jqui-dialog h1 { font-size: 1.2em; margin: .6em 0; }
#jqui-dialog div#users-contain { width: 350px; margin: 20px 0; }
#jqui-dialog div#users-contain table { margin: 1em 0; border-collapse: collapse; width: 100%; }
#jqui-dialog div#users-contain table td, #jqui-dialog div#users-contain table th {
border: 1px solid #eee;
padding: .6em 10px;
text-align: left;
}
#jqui-dialog .ui-dialog .ui-state-error { padding: .3em; }
#jqui-dialog .validateTips { border: 1px solid transparent; padding: 0.3em; }
</style>
</head>
<body>
<div id="jqui-dialog">
<div class="ui-helper-hidden" id="dialog-form" title="添加新用戶組">
<p class="validateTips">請填寫要新建的組的名稱</p>
<form action="." method="post">
<fieldset>
<label for="group_name">組名稱</label>
<input type="text" name="group_name" id="group_name" class="text ui-widget-content ui-corner-all">
<label for="comments">備註</label>
<input type="text" name="comments" id="comments" class="text ui-widget-content ui-corner-all">
<!-- Allow form submission with keyboard without duplicating the dialog button -->
<input type="submit" tabindex="-1" style="position:absolute; top:-1000px">
</fieldset>
</form>
</div>
<div id="users-contain" class="ui-widget">
<h1>用戶組列表:</h1>
<button id="create-user">添加新用戶組</button>
<table id="users" class="ui-widget ui-widget-content">
<thead>
<tr class="ui-widget-header ">
<th>id</th>
<th>群組名</th>
<th>備註</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>測試一</td>
<td>test1</td>
</tr>
<tr>
<td>2</td>
<td>測試2</td>
<td>test2</td>
</tr>
<tr>
<td>3</td>
<td>測試三</td>
<td>test3</td>
</tr>
</tbody>
</table>
</div>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(function () {
var dialog, form,
group_name = $(‘#group_name‘),
comments = $(‘#comments‘),
allFields = $([]).add(group_name).add(comments),
tips_msg = null,
tips = $(‘.validateTips‘);
// 長度驗證
function checkLength(o, n, min, max) {
if (o.val().length > max || o.val().length < min) {
o.addClass("ui-state-error");
updateTips(n + "的長度必須在" +
min + "與" + max + "之間。");
return false
} else {
return true
}
}
// 正則驗證
function checkRegexp(o, regexp, n) {
if (!(regexp.test(o.val()))) {
o.addClass("ui-state-error");
updateTips(n);
return false
} else {
return true
}
}
// 顯示錯誤信息
function updateTips(t) {
tips_msg = tips_msg || tips.text();
tips.text(t).addClass("ui-state-highlight");
setTimeout(function () {
tips.removeClass("ui-state-highlight", 1500);
}, 500);
}
function addUser() {
var valid = true;
allFields.removeClass("ui-state-error");
valid = valid && checkLength(group_name, "組名稱", 3, 32);
valid = valid && checkLength(comments, "備註", 0, 64);
valid = valid && checkRegexp(group_name, /^[^0-9]/, "組名稱不能以數字開頭");
if (valid) {
$( "#users tbody" ).append( "<tr>" +
"<td>" + ‘x‘ + "</td>" +
"<td>" + group_name.val() + "</td>" +
"<td>" + comments.val() + "</td>" +
"</tr>" );
dialog.dialog(‘close‘);
}
return valid
}
dialog = $("#dialog-form").dialog({
appendTo: ‘#jqui-dialog‘,
autoOpen: false,
height: 400,
width: 350,
modal: true,
buttons: [
{text: "提交", click: function () {
form.submit();
}},
{text: "取消", click: function () {
dialog.dialog(‘close‘);
}
}
],
open: function() {
form[ 0 ].reset();
allFields.removeClass( "ui-state-error" );
tips_msg && tips.text(tips_msg);
}
});
form = dialog.find("form").on("submit", function(event) {
event.preventDefault(); // 阻止默認操作,需要提交到後端的時候註釋掉這句
return addUser(); // 返回驗證的結果,如果為false,會阻止submit提交
});
$("#create-user").button().on("click", function() {
dialog.dialog( "open" );
});
})
</script>
</body>
</html>
jQuery UI 模態表單瘋狂踩坑