尝试动态加载API和JS文件

8
我正在尝试动态加载Skyscanner API,但似乎无法正常工作。我尝试了我能想到的所有可能的方法,但结果都是内容消失了。
我尝试了console.log,但没有结果;我尝试了chrome开发者工具中的元素,虽然所有内容的css保持不变,但内容仍然消失了(我认为可能是在html/body上添加了display:none之类的)。我尝试了Google的所有异步技巧,但仍然是空白页。我尝试了所有用于异步加载的js插件,但结果仍然相同。
Skyscanner的API文档很差,虽然他们提供了回调函数,但它的工作方式与Google的API回调不同。
例如:http://jsfiddle.net/7TWYC/ 在head部分加载API的示例:http://jsfiddle.net/s2HkR/ 那么,我如何在单击按钮或异步方式下加载API?而不在HEAD部分放置文件。如果有一种方法可以防止document.write使页面变空白或其他任何方式,我不介意使用纯js、jQuery或PHP。

编辑:

我已经将赏金增加到250,之前是50。

Orlando Leite提供了一个非常接近的想法来使异步API加载,尽管一些功能无法正常工作,例如选择日期和设置样式。

我正在寻找一个答案,可以使用所有功能,以便它能够像在加载时一样工作。

这是Orlando更新的fiddle链接: http://jsfiddle.net/cxysA/12/

-

Gijs答案的第二次编辑:

Gijs提到了两个关于覆盖document.write的链接。这听起来是个很棒的想法,但我认为我所尝试的是不可能实现的。

我使用了John Resig的方法来防止document.write,可以在这里找到:http://ejohn.org/blog/xhtml-documentwrite-and-adsense/

当我使用这种方法时,我成功地加载了API,但snippets.js文件根本没有加载。

Fiddle:http://jsfiddle.net/9HX7N/


看起来脚本根据 API 密钥加载另一个脚本。很奇怪,当我点击按钮时,它会清除 body 标签 o.O - Eliseu Monar dos Santos
我喜欢你的用户名是jQuerybeast,声望值为666。你在这里的工作完成了。 - ThinkingStiff
@thinkingstiff 通过了!你对这个问题有什么线索吗? - jQuerybeast
3个回答

5

我相信你想要的是这个:

function loadSkyscanner()
{
    function loaded()
    {
        t.skyscanner.load('snippets', '1', {'nocss' : true});

        var snippet = new t.skyscanner.snippets.SearchPanelControl();
        snippet.setCurrency('GBP');
        snippet.setDeparture('uk');
        snippet.draw(document.getElementById('snippet_searchpanel'));
    }

    var t = document.getElementById('sky_loader').contentWindow;
    var head = t.document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.onreadystatechange= function() {
        if(this.readyState == 'complete') loaded();
    }
    script.onload= loaded;
    script.src= 'http://api.skyscanner.net/api.ashx?key=PUT_HERE_YOUR_SKYSCANNER_API_KEY';
    head.appendChild(script);
}

$("button").click(function(e)
{
    loadSkyscanner();
});

在iframe#sky_loader中加载Skyscanner,然后调用loaded函数创建SearchPanelControl。但最终,片段会在主文档中绘制。这确实是一个奇怪的解决方法,但它有效。
唯一的限制是需要一个iframe。但你可以使用display:none来隐藏它。 一个工作示例 编辑
抱歉,我没有看到它。现在我们可以看到Skyscanner API有多糟糕。它放置了两个div来制作自动完成,但与您调用绘制的元素无关,而是文档。 当在iframe中加载脚本时,文档就是iframe文档。
有一个解决方案,但我不建议使用,因为它真的是一个解决方法:
    function loadSkyscanner()
{
    var t;
    this.skyscanner;
    var iframe = $("<iframe id=\"sky_loader\" src=\"http://fiddle.jshell.net/orlleite/2TqDu/6/show/\"></iframe>");

    function realWorkaround()
    {
        var tbody = t.document.getElementsByTagName("body")[0];
        var body = document.getElementsByTagName("body")[0];

        while( tbody.children.length != 0 )
        {
            var temp = tbody.children[0];
            tbody.removeChild( temp );
            body.appendChild( temp );
        }
    }

    function snippetLoaded()
    {
        skyscanner = t.skyscanner;

        var snippet = new skyscanner.snippets.SearchPanelControl();
        snippet.setCurrency('GBP');
        snippet.setDeparture('uk');
        snippet.draw(document.getElementById('snippet_searchpanel'));

        setTimeout( realWorkaround, 2000 );
    }

    var loaded = function()
    {
        console.log( "loaded" );
        t = document.getElementById('sky_loader').contentWindow;

        t.onLoadSnippets( snippetLoaded );
    }

    $("body").append(iframe);
    iframe.load(loaded);
}

$("button").click(function(e)
{
    loadSkyscanner();
});

加载另一个HTML的iframe,并在片段加载完成后回调。加载完成后在所需位置创建片段,并设置超时,因为我们无法知道SearchPanelControl何时加载。此“realWorkaround”将自动完成div移动到主文档中。 您可以在此处查看工作示例 加载的iframe是这个 编辑
修复了您发现的错误并更新了链接。
for循环已被删除并添加了while,现在效果更好。
    while( tbody.children.length != 0 )
    {
        var temp = tbody.children[0];
        tbody.removeChild( temp );
        body.appendChild( temp );
    }

