做web ssh时遇到一个问题:想要手工建立一个iframe,然后在其中添加DOM组件,但是发现各浏览器对iframe的行为不同。
测试浏览器: chrome 75.0.3770.100, firefox 60.7.2esr, firefox 67.0.4
测试代码:
<html>
<head></head>
<body>
<div>test iframe</div>
<iframe id='iframe' name='iframe' src='#'>frame content</iframe>
<script type="text/javascript">
let ifr = document.getElementById('iframe')
let newDoc = ifr.contentDocument || ifr.contentWindow.document;
let div = newDoc.createElement('div')
newDoc.body.appendChild(div)
let text = newDoc.createTextNode('text')
div.appendChild(text)
console.log('appended');
</script>
</body>
</html>
结果很出乎意料
iframe src='#' | iframe src='' | (说明 | |
chrome | 两层iframe,而且内容未处理 | 一层iframe,内容已处理,为想要的结果 | 对于chrome,设置src为空即可满足要求 |
firefox ESR | 一层iframe,内容已处理,为想要的结果 | 一层iframe,内容未处理 | firefox ESR版可以正确控制src='#'的iframe |
firefox标准版 | 两层iframe,内容已处理 | 一层iframe,内容未处理 | 比较奇怪的控制逻辑…… src必须有值,而且一定会被读取并显示 |
(说明 | 除了Firefox ESR以外,对于src='#'的iframe,浏览器都会重新读取一次当前页面内容并显示到iframe中。firefox标注版iframe内容可以显示,chrome里的内容未显示(但是可以inspect看到) | chrome可以正确理解空src的iframe并处理后续内容;firefox两版本均认为无src(或src为about:blank)的iframe无内容,无法进行appendChild之类的操作 |
为了再各种情况都获得好结果(单层iframe且处理内容),需要处理一下iframe的文档流:
...
let newDoc = ifr.contentDocument || ifr.contentWindow.document;
//hack
newDoc.open()
newDoc.write('')
newDoc.close()
let div = newDoc.createElement('div')
...
此时src已经无关紧要,不管是空或者是'#',各个浏览器均可以显示为单层iframe并正常处理内容。个人理解是用write方法强制把组件设置成可编辑,覆盖了firefox的默认操作