【问题标题】:Elegant way of extracting an object from a cross-domain script从跨域脚本中提取对象的优雅方式
【发布时间】:2013-04-23 13:47:39
【问题描述】:

为了加载一些 geojson 数据,我需要在外部域上获取脚本,例如 http://www.stat.ucla.edu/~jeroen/files/la_county_simplified.min.json。我无法控制这个脚本的内容;我只知道 url,以及我感兴趣的脚本中定义的对象的名称。脚本的虚拟版本如下所示:

var my_data = {"foo" : 123, "bar" : 456}

现在在我的应用程序中,我想从其 URL 动态加载 my_data 对象。因为是跨域的,所以不能用ajax。它也不完全是jsonp,因为我的脚本定义了一个对象,而不是一个函数。一种方法是将其简单地作为<script> 插入当前文档的头部。不过,我想避免可能的副作用。

什么是更清洁的解决方案?我正在考虑创建一个<iframe>,然后在 iframe 中插入<script> 标签,并在 iframe 加载后提取对象。但是我不确定这是一个可靠的方法,可以跨浏览器工作(特别是绑定回调以在 iframe 中加载脚本后提取对象)。

是否有一些库或标准解决方案可以在干净的页面中加载脚本,并将特定对象的副本提取到主页?我已经对 jQuery 有依赖,这样就可以了。

【问题讨论】:

  • 我认为你想要的是 JSONP,jQuery 支持 $.getJSON()、$.get 和 $.ajax 函数
  • 我认为这就是 jQuery 实现 JSONP 的方式。
  • jsonp 假定函数名作为填充。我的脚本有一个对象,而不是一个函数。
  • my_data 是固定名称吗?还是变量变量名?
  • 是变量名,但是被父应用知道。

标签: javascript jquery json cross-domain jsonp


【解决方案1】:

如果您打算执行此纯客户端操作并且无法格式化数据,则可以使用 JSONP 稍加改动。我们没有修改数据以适应回调,而是重新调整加载器以适应数据。

我们监听脚本的onload。当脚本加载时,变量现在应该在全局范围内,我们执行回调,将全局变量传递给我们的回调。

//your script loader
function loadData(url,varName,callback){

  var script = document.createElement('script');
  document.getElementsByTagName('head')[0].appendChild(script);

  //when the script loads, we pass in `window.my_data`
  script.onload = function(){
    callback.call(this,window[varName]);
  };
  script.src = url;

}

//using it
loadData('http://example.com/somefile.js','my_data',function(data){
  //executes when script is loaded, where data is `my_data`
});

这种方法的缺点是每次加载脚本时,都会将其加载到全局范围内,并且可能会发生冲突。

【讨论】:

  • 谢谢。在 iframe 中执行此操作会更安全吗?如果任何此类脚本包含无效内容或名称冲突,我想避免出现问题。
  • @Jeroen 如果文件位于另一个域中,则只有 CORS 或 JSONP 可以做到这一点。 AJAX 限定同域,帧限定同顶域。
  • @JosephtheDreamer 你打败了我,如果这个页面多次加载数据,你需要清理脚本元素和 onload 处理程序。 Jeroen:您不能使用 iframe,因为您的页面无法访问 iframe,因为它的来源与尝试访问它的页面不同。
  • @Jeroen 我更新了答案。我忘记了一种方法,但需要您的服务器作为您的“中间人”。
  • @JosephtheDreamer 我实际上正在考虑在当前域中创建一个带有 src 的 iframe。然后让 javascript 在iframe.contentDocument 中插入(跨域)脚本标签。这也会违反 SOP 吗?
【解决方案2】:

没有其他方法可以解决它,因为您必须击败相同的来源策略,您必须将脚本加载到新的脚本标签中,JSONP 也可以这种方式工作,但 jquery 会为您隐藏它。

无论是网站还是网站都有 cors 标头,如果网站没有 cors 标头,您可以在这里加载数据(不使用 jsonp,因为它不是 jsonp 格式):

function loadJS(url){
  var s=document.createElement("script");
  s.src=url;
  $(s).on("load",function(){
    console.log("var abvailable");//do something with the variable here
    $(s).remove();
  });
  document.head.appendChild(s);
}
loadJS("http://code.jquery.com/jquery-1.9.1.min.js");

【讨论】:

    【解决方案3】:

    iframe 方法应该可以正常工作:

    • 创建 iframe
    • 注入指向文件的脚本标签
    • 在脚本加载时,检索对象

    我能想到的唯一跨浏览器问题是您需要在现代浏览器中使用 addEventListener 并在旧 IE 中使用 attachEvent。

    这是将 iframe 用作沙盒的标准用法 - 如果我理解正确,您担心可能与全局变量名称发生冲突。

    [更新] 为了解决你的一些 cmets,这里是一些跨浏​​览器的代码:

    添加事件监听器:

    function addEvent(element,event,fn,capture){
    // capture defaults to false if omitted
    if (element.addEventListener) {element.addEventListener(event,fn,(capture||false));}
    // else for old IE
    else {element.attachEvent('on'+event,fn);}
    };
    

    访问 iframe 文档:

    function iframeDocument(ifr){
    var doc=ifr.contentWindow||ifr.contentDocument;
    if (doc.document) doc=doc.document;
    return doc;
    };
    

    如果您使用 jQuery,.on("load")$(ifr).contents() 将解决这些跨浏览器兼容性问题。

    【讨论】:

      【解决方案4】:

      JSON-P 是一种从远程域加载 JavaScript 的方式。

      JavaScript 的返回格式是以响应数据为参数调用函数。

      someGlobalFunctionName({/* your response data */});
      
      function someGlobalFunctionName(data) { /* do something with data */ }
      

      由于数据包含在对象中并传递给函数,因此除了全局函数本身之外,没有全局泄漏,这是不可避免的。

      更多信息:http://json-p.org/

      【讨论】:

      • 外部脚本不包含函数。它定义了一个对象。我无法修改脚本的内容。
      • @Jeroen 您应该要求所有者使资源 JSON-P 兼容。如果该脚本是为跨域使用而设计的,那么这应该是没有道理的。
      猜你喜欢
      • 2012-01-17
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      • 1970-01-01
      • 2012-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多