新手在练习Ajax的时候经常会出现“莫名其妙”的bug,代码都对,但是测试的时候控制台报错,原因可能就是测试文件路径有问题,导致了Ajax的跨域问题。跨域问题在开发过程中并不是很常见,但是还是有的,总结下常见的跨域访问的方法:
一、利用JSONP
<script type="text/javascript"> function handler(json-data) { //对获得的json数据进行处理 } </script> <script src="http://example.com/request.php?callback=handler"></script>
PS: 参数名可以不叫callback,但是一般都叫这个。
第二个script标签返回的内容会被当做JavaScript内容处理,所以服务器端可以这样写
<?php $callback = $_GET['callback']; // 获得回调函数 $data = array('a','b','c'); // 数据 echo $callback.'('.json_encode($data).')'; // 拼接输出 相当于输出JS:callback(json-data); ?>
这样就能处理数据了。
jQuery可以这样写:
<script type="text/javascript"> $.getJSON('http://example.com/request.php?callback=?', function(json-data) { //处理数据 }) </script>
二、利用Window.domain
如果主页是http://www.example.com/a.html页面,iframe是http://example.com/b.html页面就会存在跨域问题:
<script type="text/javascript"> function onLoad() { var iframe = document.getElementById('iframe'); var win = iframe.contentWindow; //这里能够获取到iframe的window对象,但是意义不大 var doc = win.document; //这里获取不到iframe的document对象 var doc = win.name; //同样获取不到windows对象的name ... } </script> <iframe src="http://example.com/b.html" onload="onLoad()" id="iframe"></iframe>
解决方法:
我们只要把http://www.example.com/a.html 和http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。
主页脚本:
<script type="text/javascript"> function onLoad() { document.domain = 'example.com'; function test() { alert(document.getElementById('iframe').contentWindow); } } </script> <iframe src="http://example.com/b.html" onload="test()" id="iframe"></iframe>
iframe脚本:
<script type="text/javascript"> document.domain = 'example.com'; </script>
三、利用window.name || location.hash
演示window.name,给name赋的值必须是字符串,而且大小不能太大,最大2M左右。如果需要传其他的数据可以转换成JSON字符串。
<!-- a页面的脚本,3s后跳转到b页面 --> <script type="text/javascript"> window.name = '页面a设置的值'; setTimeout(function() { window.location = 'b.html'; }, 3000); </script> <!-- b页面的脚本 --> <script type="text/javascript"> alert(window.name); //获取到window.name 的值 </script>
利用这个特性,同样认为所请求的页面数据和主页数据不同源:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>跨域</title> <script type="text/javascript"> function getData() { var iframe = document.getElementById('proxy'); iframe.onload = function() { var data = iframe.contentWindow.name;//iframe把数据放在了window.name里 alert(data); }; iframe.src = 'b.html';//不一定是b.html,只要和a.html同源即可,比如about:blank。这样iframe跳转到和a.html同源下,但是window.name没有发生改变,a.html就可以获取到! } </script> </head> <body> <!-- 不让iframe显现出来,iframe只是帮助跨域的 --> <iframe src="http://www.example.com/data.html" style="display: none;" onload="getData()" id="proxy"></iframe> </body> </html>
四、利用html5,postMessage()方法
<!-- http://test.com/a.html的代码 --> <script type="text/javascript"> function onLoad(){ var iframe = document.getElementById('iframe'); var win = iframe.contentWindow; win.postMessage('我是 a 页面'); } </script> <iframe src="http://www.test.com/b.html" id="iframe" onload="onLoad()"></iframe> <!-- http://www.test.com/b.html的代码 --> <script type="text/javascript"> window.onmessage = function(e) { e = e || event; alert(e.data); //也可以从iframe发到window }; </script>
<script type="text/javascript"> var img = new Image(); img.onload = img.onerror = function() { alert('OK'); }; //之所以能把onload和error写成同一个事件,因为这种跨域方式是单向的,只能从客户端向服务器端发送数据而客户端接受不到服务器端的数据 img.src = "http://www.example.com/test?name=kiscall"; </script>
七、Comet
Comet有两种实现方式,长轮询和流。
短轮询:
长轮询:
服务器实现流的方法:
<?php $i = 0; while(true) { echo "data"; flush(); sleep(10); $i++; } ?>
八、SSE和WebSockets就没时间介绍了!