realazy


使用 iframe 获取网页片段的一个好处

异步操作数据的方式有两种常见的方式:XMLHttpRequestiframe. 孰优孰劣在此我们不争论,只是想举一个例子说明在获取网片片段上,使用 iframe 有一个比 XMLHttpRequest 更易企及的好处。

Ajax 常见的一种使用方法是加载网页片段填充某个区域。假设我们要在网页 foo.com/index 上请求 foo.com/partial。我们假设 partial 就是 HTML,不涉及 JSON 或 XML 格式。在这种情况下:

  1. 使用 XMLHttpRequest 直接访问 partial,获取 responseText 后赋予 index 页面上某个元素的 innerHTML 即可完成。partial 必须是一个纯粹的 HTML 片段(基于以上假设)。
  2. 在页面上创建一个隐藏的 iframe, 使用 iframe 的 src 请求 partial, partial 可以作为一个完整的页面,里面包含 JavaScript,由 partial 里的 JS 完成替换 index 上页面片段替换。

第二种看起来更繁琐,但能给我们更多控制权。例如,假如用户直接访问 foo.com/partial(这种情况很容易发生,假设您是 unobtrusive 的拥护者,机会更大,例如需要点击网页上的链接更新某部分内容时,用户使用新窗口打开了改链接), 你希望她看到的是什么内容呢?

在第一种情况中,用户看到的是代码片段,绝大部分下没有任何样式,也没有任何额外提示,导致用户体验的下降。因为只是一个 HTML 片段,你什么事都干不了。

但在第二种情况下,用户看到内容可能也只是 HTML 片段,但这却是一个完整的页面,一个可以执行 JS 的完整页面。我们只需检查这个页面的 parent 对象有没有我们预设的值,就可以判断它是不是在 iframe 之内了,然后我们可以让它跳转到正常的页面。

Demo: http://realazy.org/lab/xhrvsiframe/

