ie8下用iframe解决表单submit以及二级域名跨域的方法

  • Post author:
  • Post category:其他


Q:使用multipart/form-data方式上传表单,并且能够获得后台返回的数据

前几个月做项目的时候遇到的这个问题,现在空下来了有时间可以好好总结一下。

那会用chrome开发,使用jquery.form插件可以很完美的解决这个问题。后期在ie8兼容性测试的时候,不知为什么这个插件总是会报错,从源代码里面调试,也找不到问题的具体原因何在。随着项目上线时间越来越近,心里也越来越急,于是最后决定抛弃这个插件,自己写一个实现。

当然最后找到了原因,jquery.form插件在ie8下使用时是用iframe实现的,访问时涉及到二级域名跨域,因此在访问子iframe里面的东西时直接被block了,然后就挂了。(渣渣ie =_=)

那两个域名是类似这样的:

top:

123456.cn


iframe:

yyyyy.aaa.123456.cn

具体本文开头的这个问题是怎么解决的,接下来一步步介绍。


-使用iframe屏蔽submit提交表单后的自动跳转

正常的情况下,要上传包括文件的数据需要使用form中的multipart/form-data。

<form method="post" action="/submitTest" enctype="multipart/form-data">....</form>

如果直接使用form表单里的submit提交给后台,submit之后,后台返回的数据会在前端新建一个页面,并显示在上面。但这样的体验并不友好。

利用在html5中XMLHttpRequest Level 2提供的FormData这个接口,可以实现不依赖submit上传包含文件的数据。

var formData = new FormData();
formData.append('username', '张三');
formData.append('id', 123456);
formData.append('file', file);  //file为通过file input选择的文件obj
xhr.send(formData);

只要一个个往里面塞包括二进制的数据,然后回调success就好了。jquery.form默认就是使用这种方法。它会先检查浏览器是否有fileapi以及Formdata这个方法。但是遇到不支持html5的浏览器,比如ie8(渣渣ie =_=),就会走另一种用iframe的方法。

使用iframe是一种经典的老方法。将form的target熟悉指向iframe的name,后台表单返回的数据就会在iframe里面出现。

<iframe src="" frameborder="0" name="iframe"></iframe>
    <form method="post" target="iframe" action="/submitTest" enctype="multipart/form-data">
    ....
    </form>

iframe显示出后台返回的数据

最后将iframe设置为不可见即可

<iframe style="display:none;" src="" frameborder="0" name="iframe"></iframe>

有一点需要提醒一下。在ie中,需要将头设置为Content-Type:text/html,否则iframe里面接收到的是404,略蛋疼。


-使用onload获取后台返回在iframe中的数据

另一方面,我们需要得到后台返回的数据。一般使用的方式是在ajax的时候通过success回调,但明显用iframe不能直接用这样的方式。

iframe相当于一个新的窗口,有个属于自己的window对象。可以利用它的onload方法,当后台刷新iframe里面的数据时,用在父页面使用iframe的onload方法捕获这个动作,然后读取它的body就可以了。

var iframeEle = document.getElementsByName("iframe")[0];
iframeEle.onload = function(){
    console.log(document.iframe.window.document.body.innerHTML);
};

之后拿到后台数据该干嘛干嘛- –

在我实际的项目中额外多遇到了一个问题,就是开头提到跨域的问题。所以到这一步之后,还要多做一点工作。


-通过后台返回js代码,解决跨域

由于后台使用的域名为

yyyyy.aaa.123456.cn

,而主域用的是

123456.cn

,直接返回数据会引起跨域的问题。解决思路就是将iframe的domain设置为

123456.cn

。但是由于跨域,不能从父页读取iframe,也不能修改其内容,因此需要用另一种方法实现。

后台返回的数据头中的Content-Type有几种类型,作用是告诉前端这些数据的类型,常见的有

text/plain
text/javascript
text/html
text/json
application/json
...

其中text/html为html语言,前端会将它当做html来解析。

res.set({'Content-Type':'text/html'});

这时我们可以加一个script标记写自己的脚本,标记之外放后台传来的数据。

由于返回的这些代码是直接在iframe上跑起来的,为了解决这个跨域的问题,我们可以这样设计后台返回回来的内容:

<script>document.domain='123456.cn'</script>
{"code":"s_ok","var":[{"name":"jone","age":123}]}

这么操作之后,父页就可以无阻碍的访问子iframe,不受到浏览器的阻拦了。

顺带一提,jquery.form插件在后台返回这段脚本之后,也没有了本文开头说的那些问题。说明我当初遇到的那个问题确实是由于跨域的限制引起的,导致在ie8下jquery.form挂掉的原因。



版权声明:本文为liyijun4114原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。