欢迎来到 jackNEss'窝窝
I like simple mind

项目中的延迟hover使用

2012年06月27日

项目中的延迟hover使用

需求由来

新版首页上线,其中有一个手风琴模块,需要鼠标移动到上面时,触发2个动作:

  • 内容由纯文字变为图文格式
  • 图文格式中的图片开始加载

在一期优化时,提出如果用户的无意识操作,如鼠标滑过手风琴模块,但其实他本意并不想操作该模块的话,模块动作依然发生。这样的话,有2点不好的地方:

  • 这一无意识操作使用户加载了一张本来没必要的图片
  • 对于服务器CDN来讲,这样无疑是一个不必要的资源浪费

基于上述2个方面,我们决定给这模块添加一个响应时间,超过响应时间用户鼠标还停留在被触发对象时,才触发事件。

在这里,我就给大家介绍下 延迟hover的实现。

延迟hover

其实这种效果已经广泛应用于各大网站如 腾讯,网易,新浪的选项卡切换部分,都采用了大概200ms的延迟时间,为了实现这种效果,我们需要给目标对象添加2个事件:onmouseoveronmouseout。下面我举一个例子来说明一下。

假设有这么一个html模块需要实现延迟hover:

<!-- html document -->
<div id="delayTarget" >delay module</div>

我们想要延迟触发的事件为 hoverHandle() 事件:

//javascript document
function hoverHandle(){
	alert("I am hover");
}

首先我们把目标赋值到变量 target 里面:

//javascript document
var target = document.getElementById("delayTarget");

延迟hover所绑定的 onmouseover事件

在这里 onmouseover 其实就是 让本该有的动作添加一个 setTimeout,让动作在 指定时间之后才触发,并将 delayKey 传到 目标对象里面,好让触发onmouseout时可以清除这个 setTimeout 事件,阻止动作发生。

//javascript document
target.onmouseover = function(){
	this.delayKey = setTimeout(hoverHandle,200);
}

延迟hover所绑定的 onmouseout 事件

当触发onmouseout时,将事先在setTimeout中储存的 delayKey 清除掉,从而阻止动作发生。

//javascript document
target.onmouseout = function(){
	clearTimeout(this.delayKey);
}

下面我做了简单的封装,代码如下:

function delayHover(target,eventHandle,delayTime){
	var dt = (typeof delayTime == "number"? delayTime:200);
	if(!target || typeof eventHandle != "function"){ return;}
	target.onmouseover = function(){
		target.delayKey = setTimeout(eventHandle,dt);
	}
	target.onmouseout = function(){
		clearTimeout(target.delayKey);
	}
}

下面是一个简单的 demo

基于这个原理,我另外做了一个带有delayhover的选项卡切换功能模块。

/*
 * f2e_delayHover() hover 延迟
 * 参数:
 * f2e_delayHover(option)
 * option = {
 *	tags:设置选项卡部分对象,必填
 *	cnts:设置选项卡对应内容对象,可以为空
 *	delay:设置延迟毫秒数,默认200
 *	toggleClass:设置鼠标经过时改变的样式,默认cur
 *	mouseOver:设置鼠标经过时触发的事件,默认为空
 *	mouseOut: 设置鼠标移开时触发的事件,默认为空
 * }
 * 方法:
 * f2e_delayHover().setOption(op) 设置参数
 * -op:参数同上
 *
 * f2e_delayHover().hover(func1,func2) 设置hover事件
 * -func1:设置鼠标经过时触发的事件
 * -func2:设置鼠标离开时触发的事件
 *
 * f2e_delayHover().init() 函数初始化/执行
 *
 *------------------------------------------------
 *example:

f2e_delayHover({
	tags:"#delayTagList li",
	cnts:"#delayTagContent .cell",
	delay:200,
	toggleClass:"cur"
}).hover(function(){
	alert("mouseOver")
},function(){
	alert("mouseOut")
}).init();

 * 精简版
 f2e_delayHover({tags:"#delayTagList li",cnts:"#delayTagContent .cell",delay:200,toggleClass:"cur"}).init();
 *------------------------------------------------
 * Author:jackNEss Lau
 * Date:2012-5-22
 * ver:1.0
 */

