为了账号安全,请及时绑定邮箱和手机立即绑定

jQuery UI 模态表单疯狂踩坑

标签:
JQuery

想套用一下前端的组件写一个表单验证的页面,于是试了一下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 class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://code.jquery.com/jquery-1.12.4.js"></script>    <script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="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. ui@jquery.com" );                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="jane@smith.com" 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>john.doe@example.com</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 标签。
解决方法:知道原因后,修改css的写法也是可以实现的。但是也是有方法按照原来想的那样,让 id="dialog-form" 这个div仍然放在  id="myui" 里面的。在dialog里加上一个 appendTo 参数,指定模态框要添加的位置,之前没有指定就是默认(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 class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://code.jquery.com/jquery-1.12.4.js"></script><script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="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>

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消