这似乎是我正在寻找的,尽管请注意自动完成不起作用? 请在此处检查:http://jsfiddle.net/s2HkR/ 这是加载API的默认方式。 注意当您输入城市时搜索表单的自动完成。 在您的工作示例中,该功能已停止工作。 不过,您的想法很聪明,我不介意隐藏iframe。谢谢。 - jQuerybeast
太棒了。虽然你不建议,但我认为我没有其他解决方案,对吧?这是一个仪表板 Web 应用程序,当他们的服务器延迟时,所有的仪表板也会变得很卡顿。 - jQuerybeast
好的,我已经试了一个小时了,还是没搞清楚。我以为这是我的页面的问题,但我刚刚发现在你的示例上也不起作用。看看这个:http://jsfiddle.net/orlleite/cxysA/7/show/ 如果你单独查看你的 fiddle,它什么都不显示。(这个可以工作:http://fiddle.jshell.net/orlleite/cxysA/7/show/)再次感谢 - jQuerybeast
它必须使用相同的域名吗?这很令人困惑。 http://jsfiddle.net/orlleite/cxysA/7/show/和http://fiddle.jshell.net/orlleite/cxysA/7/show/完全具有相同的源代码,但其中一个有效而另一个无效。在我的情况下,它们位于同一个目录但不同的文件中。 - jQuerybeast
我的意思是,如果您更改iFrame源代码:http://fiddle.jshell.net/HCrQM/,将其从fiddle.jshell更改为jsfiddle。 - jQuerybeast
显示剩余7条评论

3
对于这种问题,你可以直接覆盖document.write。虽然有点瑕疵,但它能够起作用,并且您可以决定所有内容的位置。请参见John Resig的此博客文章。这忽略了IE,但是通过一些工作,这个技巧也可以在IE中使用,请参见此博客文章
所以,我建议用自己的函数覆盖document.write,必要时批量输出,并将其放在您喜欢的位置(例如,在您的<body>底部的div中)。这应该可以防止脚本删除页面的内容。
编辑:好吧,我花了一些时间来研究这个脚本。为了以后参考,使用类似http://jsbeautifier.org/的工具来调查第三方脚本。那样更容易读。幸运的是,几乎没有混淆/缩小,因此您可以得到他们API文档的补充(顺便说一句,我找不到他们的API文档,我只找到“代码向导”,但我对此不感兴趣)。
这是一个几乎可以工作的示例:http://jsfiddle.net/a8q2s/1/ 我所采取的步骤如下:
覆盖document.write。这需要在加载初始脚本之前发生。您的替换函数应将其代码字符串附加到DOM中。不要调用旧的document.write,那只会给您带来错误,并且无论如何都不会做您想要的事情。在这种情况下,您很幸运,因为所有内容都在单个document.write调用中(检查初始脚本的源代码)。如果不是这种情况,您将不得不批量处理所有内容,直到他们给出的HTML有效和/或您确定没有其他内容出现。
使用jQuery的$.getScript或等效方法在按钮单击时加载初始脚本。传递回调函数(我使用了命名函数引用以便更清晰,但如果您喜欢,也可以内联它)。
告诉Skyscanner加载模块。
编辑#2:哈,他们有一个API(skyscanner.loadAndWait),可以在他们的脚本加载后获取回调。使用它可以工作:http://jsfiddle.net/a8q2s/3/(注意:这似乎仍然在内部使用超时循环)

我正在努力解决这个问题。如果我能够弄清楚,我会是最幸福的人。 - jQuerybeast
我认为这是不可能的。您必须先加载API,然后加载代码片段,然后设置回调。如果在代码片段之前停止document.write,则代码片段不会加载。反之亦然,如果您首先加载代码片段,则页面将变空白。请查看我的fiddle:http://jsfiddle.net/bLmer/ - jQuerybeast
基本上我正在寻找一种方法,它将允许新的document.write,但防止清除页面。 - jQuerybeast
好的,我已经添加了一个可工作的示例。目前看起来效果不太好,但我猜测这是因为你的 CSS 还没有应用?此外,我目前只在 Chrome 浏览器中进行过测试。在其他浏览器中可能会有所不同,不提供任何保证,等等。 - Gijs
我用代码美化工具查看了他们的脚本。我是一个页面加载性能狂热者,因此我有这个问题。现在针对您的答案,先生,我真的不知道该如何感谢您。一个多月来,我一直在思考如何解决这个问题。我尝试了我所能想到的一切。非常感谢您! - jQuerybeast

3
skyrunner.js文件中,他们使用document.write来使页面在加载回调时变空白... 因此,在您的场景中会有一些后果。
  1. 当你点击按钮时,它会使页面变成空白。
  2. 因此,它会删除页面上的所有内容,包括'jQuery.js',这就是为什么回调不起作用的原因...也就是说,主函数不能被调用,因为它是使用jQuery编写的。
  3. 另外,我注意到你错过了一个目标'div'标签,id = map(根据代码)。实际上,这是地图加载的目标。
  4. 我还观察到,当前上下文中的地图实际上不是一个div,而是地图api的加载。

在这里,您必须采用老派的方法,即..应该将skyrunner.js文件包含在头部内容的顶部。

因此,请尝试下载该文件并将其包含在head标记中。

谢谢


我注意到了。我的问题是不要在头部使用skyscanner.js,因为有时它会很卡顿,导致整个webapp都很卡顿。 - jQuerybeast
地球上有没有任何方法(PHP,AJAX),可以在按钮点击时加载它? 有什么可以说,取消所有document.write页面空白的东西吗? - jQuerybeast

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接