var f2e_delayHover = function(){

	var option = {
		tags:null,
		cnts:null,
		delay:200,
		toggleClass:"cur",
		mouseOver:function(){},
		mouseOut:function(){}
	}
	var op;
	arguments[0]?op = arguments[0]:"";
	/* jns start
	---------------------------------------------------------*/
	var jns = {};

	/*
	 * console
	 * exp:jns.console("elm is undefined")
	 * date: 2012-5-25
	 * ver 1.0
	 */
	jns.console = function(msg){
		if(typeof console != "undefined"){
			console.log(msg);
		}
	}

	/*
	 * element selector
	 * exp: jns.selector("#a .b span")
	 * exp: jns.selector("#a");
	 * exp: jns.selector(elm);
	 * date: 2012-5-25
	 * ver 1.0
	 */
	jns.selector = function(source){
		var dc = document;
			
		if( typeof source == "object"){
			return source;
		}
		else if( typeof source == "string"){
			
			var strGroup = source.split(/\s+/);
			var strGroup_len = strGroup.length;
			var fragSource;
			var fragSource_len;
			var result = dc;
			if(strGroup_len == 1 && strGroup[0].substring(0,1) == "#"){
				return dc.getElementById( strGroup[0].substring(1) )
			}
			else{
				if(dc.querySelectorAll){
					result = dc.querySelectorAll(source); 
				}
				else{
					for( var i = 0; i < strGroup_len; i++){
						fragSource = strGroup[i];
						fragSource_len = fragSource.length;
						if(fragSource_len == 0){continue;}
						switch(fragSource.substring(0,1)){
							case "#": result = idSelector(result,fragSource.substring(1)); break;
							case ".": result = classSelector(result,fragSource.substring(1)); break;
							default : result = tagSelector(result,fragSource); break;
						}
					}
					result = [].concat(result);
					
				}
				if(result.length == 0){
					return false;
				}
				
				else if(result.length == 1){
					return result[0];
				}
				
				else{
					return result;
				}
			}
		}
		
		function idSelector(target,idStr){
			return dc.getElementById(idStr);
		}
		
		function classSelector(target,className){
			var targetGroup = [].concat(target);
			var targetGroup_len = targetGroup.length;
			var result = [];
			var fragTarget;
			var fragGroup;
			var fragTarget_cells;
			var fragTarget_cells_len;
			var fragClassNames;
			var fragClassNames_len;
			for( var i = 0; i < targetGroup_len; i++){
				fragTarget = targetGroup[i];
				if(!fragTarget){continue;}
				fragTarget_cells = fragTarget.getElementsByTagName("*");
				fragTarget_cells_len = fragTarget_cells.length;
				
				for( var k = 0; k < fragTarget_cells_len; k++){
					fragClassNames = fragTarget_cells[k].className.split(" ");
					fragClassNames_len = fragClassNames.length;
					
					for( var j = 0; j < fragClassNames_len; j++){
						if(fragClassNames[j] == className){
							result = result.concat(fragTarget_cells[k]);
							break;
						}
					}
				}
				
				
			}
			return result;
		}
		
		function tagSelector(target,tagStr){
			var targetGroup = [].concat(target);
			var targetGroup_len = targetGroup.length;
			var result = [];
			var fragTarget;
			var fragGroup;
			var fragGroup_len;
			for( var i = 0; i < targetGroup_len; i++){
				fragTarget = targetGroup[i];
				if(!fragTarget){continue;}
				fragGroup = fragTarget.getElementsByTagName(tagStr);
				fragGroup_len = fragGroup.length;
				for( var j = 0; j < fragGroup_len;j++){
					result = result.concat(fragGroup[j]);
				}
			}
			return result;
		}
	}
	
	/*
	 * each
	 * exp: jns.each(targets,function(){alert(this);})
	 * date: 2012-5-25
	 * ver 1.0
	 */
	jns.each = function(elms,func){
		if(!elms){return;}
		if(typeof elms.length == "undefined"){
			elms = [elms];
		}
		for( var i = 0, len = elms.length; i < len; i++){
			func.call(elms[i]);
		}
	}

	/*
	 * add event
	 * exp: jns.bind(elm,"click",clickHandle)
	 * date: 2012-5-25
	 * ver 1.0
	 */
	jns.bind = function(target,type,func){
		if(typeof func != "function" || !target || typeof type != "string"){return;}
		if(target.addEventListener){
			target.addEventListener(type,_addEvent,false);

		}
		else if(target.attachEvent){
			target.attachEvent("on" + type,_addEvent);
		}
		
		if(!func.jnsBindKey){ func.jnsBindKey = [];}
		func.jnsBindKey.push({target:target,bindKey:_addEvent});

		function _addEvent(e){
			func.call(target,e);
		}
	}

	/*
	 * remove event 1.0
	 * exp: jns.unbind(elm,"click",clickHandle)
	 * date: 2012-5-25
	 * ver 1.0
	 */
	jns.unbind = function(target,type,func){
		if(typeof func != "function" || !target || typeof type != "string"){return;}
		var unbindKey;
		if(func.jnsBindKey){
			for( var i = 0, len = func.jnsBindKey.length; i < len; i++ ){
				var fragSource = func.jnsBindKey[i];
				if(fragSource.target == target){
					unbindKey = fragSource.bindKey;
					func.jnsBindKey.splice(i,1);
					break;
				}
			}
		}
		else{
			unbindKey = func;
		}
		if(target.removeEventListener && type && unbindKey){
			target.removeEventListener(type,unbindKey);
		}
		else if(target.detachEvent){
			target.detachEvent("on" + type,unbindKey);
		}
	}

	/*
	 * add class
	 * exp: jns.addClass(elm,"cur")
	 * date: 2012-5-25
	 * ver 1.0
	 */
	jns.addClass = function(target,className){
		var classNames = target.className.split(/\s+/);
		for(var i = 0,len = classNames.length; i < len; i++){
			if(classNames[i] === className){ return;}
		}
		target.className += " " + className;
	}

	/*
	 * remove class
	 * exp: jns.removeClass(elm,"cur")
	 * date: 2012-5-25
	 * ver 1.0
	 */
	jns.removeClass = function(target,className){
		var classNames = target.className.split(/\s+/);
		for(var i = 0, len = classNames.length; i < len; i++){
			if(classNames[i] === className){ classNames[i] = "";}
		}
		target.className = classNames.join(" ");
	}

	/* jns end
	---------------------------------------------------------*/

	function areaReset(){
		if(typeof option.tags.length =="undefined"){
			option.tags = [option.tags];
		}
		if(option.cnts && typeof option.cnts.length == "undefined"){
			option.cnts = [option.cnts];
		}
		var tags = option.tags,
			cnts = option.cnts,
			delayTime = option.delay,
			toggleClass = option.toggleClass,
			mouseOverEvent = option.mouseOver,
			mouseOutEvent = option.mouseOut;

		for(var i = 0, len = tags.length; i < len; i++){
			var fs = tags[i];
			jns.bind(fs,"mouseover",mouseOverHandle);
			jns.bind(fs,"mouseout",mouseOutHandle);
		}

		function mouseOverHandle(){
			var fs = this;
			attrSiblingsCheck.call(fs);
			fs.delayKey = setTimeout(function(){
				jns.addClass(fs,toggleClass);
				jns.each(fs.jns_siblings,function(){
					jns.removeClass(this,toggleClass);
				})
				jns.each(cnts,function(){
					this === cnts[fs.jns_index]?(
						this.style.display = "block"
					):(
						this.style.display = "none"
					);
				})
				typeof mouseOverEvent == "function"? mouseOverEvent.call(fs):"";

			},delayTime);
		}

		function mouseOutHandle(){
			attrSiblingsCheck.call(this);
			clearTimeout(this.delayKey);
			typeof mouseOutEvent == "function"? mouseOutEvent.call(this):"";
		}

		function attrSiblingsCheck(){
			if(!this.jns_siblings){
				this.jns_siblings = [];
				var children = this.parentNode.children;
				for(var i = 0,len = children.length; i < len; i++){
					this === children[i]?this.jns_index = i:this.jns_siblings.push(children[i]);
				}
			}
		}

	}


	return{
		setOption:function(op){
			op.tags? option.tags = jns.selector(op.tags):"";
			op.cnts? option.cnts = jns.selector(op.cnts):"";
			op.delay? option.delay = (parseInt(op.delay)||0):"";
			typeof op.toggleClass == "string"? option.toggleClass = op.toggleClass:"";
			typeof op.mouseOver == "function"? option.mouseOver = op.mouseOver:"";
			typeof op.mouseOut == "function"? option.mouseOut = op.mouseOut:"";

			return this;
		},
		hover:function(){
			typeof arguments[0] == "function"? option.mouseOver = arguments[0]:"";
			typeof arguments[1] == "function"? option.mouseOut = arguments[1]:"";
			return this;
		},
		init:function(){
			if(op){
				this.setOption(op);
			}
			if(!option.tags){return;}

			areaReset.call(this);
		}
	}
}

这里是该模块的 demo 地址

分类javascript
阅读 742
  • 评论加载中...

标签云

分类目录

最新留言

  • 评论加载中...

与我联系

如有疑问or建议可通过以下方式跟我取得联系.

Q Q:373435871
Email:jackness1208@gmail.com
© Copyright 2011 - 2014 jackNEss.org All Rights Reserved 粤ICP备14065612号
首页 | 关于我 | 网站地图 | RSS