动态写入内容到 iFrame 并执行其中脚本

How to - Injecting HTML into an IFrame

其实实现思路比较简单,与动态打开一个窗口并使用 document.write(...) 写入内容的做法类似,IFrame 内部包含的 window 对象也可以使用同样的方法写入 HTML 内容,并且可以执行其中的脚本。

主要参考

  • stackoverflow "Creating an iframe with given HTML dynamically"

  • Dojo 的 Reference Guide 采用类似做法来运行示例代码
    • 经过分析可以看到 Dojo Reference Guide 是采用一个 src=javascript:[页面内容] 的方式来打开 iframe,例如:
      javascript:%20'<!DOCTYPE%20html>\n<html%20>\n<head>\n\n<link%20rel="stylesheet"%20href="../_static/js/dojo/../dijit/themes/claro/claro.css">\n\n<script>dojoConfig%20=%20{async:%20true}</script><script%20src=\'../_static/js/dojo/dojo.js\'></script><script>require([\n%20%20%20%20"dojo/ready",%20"dojo/_base/window",%20"dojo/store/Memory",\n%20%20%20%20"dijit/tree/ObjectStoreModel",%20"dijit/Tree"\n],%20function(ready,%20win,%20Memory,%20ObjectStoreModel,%20Tree){\n\n%20%20%20%20//%20Create%20test%20store,%20adding%20the%20getChildren()%20method%20required%20by%20ObjectStoreModel\n%20%20%20%20var%20myStore%20=%20new%20Memory({\n%20%20%20%20%20%20%20%20data:%20[\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'world\',%20name:\'The%20earth\',%20type:\'planet\',%20population:%20\'6%20billion\'},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'AF\',%20name:\'Africa\',%20type:\'continent\',%20population:\'900%20million\',%20area:%20\'30,221,532%20sq%20km\',\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20timezone:%20\'-1%20UTC%20to%20+4%20UTC\',%20parent:%20\'world\'},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'EG\',%20name:\'Egypt\',%20type:\'country\',%20parent:%20\'AF\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'KE\',%20name:\'Kenya\',%20type:\'country\',%20parent:%20\'AF\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'Nairobi\',%20name:\'Nairobi\',%20type:\'city\',%20parent:%20\'KE\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'Mombasa\',%20name:\'Mombasa\',%20type:\'city\',%20parent:%20\'KE\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'SD\',%20name:\'Sudan\',%20type:\'country\',%20parent:%20\'AF\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'Khartoum\',%20name:\'Khartoum\',%20type:\'city\',%20parent:%20\'SD\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'AS\',%20name:\'Asia\',%20type:\'continent\',%20parent:%20\'world\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'CN\',%20name:\'China\',%20type:\'country\',%20parent:%20\'AS\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'IN\',%20name:\'India\',%20type:\'country\',%20parent:%20\'AS\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'RU\',%20name:\'Russia\',%20type:\'country\',%20parent:%20\'AS\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'MN\',%20name:\'Mongolia\',%20type:\'country\',%20parent:%20\'AS\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'OC\',%20name:\'Oceania\',%20type:\'continent\',%20population:\'21%20million\',%20parent:%20\'world\'},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'EU\',%20name:\'Europe\',%20type:\'continent\',%20parent:%20\'world\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'DE\',%20name:\'Germany\',%20type:\'country\',%20parent:%20\'EU\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'FR\',%20name:\'France\',%20type:\'country\',%20parent:%20\'EU\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'ES\',%20name:\'Spain\',%20type:\'country\',%20parent:%20\'EU\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'IT\',%20name:\'Italy\',%20type:\'country\',%20parent:%20\'EU\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'NA\',%20name:\'North%20America\',%20type:\'continent\',%20parent:%20\'world\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'SA\',%20name:\'South%20America\',%20type:\'continent\',%20parent:%20\'world\'%20}\n%20%20%20%20%20%20%20%20],\n%20%20%20%20%20%20%20%20getChildren:%20function(object){\n%20%20%20%20%20%20%20%20%20%20%20%20return%20this.query({parent:%20object.id});\n%20%20%20%20%20%20%20%20}\n%20%20%20%20});\n\n%20%20%20%20//%20Create%20the%20model\n%20%20%20%20var%20myModel%20=%20new%20ObjectStoreModel({\n%20%20%20%20%20%20%20%20store:%20myStore,\n%20%20%20%20%20%20%20%20query:%20{id:%20\'world\'}\n%20%20%20%20});\n\n%20%20%20%20//%20Create%20the%20Tree.%20%20%20Note%20that%20all%20widget%20creation%20should%20be%20inside%20a%20dojo.ready().\n%20%20%20%20ready(function(){\n%20%20%20%20%20%20%20%20var%20tree%20=%20new%20Tree({\n%20%20%20%20%20%20%20%20%20%20%20%20model:%20myModel\n%20%20%20%20%20%20%20%20});\n%20%20%20%20%20%20%20%20tree.placeAt(win.body());\n%20%20%20%20%20%20%20%20tree.startup();\n%20%20%20%20});\n});</script>\n</head>\n<body%20class="claro">\n%20%20%20%20\n</body>\n</html>'
      

