最近在做項目的時候遇到一個需要上傳文件的需求,因為ajax請求是無法上傳二進制文件流的,所以只能用form表單提交,而form提交有一個問題就是會使頁面刷新,本文解決了form表單提交文件時頁面刷新的問題。
一、無刷新實現form提交文件
將form的target指向為一個iframe就可以實現無刷新提交文件了,但關鍵是還需要看到后台返回的數據,所以還需要為該iframe注冊一個回調函數,因為iframe和該頁面在同域內,所以可以在iframe里可以調用該回調函數,就可以看到后台返回的數據了。實例如下:
<body> <form method="post" target="targetFrame" action="#" enctype="multipart/form-data" id="fileupId"> <input type="file" name="" id=""> </form> <script> var ifmId = 'targetFrame'; var iframe = document.createElement("iframe"); document.body.appendChild(iframe); iframe.style.display = "none"; iframe.contentWindow.name = ifmId; iframe.id = ifmId; iframe.callback = function(json) { success && success(json) } </script> </body>
里面的js的作用主要是創建了一個iframe,並為iframe設置id和name,並為其注冊回調函數。另外后台需要注意的有兩點:
1.后台返回的數據類型應為html,否則無法在iframe里面顯示,舉個栗子:<html><script>frameElement.callback({test:'test'})</script></html>
,其中{test:test}應該是后台返回的數據;
2.ngix的http頭應將x-Frame-Option設置為SAMEORIGIN,這樣使頁面可以在同域下能夠被iframe調用;
這樣一來,一個可以不刷新頁面上傳文件的功能就做好了,但是我們整個的系統都是ajax完成的,如果臨時改成form可能改變很大,那么有沒有有個方法可以不用一個個的去改,直接用一個方法就可以將所有的ajax上傳的內容都改成form呢
二、動態轉換所有數據到form表單內,並實現提交;
這個方法總的來說分為三步:1.在打開頁面的時候遍歷所有需要提交的輸入框或者文件上傳等內容,並將其每一個添加到動態創建的form表單之中;2.將其中的上傳文件按鈕綁定表單中的文件上傳input;3.當用戶點擊提交的時候自動填充除了文件外其他的form表單,並提交表單,獲取返回數據。
直接看代碼:
var newForm = function(){ var conds = $('[data-cond]'); var formDom = $('<form method="post" style="display:none;" enctype="multipart/form-data" id="fileupId"></form>') var textDom = $('<input type="text">'); var fileDom = $('<input type="file">'); conds.each(function(i, ele) { var _ele = $(ele); var key = _ele.data('cond').toString().trim(); if (_ele.hasClass('select')) { //下拉框 formDom.append($('<input type="text">').attr('name',key)); }else if (_ele.hasClass('fileUp')) { //文件 formDom.append($('<input type="file">').attr('name',key)); _ele.on('click',function(){ $("input[name="+ key +"]").click(); }) }else{ formDom.append($('<input type="text">').attr('name',key)); } }); $('body').append(formDom); formDom.delegate('input','change',function(){ var id = $(this).attr('name'); var files = $(this).get(0).files[0]; //判斷文件類型 if(!/\.jpg$|\.jpeg$|\.gif|\.png$/ig.test(files.name)){ alert('請選擇圖片文件~') return false; } //判斷文件大小 if(files.size > 20480000){ alert('請上傳20M內的文件~') return false; } var name = files.name.replace(/(\w{10})(\w+)/,'$1..') //文件名篩選只顯示前10個字符 $('#'+id+'').text(name); }) //formDom.hide(); }
這個方法的目的主要是為了動態創建一個表單,並為html文件中的上傳文件按鈕與form內的上傳文件輸入框綁定,實現選擇文件的功能,另外還用正則實現了文件類型、大小的篩選並選擇性顯示文件名的前十個字符,其中$('#'+id+'')就是相應的上傳文件按鈕,另外為想轉換為form表單內的內容的dom添加標簽[data-cond="xxx"],通過判斷它的類來添加不同的Input。
var form = function(opt){ console.log() var dom = opt.dom; var success = opt.success || function() {}; var preUrl = eking.global.preUrl; var postUrl = opt.postUrl || ""; var iframeName = opt.iframeName; var conds = $('[data-cond]'); var consObj = {}; conds.each(function(i, ele) { var _ele = $(ele); var key = _ele.data('cond').toString().trim(); if (_ele.hasClass('select')) { //下拉框 $("input[name="+ key +"]").val(_ele.data('select').getValue()); }else if (_ele.attr('type') == "text") { //文本框 $("input[name="+ key +"]").val(_ele.val()); }else if (_ele.attr('type') == "password") { //密碼框 $("input[name="+ key +"]").val(_ele.val()); }else if (_ele.hasClass('span')){ $("input[name="+ key +"]").val(_ele.text()); } }); dom.attr({ target: iframeName, action: preUrl + postUrl }); var ifmId = 'targetFrame'; var iframe = document.createElement("iframe"); document.body.appendChild(iframe); iframe.style.display = "none"; iframe.contentWindow.name = ifmId; iframe.id = ifmId; iframe.callback = function(json) { success && success(json) $("#targetFrame").remove(); } }
第二個函數就和我們一開始介紹的方法類似,不過將其進行了封裝,添加了幾個參數dom、success、preUrl、postUrl、iframeName,其中兩個url是為了設置form表單提交的地址,dom則是在第一個函數中創建form表單,success則是數據傳輸后的回調函數。
在目標頁面剛打開時調用第一個函數,當用戶點擊提交時,調用第二個函數就可以實現轉換輸入內容到form表單中並進行提交 :)