在博客园写博客,贴得出代码却看不到效果,需要用户复制到本地文件去运行,这样的阅读体验很差,所以我决定开发一个代码运行框,进一步提升用户体验。
需要的控件有<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 οnclick="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>