21 Responses to “使用 iframe 获取网页片段的一个好处”

  1. dexbol Says:

    今天心血来潮,抢个沙发(好像这是一次做这么蠢的事)。

  2. Jerry Qu Says:

    有道理!
    不过用iframe也有不好之处:样式/脚本需要额外链入,会增加请求。
    另外用js防盗链只防得了小偷,防不了大盗。

  3. 神仙 Says:

    这个还是要去同一域名下的

  4. nickcheng Says:

    第一种情况的页面也是可以像第二种情况那样判断用户是否为直接访问的.

  5. winsteps Says:

    刚在项目中遇到过这个问题,用iframe加载的确有其好处:比如隔离javascript框架、作用域等,也没有绝对的好坏,用哪个得具体情况具体分析。

  6. Sparkle Says:

    汗,这个好处,用方法一更好实现

    调XMLHttpRequest的时候,随便传一个额外的header,后台判断header是否存在作出不同的相应。想要正规点,就按照REST的做法,传Accept: application/x-javascript

  7. realazy Says:

    @Jerry Qu 我想你误解了文章的意思。

    @Sparkle 或许我举的例子不合适。我想说明的是使用 iframe 有一个完整的 js 执行环境,比使用 o.responseText 更容易控制。

  8. hax Says:

    “假如用户直接访问 foo.com/partial……假设您是 unobtrusive 的拥护者……用户看到的是代码片段,绝大部分下没有任何样式,也没有任何额外提示,导致用户体验的下降。”
    这是自相矛盾的!如果是unobtrusive,则不应允许链接是指向一个partial的,而应指向一个具有功能的替代页面。
    同样的,在第二种情况下,如果禁用JS会发生什么情况呢?

    退一步说,我们只考虑启用JS的情形,那么第一种情况也完全可以实现与第二种相同的行为,片段中直接跳转即可。如果是XHR读入,则可先过滤掉此段脚本,甚至可以不用管——因为直接插入到innerHTML中的脚本并不会被执行。要测试这一点,只要把你的示例中的xhr-link也指向到iframe.html就可以了。要偷懒的,可以打开示例,然后在地址栏执行 javascript:void(document.getElementById(‘xhr-link’).href=’iframe.html’) 。

    注意我并不是在赞同这种做法。归根到底,问题在于标记是否合理。在A标记中href指向一个html片段是什么意思呢?想要表达什么语义呢?去到那个链接的结果是“闲人免进”,那么写出来这个链接又有什么意义呢?跳转只是欲盖弥彰而已,因为跳转的结果并不符合用户的期望(除非你能完整的恢复犯罪现场,使得跳转的结果和直接点击的效果一致)。真还不如直接隐藏这个地址,比如换成button或干脆div。

    最后,假如你一定要在href里写上partial页的地址,那么跳转这点用心,对于用户来说算是不唐突吧。但是对Web开发者就唐突的很。Web开发者知道你是一个partial页面,那进来肯定是要看你的源码。所以至少你可以对Web开发者友好一点,不要直接跳,而是在网页中显示文字说明和链接,等5秒再自动跳转,方便Web开发者停止跳转,查看网页源码。

  9. realazy Says:

    @hax

    >>应指向一个具有功能的替代页面……如果是XHR读入,则可先过滤掉此段脚本,甚至可以不用管——因为直接插入到innerHTML中的脚本并不会被执行……
    完美主义可以理解,但在现实中大部分情况下时间和金钱预算并不允许你做得十全十美。假设我们没有金钱或时间去实现替换页面,使用相同的页面来做用户“误闯”看到的页面和提供数据,你认为 xhr 还是 iframe 更有优势?注意 iframe 有一个完整的 JS 执行环境以及 CSS 控制环境,能挥洒舞台更大。

    >>……去到那个链接的结果是“闲人免进”,那么写出来这个链接又有什么意义呢?跳转只是欲盖弥彰而已,因为跳转的结果并不符合用户的期望(除非你能完整的恢复犯罪现场,使得跳转的结果和直接点击的效果一致)……
    唉,我承认我的例子不好,不能很好说明我文章的中心思想。至于跳转的结果和直接点解的效果一致,存在 JS 执行环境的话是比较容易实现的(比如使用 url 的 hash 来做,常见的如 gmail)。

    >> 真还不如直接隐藏这个地址,比如换成button或干脆div
    那 unobtrusive 哪去了呢?accessibility 何在?

    >>……对Web开发者就唐突的很……
    既然你是开发者,你肯定能有办法看到它的源码。超越用户使用性的范围我们还是不要讨论为好。

  10. Aw Guo Says:

    用户为什么要访问partial?

  11. vsky Says:

    个人认为比较而言,Iframe在跨域和可用性上是有优势的。但是使用Iframe也带来了一些不便,例如载入的Iframe的高度计算、父窗口和Iframe之间的JS交互更复杂了。

  12. Robin Says:

    刚好最近也被这个问题困扰着. 通过XHR方式请求页面后退按钮不能用,被请求页面事件绑定比较麻烦,看不到源代码或许对SE不友好… 对于复杂的后台管理系统frame或许更有优势.

  13. sobo Says:

    我用的是表格分块布局,iframe 中src来调用做好的每一个小块,感觉还不错。(:>)本人是初学者。来看看师兄们。

  14. NetPuter Says:

    好,好有技术味道的讨论..!
    感觉用iframe和ajax是不能比的吧..

  15. dexteryy Says:

    土豆网播放页的评论就是用iframe来跨域获取的……不过方法是在iframe的src里调用一个包含ajax方法的页面,然后父页面调用这个方法来发起跟子页面同域名下的ajax请求。

    我做了一个通用的crossdomain.html :

    http://comments.tudou.com/crossdomain/index.html

    此外也可以直接用iframe的src来请求数据,不过不推荐把JS也写在src请求的页面里,那样耦合的太紧了,可以在那个页面里包含一个textarea标签,数据都放在它里面(这样可以支持任意内容和格式的字符串),然后用父页面里的JS去操作

    用iframe跨域的缺点就是父页面里必须加 document.domain = “[一级域名]“,可能会产生一些无法预料的问题,比如在firefox里,document.styleSheets的cssRules会失效

  16. Kael Says:

    第一种情况,获取的responseText可以进行适当的筛选,partial可以是一个完整的页面,也可以包含js,但是我们最后赋予Index中的某个元素的时候,只是partial的一部分。而我们在新窗口中打开partial的时候,也是一个完整的页面。

    这个和unobtrusive也不冲突,比如jQuery/API/ajax中的load()函数,要将/partial页面中的#resDiv li的内容载入到/index中的#desDiv
    $(‘#desDiv’).load(‘/partial #resDiv li’);

  17. Kael Says:

    第一种情况,获取的responseText可以进行适当的筛选,partial可以是一个完整的页面,也可以包含js,但是我们最后赋予Index中的某个元素的时候,只是partial的一部分。而我们在新窗口中打开partial的时候,也是一个完整的页面。

    这个和unobtrusive也不冲突,比如:
    要将/partial页面中的#resDiv li的内容载入到/index中的#desDiv
    文字链接的href为/partial

    使用jQuery/API/ajax中的load()函数,
    $(‘#desDiv’).load(‘/partial #resDiv li’);

  18. Kael Says:

    awaiting moderation的留言,留言者本人也看不到,还以为没有发送成功呃……

  19. Sam Says:

    请问一下iframe跨域调用有没有解决办法?谢谢

  20. Hades Says:

    使用iframe的问题是加载过程比XMLHttpRequest 复杂很多,比如计算布局之类,加载图片、样式之类。另外你说的用户访问foo.com/partial的问题,作为开发者应该尽量隐藏这些页面,不让浏览者有机会看到他。如果浏览者是有心想打开这个页面的话,那你也拿他没办法。一般人访问网站,我相信他不会这样子做的,除非故意找茬的。。。

  21. jlake Says:

    从跨域的 iframe 取内容可不是一件容易的事。

Leave a Reply


realazy (懒到死) is proudly powered by WordPress | Entries (RSS) and Comments (RSS)