实现实例

参考 fill-iframe-with-string.html, 要点解释如下:

  1. 基本流程就是:
    • IFrame 内部 document.open();
    • IFrame 内部 document.write(...);
    • IFrame 内部 document.close();
  2. 示例中实现了向 IFrame 内部窗口传递参数的功能。注意在 IE 中,document.open() 会清除 IFrame 内部 window 对象上的属性,因此参数的设置需要在 document.open() 之后进行;
  3. 关于脚本的执行:
    • 按照一般的习惯,在页面头部会使用诸如 <script src="./jquery-ui-1.10.3/jquery-1.9.1.js"></script> 这样的写法引入外部 js,然后在 HTML 元素之后使用嵌入的 javascript 脚本执行与页面元素相关的操作;
    • 在 Firefox 和 Chrome 中,<script> 的执行是按照在页面中的先后顺序进行的, 因此处于文档后部的嵌入 javascript 脚本可以使用外部 js 中定义的变量(比如:jQuery 的 "$");
    • 但是在 IE 中情况正好相反,在执行嵌入的 javascript 脚本时,引入的外部 js 还没有执行,这样就造成嵌入 javascript 脚本出现 "找不到 $ 对象" 之类的错误;
    • 解决方法是在 IFrame 内部页面上定义 bodyonload 事件,在这个事件中执行页面中的嵌入 javascript 脚本。

实际效果见 在线演示

讨论:不同浏览器的差异

总的来说 IE9、Firefox 和 Chrome 的差别很小,倒是 IE8 与其他浏览器存在不小的差别,主要包括:

  • 安全模型:上面提到的示例 html 文件无法通过 IE8 直接在本地打开(如下图),但是其他浏览器可以正常打开,而发布到 Web 服务器上之后,IE8 也可以正常打开;

    另外如果 IFrame 中引用的是来自网络的 js 外部文件,IE8 可以正常运行(见这个例子:fill-iframe-with-string-online-test.html);
  • IFrame 相关对象, 以及页面高度检测:下面对不同浏览器中 iframe.documentWindowiframe.contentWindowiframe.Documentiframe.contentDocument.body.offsetHeightiframe.Document.body.scrollHeight进行了比较,可以看出来,IE8 与其他几个浏览器的差异比较明显:
    • IE8, WindowsXP:
    • IE9, Windows 7:
    • Firefox19, Windows 7:
    • Firefox20, Ubuntu:
    • Google Chrome 6, Windows 7:
    • Chromium25, Ubuntu:

END

Attachments (7)

Download all attachments as: .zip

Comments

No comments.