jsonp封装方法二
前言: 看到玉伯的聊聊jsonp的p,引发了另一种loader方式来跨域的方法,他把它叫做JSONM协议,原理和seajs相似,都是动态加载script,加载完成后执行callback,
同时还不用考虑回调函数名,都指定为define,服务端可以静态存储例如(define({name:"alice",age:21}))。如果数据是静态的,还可以放在cdn上。原文issue连接
以及一篇原理分析,写得很赞 连接。
所以我就斗胆按照自己的理解,自己写了一个简易的用loader的思想来完成jsonp。
原理概述:动态加载script,利用onload事件执行回调函数,在ie9/10中 用onreadystatechange。可是在ie6-8中不支持,在seajs里看到了绝招,利用readyState为interactive判断正在执行的脚本,脚本的src和传入时的callback建立唯一连接,进行查找,执行回调函数。
那么怎么获取json数据呢? 我用JSONPData这个里存储获取到的_jsondata(私有变量,提供接口访问)。在非ie中就简单了,直接callback.call(this,JSONPData.getJsonData()).
我实现的这个有什么限制呢?
在ie6-8中,因为用的是src和callback唯一对应,假若几个请求的src相同,callback只能得到第一次设置的函数,解决方法,如果回调函数不同,就用不同的src请求喽,加个无意义的参数就ok啦。
var currentAddingScript;
var JSONP = (function() {
var _head = document.getElementsByTagName("head")[0];
var _baseElement = document.getElementsByTagName("base")[0];
var _helper = {
encodeURL: function(url) {
var param = url.split("?")[1];
var url = url.split("?")[0];
var params = param.split("&");
var parLen = params.length; for (var i = 0; i < parLen; i++) {
var paItem = params[i].split("=");
url += url.indexOf("?") >= 0 ? "&" : "?";
url += encodeURIComponent(paItem[0]) + "=" + encodeURIComponent(paItem[1]);
}
return url;
},
addLoad: function(node, callback) {
node.onload = node.readystatechange = function() {
// Ensure only run once in IE9
node.onload = node.readystatechange = null;
if (!this.readyState || node.readyState == "complete" || node.readyState == "loaded") {
callback.call(this, JSONPData.getJsonData());
this.parentNode.removeChild(this);
}
}
}
};
return {
start: function(url, func) {
var encUrl = url;//存储的是未经过编码的,以便与对应的callback创建关联
//创建script结点
var script = document.createElement("script");
if (encUrl.indexOf("?") >= 0) {
url = encUrl + "&callback=JSONP.callback";
script.src = _helper.encodeURL(url);
} else {
url = encUrl + "?callback=JSONP.callback";
script.src = _helper.encodeURL(url);
}
script.charset = "UTF-8"; //绑定加载完成事件
_helper.addLoad(script, func); // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
// the end of the insert execution, so use `currentlyAddingScript` to
// hold current node,
currentAddingScript = script;
_baseElement ? _head.insertBefore(script, _baseElement) : _head.appendChild(script); //for ie6-9 关联script 和callback
var data = {};
data[url] = func;
JSONPData.save(data);
},
getInteractiveScript: function() {
//ie6-9 获取正在执行的脚本 if (currentAddingScript) {
return currentAddingScript;
}
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
var script = scripts[i];
if (script.readyState === 'interactive') {
return script;
}
}
return null;
},
callback: function(data) { // if ie6-9
if (document.attachEvent) {
var script = JSONP.getInteractiveScript();
var callback = null;
if (script) {
callback = JSONPData.findCallback(script.src);
if (typeof callback == "function") {
callback(data);
}
} else {
console.log("fail");
}
}
// 设置返回的json数据,供调用
JSONPData.setJsonData(data);
}
}
})(); var JSONPData = (function() {
var _dataStorage = [];
var _jsonData; // 每个数据项是data {url: [callbackFunc]}
return {
save: function(data) {
//检查data的url若相同,则回调函数不变
for (var i in data) {
if (!_dataStorage.hasOwnProperty(i)) {
_dataStorage.push(data);
}
}
},
findCallback: function(url) {
var len = _dataStorage.length;
for (var i = 0; i < len; i++) {
if (_dataStorage[i][url]) {
return _dataStorage[i][url];
}
}
return null;
},
setJsonData: function(data) {
_jsonData = data ? data : null;
},
getJsonData: function() {
return _jsonData;
}
}
})();
update分割线
==========================================================================================
我用Ie的仿真模式,测试ie7和ie8时,发现第一次点击按钮触发请求不能返回数据,请先看重要的html部分
<button id="button">发送三个jsonp请求</button>
绑定button点击事件
var button = document.getElementById("button");
button.onclick = function() {
JSONP.start("http://localhost:8088/data.php", function(data) {
console.log(data);
console.log("yes1");
});
JSONP.start("http://localhost:8088/data.php?cc=ff", function(data) {
console.log(data);
console.log("yes2");
});
JSONP.start("http://localhost:8088/data.php?ur=f", function(data) {
console.log(data);
console.log("yes3");
});
}
在ie7,8下我点一下后不出现结果,

通过断点调试,代码问题出在了这段上,我先把创建的script结点添加到dom结点中,之后再存储url和callback,导致在立即执行函数时,数据还未存储,无法找到关联的callback,所以没有成功调用,解决方案是,存储数据放在插入节点之前,就完美啦!

再看运行结果:

新代码:就只贴了需要做小改动得JSONP返回的start函数代码:
start: function(url, func) {
var encUrl = url;
//创建script结点
var script = document.createElement("script");
if (encUrl.indexOf("?") >= 0) {
url = encUrl + "&callback=JSONP.callback";
script.src = _helper.encodeURL(url);
} else {
url = encUrl + "?callback=JSONP.callback";
script.src = _helper.encodeURL(url);
}
script.charset = "UTF-8";
//for ie6-9 关联script 和callback
var data = {};
data[url] = func;
JSONPData.save(data);
//绑定加载完成事件
_helper.addLoad(script, func);
// For some cache cases in IE 6-8, the script executes IMMEDIATELY after
// the end of the insert execution, so use `currentlyAddingScript` to
// hold current node,
currentAddingScript = script;
_baseElement ? _head.insertBefore(script, _baseElement) : _head.appendChild(script);
},
jsonp封装方法二的更多相关文章
- getElementsByClassName 方法兼容性封装方法二
var getElmsByClsName = function(className, results) { results = results || []; // 判断浏览器是否支持 getEleme ...
- Selenium应用代码(常见封装的方法二)
滚动窗口: //将滚动条滚到适合的位置 , 方法一 public static void setScroll(WebDriver driver,int height){ try { // String ...
- MP实战系列(十二)之封装方法详解(续二)
继续MP实战系列(十一)之封装方法详解(续一)这篇文章之后. 此次要讲的是关于查询. 查询是用的比较多的,查询很重要,好的查询,加上索引如鱼得水,不好的查询加再多索引也是无济于事. 1.selectB ...
- iOS开发——开发必备OC篇&UITableView设置界面完整封装(二)
UITableView设置界面完整封装(二) 简单MVC实现UITableView设置界面之Cell右边类型设置 首先来看看第一种方法证明使用,结合两种方法之后根据个人的爱好去选择就可以了, 一:使用 ...
- SSH框架的多表查询(方法二)增删查改
必须声明本文章==>http://www.cnblogs.com/zhu520/p/7773133.html 一:在前一个方法(http://www.cnblogs.com/zhu520/p ...
- MP实战系列(十一)之封装方法详解(续一)
之前写的封装方法详解,比较简要. 今天我主要讲增加和删除及其修改.查的话得单独再详讲. 增删改查,无论是Java或者C#等等,凡是对数据库操作的都离不开这四个. 一.增加方法讲解 MyBatis Pl ...
- .Net基础——程序集与CIL HttpClient封装方法 .Net Core 编码规范 C#中invoke和beginInvoke的使用 WebServeice 动态代理类
.Net基础——程序集与CIL 1. 程序集和CIL: 程序集是由.NET语言的编译器接受源代码文件产生的输出文件,通常分为 exe和dll两类,其中exe包含Main入口方法可以双击执行,dll ...
- SSH框架的多表查询(方法二)
必须声明本文章==>http://www.cnblogs.com/zhu520/p/7773133.html 一:在前一个方法(http://www.cnblogs.com/zhu520/p ...
- 八、MD5加密并封装,并调用封装方法
一.MD5加密 封装Md5 public class Md5 { //十六进制下数字到字符的映射数组 private static final char hexDigits[] = { '0', '1 ...
随机推荐
- HTML 5 <input> placeholder 属性 实现搜索框提示文字点击输入后消失
H5之前要实现这个功能还要用到JS,H5出来之后新增加了placeholder属性,有了这个属性就就能轻松实现这个功能. 定义和用法 placeholder 属性提供可描述输入字段预期值的提示信息(h ...
- parseInt OR Number进行数字的转换
在js中,字符串转为数字类型是比较常见的,平时用的比较多的是parseFloat和parseInt这两个方法.当然,除了这个方法之外还有一个Number:都是转为数字类型,有什么差别? 可以简单的说N ...
- <Android 基础(二十六)> 渐变色圆角Button
简介 总结下之前看的自定义View的内容,结合一个简单的例子,阐述下基本用法和大致的使用流程,这个例子比较简单,更复杂的自定义View,随着自己的学习,后面再慢慢添加.作为一个Android开发者,这 ...
- .NET源代码已经下载,潜心研读…
有兴趣的弟兄可以从这里下载:http://referencesource.microsoft.com
- 使用99元一年的256MB高性能阿里云Redis加速Discuz论坛
介绍 Discuz是一个常见的论坛,支持使用Redis来对论坛进行加速访问,对于访问量比较大的论坛能够取到很好的作用,本文介绍如何使用阿里云高性价比256MBRedis来加速该论坛. 阿里云Redis ...
- Flutter Stack布局中定位的方式
前言 想要记录一下Stack布局中,定位的两种方式 代码 //……省略无关代码…… child: new Column( children: <Widget>[ new SizedBox( ...
- APP之红点提醒三个阶段
下面这个页面就是我们进入APP后的主界面.客户选项的红点上数字就是显示我们没有查看的客户总数量. 当我们切换到客户这个fragment时,会显示贷款客户数量与保险客户数量. 当我们随便点击入一 ...
- Ubuntu做Tomcat服务:insserv: warning: script 'tomcat' missing LSB tags and overrides
https://blog.csdn.net/hanchao5272/article/details/79819460 转载自:https://blog.bbzhh.com/index.php/arch ...
- 转:MVC框架
MVC框架是什么 MVC (Modal View Controler)本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器.使用MVC的目的是将M和V的实现代码分离,从而使 ...
- 测试SDWebImage淡入淡出效果在UITableView中的重用显示问题
测试SDWebImage淡入淡出效果在UITableView中的重用显示问题 这个是在上一篇教程的基础上所添加的测试环节! 效果图(从效果图中看是没有任何重用问题的): 源码: ImageCell.h ...