绕道解决IE6下Select遮罩其他层的问题

以前碰到弹出层被Select框突出的时候,都是靠再弹出层上价格iframe来盖住,因为再ie6中iframe和select以及object都属于特殊对象,无论你改变div的zIndex为多大都照样会出现这种情况。

前段时间做支付宝手机站项目的时候又遇到了这个问题,无奈,用传统iframe解决的时候发现项目中弹出层背后还有半透明层,如果使用iframe达不到需要的效果。

但在使用iframe的时候发现,select box更像是被隐藏掉了,使用iframe之前:

使用iframe之后:

于是萌生了针对ie6操作dom将所有select元素隐藏掉。于是写了以下函数(改进版):

/**
  * s - Bool, 控制selectbox为显示(false)或隐藏(true)
  * e - DomElement, 控制selectbox dom查询深度,有利于提高效率
  **/
var hideSelectBox = function(s, e){
    if(!window.XMLHttpRequest){  //Detect IE6
        var d = e||document;
        var b = d.getElementsByTagName('select');
        for(var i=0,l=b.length; i<l; i++){
            b[i].style.visibility = s?'hidden':'visible';
        }
    }
};

查看DEMO

从实例来看JS的事件监听

Javascript也是一门事件驱动的脚本语言,和其他桌面软件一样,WEB应用程序也有自己的事件驱动机制。比如:单击(onclick)、鼠标经过(onmouseover)、鼠标按下(onmousedown)等等。

本文的实例借用下支付宝个人版“个人帐户信息”框:
https://lab.alipay.com/life/payment/fill.htm

未展开状态:

展开状态:

当鼠标单击信息框外的任何区域时,信息框自动消失。

先写一个页面结构 =>Demo: /demo/eventAlipay/demo_html.html

在继续之前,我先详细讲讲Javascript的事件机制。

很久以前有个叫Netscape的姑娘,她制订了Javascript的一套事件驱动机制,事件产生的顺序如下图所示:

事件流如箭头所指顺序开始由根节点往各节点派送,如果该节点上绑定有事件动作,则执行该动作,完毕后继续往下一节点走。
看看这个Demo: /demo/eventAlipay/demo_capture.html (请使用FireFox或Chrome等标准浏览器打开)

<script language="javascript">
//Javascript 事件捕获过程演示
window.onload = function(){
	if(document.addEventListener){
		//先在各节点监听事件
		document.addEventListener('click', function(){
			alert('Node: document');
		}, true);	//监听document节点
		document.body.addEventListener('click', function(){
			alert('Node: body');
		}, true);	//监听body节点
		document.getElementById('status_hide').addEventListener('click', function(){
			alert('Node: status_hide');
		}, true);	//监听DIV#status_hide节点
		document.getElementById('status_show').addEventListener('click', function(){
			alert('Node: status_show');
		}, true);	//监听DIV#status_show节点
	}else {
		alert('请使用Chrome、Firefox或其他非标准浏览器打开本页');
		window.close();
	}
};
</script>

后来又有一个叫“IE”的小子,这孩子比较傲气,他认为“凭什么我要依照你的规则走”,于是他又创造了一套自己的规则:

恰好相反,IE的规则是事件流从最底层级的节点开始往根节点派送,如果节点元素绑定了该事件,则执行,完毕后继续往父级节点走。
看看这个Demo:  /demo/eventAlipay/demo_bubble.html (请使用IE浏览器打开)

<script language="javascript">
//Javascript 事件捕获过程演示
window.onload = function(){
	if(document.attachEvent){
		//先在各节点监听事件
		document.attachEvent('onclick', function(){
			alert('Node: document');
		});	//监听document节点
		document.body.attachEvent('onclick', function(){
			alert('Node: body');
		});	//监听body节点
		document.getElementById('status_hide').attachEvent('onclick', function(){
			alert('Node: status_hide');
		});	//监听DIV#status_hide节点
		document.getElementById('status_show').attachEvent('onclick', function(){
			alert('Node: status_show');
		});	//监听document节点
	}else {
		alert('请使用IE浏览器打开本页');
		window.close();
	}
};
</script>

再后来,有个叫W3C的媒婆,想撮合这两个孩子,将他们的特点融合在了一起,这下,事件产生的顺序变成了这样:

事件从根节点开始,逐级派送到子节点,若节点绑定了事件动作,则执行动作,然后继续走,这个阶段称为“捕获阶段(Capture)
执行完捕获阶段后,事件由最高节点往根节点派送,若节点绑定了事件动作,则执行动作,然后继续走,这个阶段称为“冒泡阶段(Bubble)
看看这个Demo:  /demo/eventAlipay/demo_all.html (请使用FireFox或Chrome等标准浏览器打开)

<script language="javascript">
//Javascript 事件过程演示
window.onload = function(){
	if(document.addEventListener){
		//先在各节点监听事件
		document.addEventListener('click', function(){
			alert('Node: document Capture');
		}, true);	//监听document节点
		document.addEventListener('click', function(){
			alert('Node: document Bubble');
		}, false);
		document.body.addEventListener('click', function(){
			alert('Node: body Capture');
		}, true);	//监听body节点
		document.body.addEventListener('click', function(){
			alert('Node: body Bubble');
		}, false);
		document.getElementById('status_hide').addEventListener('click', function(){
			alert('Node: status_hide Capture');
		}, true);	//监听DIV#status_hide节点
		document.getElementById('status_hide').addEventListener('click', function(){
			alert('Node: status_hide Bubble');
		}, false);
		document.getElementById('status_show').addEventListener('click', function(){
			alert('Node: status_show Capture');
		}, true);	//监听DIV#status_show节点
		document.getElementById('status_show').addEventListener('click', function(){
			alert('Node: status_show Bubble');
		}, false);
	}else {
		alert('请使用Chrome、Firefox或其他非标准浏览器打开本页');
		window.close();
	}
};
</script>

