在做form表单提交的时候,都会遇到一个非常尴尬的事情:就是浏览器默认的submit功能竟然会刷新当前页面,并跳转至action链接

这(“▔□▔) Σ<(=~д~=!)> (~▽~”) (~_~|||) – … …

一般来说,这个action链接都是处理表单并返回一些文本信息的Controller函数,以空白页面居多。如果这个action链接需要以POST请求访问的话,浏览器窗口很可能还会提示服务器错误信息。显然,这不是我们想要的。

我们大多时候想要的是无刷新的表单提交

一想到无刷新提交,一般想法是用Ajax请求模拟表单提交。如果遇到文件上传的话,可以考虑使用FormData来封装数据。想想就觉得完美。

用Ajax请求模拟时,要注意是否上传文件。如果上传文件的话,注意请求的Content-Type要设置为“mutipart/form-data”。用jQuery的写法如下:

var formData = new FormData($("#form")[0]);
$.ajax({
    url: formUrl,
    method: "POST",
    cache: false, // 禁用缓存
    contentType: false, // 防止自动转换content-type为默认的"application/x-www-form-urlencoded"
    processData: false, // 防止自动转换FormData数据
    data: formData,
    success: function(data) {
        // 表单提交成功后的操作
    }
});

然而,很不幸的是,FormData是HTML5的新特性,兼容性不好,IE系列浏览器只能支持IE10+。

其实,无刷新表单提交还有一种很hack的做法:将form提交后的结果在target指定的iframe里执行。即将form的target属性指向iframe。具体如下:

<form name="form1" id="form1" method="post" action="/test" target="ajaxForm">
    <input type="file" name="avatar">
    <input type="submit" value="提交">
</form>
<iframe id="ajaxForm" src="about:blank" style="display: none"></iframe>

将form提交后的结果在target指定的iframe里执行,这种实现方式的兼容性很好,基本没有什么兼容性问题。然而,<iframe>元素的Web安全性向来为人所诟病,而且在页面里引入了新标签,似乎并不怎么优雅。

为了能实现IE6+的兼容性,我选择的做法是优雅降级,具体如下:

对于支持FormData方法的浏览器,使用Ajax请求模拟表单提交; 对于不支持FormData方法的浏览器,则将form提交后的结果在target指定的iframe里执行

封装后的代码如下:(原生JS实现)

var util = util || {};

util = {
    /* ajax请求函数封装 */
    ajax: function(options) {
        var __this = this,
            url = options.url || "",
            method = options.method || "get",
            data = options.data || "",
            dataType = options.dataType || "text",
            successCallback = options.success || new Function(),
            errorCallback = options.error || new Function();
        method = method.toUpperCase();

        if(method == 'GET' && data) {
            // 拼接query字符串
            var queryStr = "",
                index = 0;
            for(var i in data) {
                queryStr += i + "=" + data[i];
                if(index <= __this.getObjectlength(data) - 1) {
                    queryStr += "&";
                }
                index++;
            }
            url += (url.indexOf('?') == -1 ? '?' : '&') + queryStr;
            data = null;
        }

        var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');

        xhr.onreadystatechange = function() {
            if(xhr.readyState == 4) {
                var status = xhr.status;
                if(status >= 200 && status < 300) {
                    var data = xhr.responseText;
                    successCallback(data);
                } else {
                    errorCallback(xhr);
                }
            } else {
                errorCallback(xhr);
            }
        };

        xhr.open(method, url, true);

        if(method === 'POST'){
            // 设置content-type
            var contentType;
            switch(dataType) {
                case "json":
                    contentType = "application/json";
                case "form":
                    contentType = "mutipart/form-data";
                default:
                    contentType = "application/x-www-form-urlencoded";
            }
            xhr.setRequestHeader('Content-type', contentType);
        }

        xhr.send(data);
        return xhr;
    },

    /* 获取对象长度 */
    getObjectlength: function(obj) {
        var len = 0;
        for(var i in obj) {
            len++;
        }
        return len;
    }
};

/* 无刷新提交表单组件 */
var AjaxForm = function(form_dom) {
    this.form_dom = form_dom;
};
AjaxForm.ajaxFormCache = []; // 无刷新提交的表单的记录数组
AjaxForm.prototype = {
    // 通过ajax请求
    submitByAjax: function() {
        var form_dom = this.form_dom,
            url = form_dom.getAttribute("action"),
            formData = new FormData(form_dom);
        util.ajax({
            url: url,
            method: "post",
            data: formData,
            success: function(data) {
                console.log("data: " + data);
            }
        });
    },

    // 通过将请求指向iframe
    submitByIframe: function() {
        var form_dom = this.form_dom,
            body_dom = document.getElementsByTagName("body")[0],
            iframe = document.createElement("iframe"),
            formName = "ajaxForm" + AjaxForm.ajaxFormCache.length;

        iframe.setAttribute("name", formName);
        iframe.setAttribute("src", "about:blank");
        iframe.style.display = "none";

        AjaxForm.ajaxFormCache.push(formName);

        body_dom.appendChild(iframe);
        form_dom.setAttribute("target", formName);
    },

    init: function() {
        var __this = this;
        if(window.FormData) {
            this.form_dom.addEventListener("submit", function(e) {
                var event = e || window.event;
                event.preventDefault || event.preventDefault();
                __this.submitByAjax();
                return false;
            });
        } else {
            this.submitByIframe();
        }
    }
};

AjaxForm.init = function(form_dom) {
    // 创建无刷新表单实例
    var ajaxForm = new AjaxForm(form_dom);
    ajaxForm.init();
    return ajaxForm;
};

调用实例:

html:

<form name="form1" id="form1" method="post" action="/test">
    <input type="text" name="username" value="dzr">
    <input type="file" name="avatar">
    <input type="submit" value="提交">
</form>

<form name="form2" id="form2" method="post" action="/test">
    <input type="text" name="username" value="dzr">
    <input type="file" name="avatar">
    <input type="submit" value="提交">
</form>

JavaScript:

var form1 = document.getElementById("form1"),
    form2 = document.getElementById("form2");
AjaxForm.init(form1);
AjaxForm.init(form2);

好的。搞掂。

亲测可完美兼容IE6+ ~ o(≥v≤)o~

本文作者:子匠_Zijor,转载请注明出处:http://www.dengzhr.com/frontend/html/1207