博客代码运行框 开发实录
在博客园写博客,贴得出代码却看不到效果,需要用户复制到本地文件去运行,这样的阅读体验很差,所以我决定开发一个代码运行框,进一步提升用户体验。
需要的控件有<textarea>和<button>
<textarea>:用于输入HTML代码
<button>:点击后弹出浮层,展现运行结果
这个运行框组件的HTML结构为:
<div id="code_box"> <textarea></textarea> <button>运行</button> </div> <div id="layer"> <div id="layer_bg"></div> <div id="layer_content"></div> </div>
其中layer是浮层,layer_bg用于实现半透明背景,真正的内容放在layer_content,这里涉及一个问题,就是确定layer_content的高宽和位置。
先来说高宽吧,我们点击button后,会把textarea的value取出来,因为是一段完整的HTML代码,即一个单独的页面,自然想到使用iframe,怎么用iframe呢?
var codeBox = document.getElementById('code_box'), textarea = codeBox.children[0], btn = codeBox.children[1], layer = document.getElementById('layer'), content = document.getElementById('layer_content'); btn.onclick = function(){ runCode(textarea.value); } var runCode = function(htmlCode){ var iframe = document.createElement('iframe'), win = null, doc = null; content.innerHTML = ''; content.appendChild(iframe);//如果不加在这,iframe.contentDocument为空 win = iframe.contentWindow || iframe.contentDocument.parentWindow; doc = iframe.contentWindow.document || iframe.contentDocument; doc.open(); doc.write(htmlCode); doc.close(); layer.style.display = 'block';//如果不加在这,IE10,FF获取不到iframe高度 var h = doc.documentElement.scrollHeight || doc.body.scrollHeight, w = doc.documentElement.scrollWidth || doc.body.scrollWidth; iframe.style.width = w + 'px'; iframe.style.height = h + 'px'; var viewWidth = document.documentElement.clientWidth, viewHeight = document.documentElement.clientHeight; content.style.left = (viewWidth/2 - w/2) + 'px'; content.style.top = (viewHeight/2 - h/2) + 'px'; }
可以看到我用了scrollXXX来获得高宽,其实我开始是想用clientXXX的,为啥不用了呢,我先把这两种属性的测试数据贴出来吧:
测试页面:www.baidu.com;测试时,浏览器控制台的高度保持不变,所有浏览器均出现垂直和水平滚动条
先来张chrome的测试图
先来看看clientXXX
documentElement.clientWidth 和 documentElement.clientHeight就是上图红框标注的区域,即文档可见区域
奇怪的是body.clientWidth 和 body.clientHeight
body.clientWidth == documentElement.clientWidth
body.clientHeight != document.Element.clientHeight
如果body.clientXXX表示的是body的可见区域,那么body.clientHeight说不通
如果body.clientXXX表示的是body实际的高宽,那么body.clientWidth说不通
我以为是百度首页写得不好,切到google.cn去看了下,结果一样的,这就奇怪了 ,希望大家能指点一下。
接着scrollXXX
documentElement.scrollWidth 和 documentElement.scrollHeight表示文档实际的高宽
body.scrollWidth 和 body.scrollWidth表示body元素实际的高宽,不出意外的话,基本都等于documentElement.scrollXXX,但还有例外
测试结果中,Firefox和Opera的 body.scrollWidth < documentElement.scrollWidth
现在我除了想骂街已经没有别的想法了。。。。
===============================================================
骂街归来继续写,好了,先小结一下,下面要用到的:
获得文档可见区域的宽度:document.documentElement.clientWidth
获得文档可见区域的高度:document.documentElement.clientHeight
获得文档实际的宽度:document.documentElement.scrollWidth
获得文档实际的高度:document.documentElement.scrollHeight
回到之前iframe高宽的问题,因为是动态写入数据,高宽都未知,自然是用scrollXXX比较好,因为最大也就它了。
确定了高宽,再来看看定位的问题,为了美观,#layer_content必须水平垂直居中对齐,下面用js实现定位:
var viewWidth = document.documentElement.clientWidth, viewHeight = document.documentElement.clientHeight; content.style.left = (viewWidth/2 - w/2) + 'px'; content.style.top = (viewHeight/2 - h/2) + 'px';
核心功能都解释完了,下面开始收尾,还剩这几个需求:
1. 关闭按钮
2. 一个页面可能有多个运行框,所以需要调整HTML代码
3. 浮层的淡入淡出效果(这个我不贴代码,一帖又是超长的)
4. 代码运行框可能位于任何地方,所以弹出浮层时,它的top值需要设置
5. 如果有垂直滚动条,弹出浮层后需要禁用
下面给出最终版,这里就不让大家运行了,因为浮层上弹浮层是很蛋疼的一件事。
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <style type="text/css"> html, body, #layer, #layer_bg { width:100%; height:100%; } #layer, #layer_content { position:absolute; left:0; top:0; } html, body { margin:0; padding:0; } body { height:10000px; } #layer { display:none; } #layer_bg { background:#000; opacity:0.8; } #layer_content { background:#fff; padding:10px; } #close_btn { color:#fff; cursor:pointer; position:absolute; right:-5px; top:-10px; } </style> <script type="text/javascript"> var layer = null, content = null, closeBtn = null, iframeDiv = null; window.onload = function(){ layer = document.getElementById('layer'); content = document.getElementById('layer_content'); closeBtn = document.getElementById('close_btn'); iframeDiv = document.getElementById('layer_iframe'); closeBtn.onclick = function(){ layer.style.display = ''; document.body.style.overflow = ''; }; } var runCode = function(btn){ var code = btn.previousSibling.value; if(code){ document.body.style.overflow = 'hidden';//禁用滚动条,测试发现body通吃, 而documentElement在FF是个悲剧,求正解!!! layer.style.top = (document.documentElement.scrollTop || document.body.scrollTop) + 'px'; showResult(code); }else{ window.alert('请先输入代码!'); } }; var showResult = function(htmlCode){ var iframe = document.createElement('iframe'), win = null, doc = null; iframe.frameBorder = 0;//把那个难看的边框去了 iframeDiv.innerHTML = ''; iframeDiv.appendChild(iframe);//如果不加在这,iframe.contentDocument为空 win = iframe.contentWindow || iframe.contentDocument.parentWindow; doc = iframe.contentWindow.document || iframe.contentDocument; doc.open(); doc.write(htmlCode); doc.close(); layer.style.display = 'block';//如果不加在这,IE10,FF获取不到iframe高度 var h = doc.documentElement.scrollHeight || doc.body.scrollHeight, w = doc.documentElement.scrollWidth || doc.body.scrollWidth; iframe.style.width = w + 'px'; iframe.style.height = h + 'px'; var viewWidth = document.documentElement.clientWidth, viewHeight = document.documentElement.clientHeight; content.style.left = (viewWidth/2 - w/2) + 'px'; content.style.top = (viewHeight/2 - h/2) + 'px'; } </script> </head> <body> <!-- 这个div整个页面可以有多个 --> <div> <!-- 写成一行是为了button.previousSibling可以获得textarea--> <textarea></textarea><button onclick="runCode(this)">运行</button> </div> <!-- 这个div整个页面只有一个 --> <div id="layer"> <div id="layer_bg"></div> <div id="layer_content"> <span id="close_btn">X</span> <div id="layer_iframe"> </div> </div> </div> </body> </html>