善良的Netscape以及其姐妹们都接受了媒婆的建议,采用了新的事件规则,而骄傲固执的IE小子始终按照自己的规则执行。最终使得这成为困扰前端开发人员的兼容性问题之一。

那么,怎么绑定事件呢?

由于这两派浏览器的差异,其绑定的方法也不一样,其中,遵循标准的浏览器使用W3C定义的addEventListener函数绑定,函数定义如下:
function addEventListener(string eventFlag, function eventFunc, [bool useCapture=false])
eventFlag : 事件名称,如click、mouseover…
eventFunc: 绑定到事件中执行的动作
useCapture: 指定是否绑定在捕获阶段,true为是,false为否,默认为true
在事件监听流中可以使用event.stopPropagation()来阻止事件继续往下流

IE中使用自有的attachEvent函数绑定时间,函数定义如下:
function attachEvent(string eventFlag, function eventFunc)
eventFlag: 事件名称,但要加上on,如onclick、onmouseover…
eventFunc: 绑定到事件中执行的动作
在事件监听流中可以使用window.event.cacenlBubble=false来阻止事件继续往下流

有了这些认知之后,你应该对本文所要实现的功能有个大致的思路了,即:
使用单击事件的冒泡阶段,在status_hide节点绑定信息框展开函数并停止事件流,在document绑定信息框隐藏函数,在status_show绑定阻止事件流函数。

为什么使用冒泡阶段?是为了兼容两类浏览器,另外,使用冒泡阶段也可以更方便的控制事件流。

最终的效果看这个Demo: /demo/eventAlipay/demo.html (兼容各主流浏览器)

<script language="javascript">
//Javascript 事件演示
window.onload = function(){
	var hideBox = function(event){
		document.getElementById('status_show').style.display = 'none';
		document.getElementById('status_hide').style.display = 'block';
	};
	var showBox = function(event){
		document.getElementById('status_show').style.display = 'block';
		document.getElementById('status_hide').style.display = 'none';
		stopEvent(event);
	};
	var stopEvent = function(event){
		e = event || window.event;
		if(e.stopPropagation){
			e.stopPropagation();
		}else {
			e.cancelBubble = true;
		}
	};
	if(document.addEventListener){
		document.addEventListener('click', hideBox, false);
		document.getElementById('status_hide').addEventListener('click', showBox, false);
		document.getElementById('status_show').addEventListener('click', stopEvent, false);
	}else {
		//For IE
		document.attachEvent('onclick', hideBox);
		document.getElementById('status_hide').attachEvent('onclick', showBox);
		document.getElementById('status_show').attachEvent('onclick', stopEvent, showBox);
	}
};
</script>

终于知道为什么js语句换行写会报错

以前写js,遇到诸如输出一大段html的时候,往往会遇到js报错,因为换行这些hrml被写在了不同的行内。比如:
var str = “<html>
<head>
</head>
</html>”;

有此遭遇后,遇到所有的需要换行的时候都采取的其他方法,如使用+进行字符运算。但却从未对其进行过深究。如:
var str = “<html>\n” +
“<head>\n”+
+”</head>”+
+”</html>”;

今天看到这篇文章 http://goo.gl/Q3qi 《有关JavaScript的10件让人费解的事情》,所列的最后一条“莫名其妙的代码错误”,在最后解释原因的时候说“这是因为 JavaScript 有一个功能,会纠正它认为错误的代码书写,它会自作聪明地在 return 这个词后面插入一个 “;” ,错误因此而生。”

虽然,js可以不严谨的去掉”;”,但保险起见,建议平时开发还是养成良好的书写习惯。

setTimeout的另外一个作用

在文档的任何地方将要执行的代码加入到setTimeout(function(){ /**Your Code Here**/ }, 0); 可以将要执行的代码提到window.onload后执行,这样在某些情况下可以增强用户体验。

demo不好放,最近在构思一个demo站点,域名申请好了 51demo.net

使用JS动态创建style标签的通用办法

处于某些目的,我们需要在Javascript运行中创建一个style标签,并写入我们期望的css内容。

按照常规可以通过DOM操作来实现:
var css = document.createElement(’style’);
css.setAttribute(‘type’, ‘text/css’);
var css_text = document.createTextNode(‘div{width:200px;height:100px;background:#ccc;}’);
css.appendChild(css_text);
document.getElementsByTagName(‘head’)[0].appendChild(css);

好吧,运行试一试!
结果:FF下正确显示,IE下报错,查询了一下资料,原来PPK也说出了这个问题http://www.quirksmode.org/bugreports/archives/2006/01/IE_wont_allow_documentcreateElementstyle.html
具体产生原因不明,等待高手解答,偶在这也只有提供通用的办法:
来自这篇文章http://www.phpied.com/dynamic-script-and-style-elements-in-ie/

将上面代码修改如下:
var css = document.createElement(’style’);
css.setAttribute(‘type’, ‘text/css’);
var css_str = ‘div{width:200px;height:100px;background:#ccc;}’;
var css_text = document.createTextNode(css_str);
css.appendChild(css_text);
/**Cross-Browser**/
 if(css.styleSheet){   //For IE
     css.styleSheet.cssText = css_str;
 }else {
     var css_text = document.createTextNode(css_str);
     css.appendChild(css_text);
 }