click事件细节

这是前一个月被反馈的问题,当时没有时间研究,今天稍有时间研究汇总下

一个小问题

click事件是鼠标点击某个元素的时候触发的吗?
这么问还不够细……
是鼠标点下触发的吗?
是鼠标松开时触发的?
还是鼠标一次按下+松开再触发。

这个问题很好回答,相信很多人有这样一种检验:一些二逼的产品经理把一些点击的交互放在非常不寻常的位置,这导致我们经常点击到错误的区域——好的,我们细化这个过程:

-->我们需要执行某个无脑操作,鼠标在我们直觉认为的地方左键按下(并没有松开)
-->然后大脑忽然参与了,“二逼,这个地方点击并不能达到你的期望,并且会跳到一个操作时间很长的页面,以公司这破网速,你再回到此页面需要250秒!”
-->然后我们的右手(此处忽略左撇子的感受!)拖动鼠标到一个无关的地方,释放按下的左键,“好险,又避开了产品经理的陷阱!”

一次连贯的click事件就被我们摧毁了。

和别的事件结合会发生什么事

就像我们打断鼠标的点击事件一样,在同一元素上触发click事件和一些别的事件,会造成意外的情况。

因为click包含mousedown和mouseup,那么我们就要特别注意那些mousedown时触发的事件:mousedown,focus,focusin。

假如这些mousedown时就会触发的事件是把鼠标移到别的位置,例如弹出一个遮挡层——鼠标与原来的元素之间的联系就断开了,就算鼠标的坐标没有发生变化,但是此时再松开鼠标——还有卵用!你只是在遮挡上来了一发mouseup!

细节之处

模拟select元素遇到的问题

方案一

我时常看到一些人嫌select元素不好用,要自己模拟一个select。

代码会这样写

/**
 * 模拟一个下拉列表
 */
function DropdownList(){
	var container = $("<div class='select'></div>");
	var curitem = $("<input readonly='readonly' class='curitem'>");
	var list = $("<ul style='display:none'><li>选项1</li><li>选项2</li></ul>");

	//事件
	curitem
	.focus(function(){
		list.css({
			"display":"block"
		})
	})
	.blur(function(){
		list.css({
			"display":"none"
		})
	})
	list.find("li").click(function(e){
		var value = $(e.target).html();
		curitem.val(value);
	})

	container.append(curitem);
	container.append(list);

	return container;
}

他们使用一个input来呈现已选中的项的值,用一个ul存放下拉列表的值,通过input的焦点事件来显隐ul。给li元素绑定click事件来选中。
然而:click事件并没有什么卵用!

因为我们click一个li的时候,放慢动作:

-->mousedown到了li上 --> input 失去焦点,blur了 --> ul隐藏了 -->此时™释放了mouseup

你的click被沉默了!

解决办法:只要把选择事件从click换成 mousedown即可。

方案二

一些同学在上面一步就垮掉了,愤怒地说道:“尼玛的,傻逼设计的js,傻逼设计的dom,本屌的思路如此行云流水,竟然会出现此路不通!”

他们会执行方案二:

/**
 * 模拟一个下拉列表
 * 使用一个div和ul
 */
function DropdownList2(){
	var container = $("<div class='select'></div>");
	//这里不再用input元素,而是一个div元素,他们不再依赖focus和blur机制
	var curitem = $("<div class='curitem'></div>");
	curitem.css({
		"border":"1px solid #ddd",
		"height":"30px",
		"width":"100px"
	})
	var list = $("<ul style='display:none'><li>选项1</li><li>选项2</li></ul>");

	//事件
	curitem
	.click(function(){
		list.css({
			"display":"block"
		})
	})
	
	list.find("li").click(function(e){
		var value = $(e.target).html();
		curitem.html(value);
		list.css({
			"display":"none"
		})
	})

	container.append(curitem);
	container.append(list);
	return container;
}

然而坑爹的是,当ul显示出来之后,点击页面的其他地方,ul隐藏不了。于是……
在body上绑定了一个click事件,click之后隐藏ul……

有一些模拟化编程基础的同学是忍受不了这种事情的。

我的解决方法是使用focuseout,它有一些兼容性问题我这里不讨论:

focusout和blur的概念是一样的,但它是冒泡的。有人说,这里是个div元素没有focus和blur事件,你冒个卵子,我只能说too young too simple!

需要背景知识的去看这篇文章,很棒的,说说focus /focusin /focusout /blur 事件

我们直奔主题了:
我们将上面的代码改造成如下

/**
 * 模拟一个下拉列表
 * 使用一个div和ul
 */
function DropdownList2(){
	var container = $("<div class='select' tabindex='0'></div>");//这里使用一个tabindex是为了让div在非ie环境下具有fouseout事件
	//这里不再用input元素,而是一个div元素,他们不再依赖focus和blur机制
	var curitem = $("<div class='curitem'></div>");
	curitem.css({
		"border":"1px solid #ddd",
		"height":"30px",
		"width":"100px"
	})
	var list = $("<ul style='display:none;width:200px;border:1px solid #ddd;'><li>选项1</li><li>选项2</li></ul>");

	//事件
	curitem
	.click(function(){
		list.css({
			"display":"block"
		})
	})
	
	list.find("li").click(function(e){
		var value = $(e.target).html();
		curitem.html(value);
		list.css({
			"display":"none"
		})
	})
	container.focusout(function(e){
		console.log("container focusout")
		list.css({
			"display":"none"
		})
	})

	container.append(curitem);
	container.append(list);
	return container;
}

上面的代码在chrome,firfox下没有问题,但是在ie下是有问题的:li的click之后触发的首先是container的focusout事件,然后list直接被隐藏了,click又被沉默了!——因为ie下li和div天然就可触发focuseout事件,所以我们放下慢动作:

ie中(别的浏览器我就不说了):

-->首先你click了用来显示选中项的curitem,然后ul显示出来了——此时焦点在curitem上
-->然后你选中了一项(mousedown):焦点到了ul中的此项上-->curitem上触发了focusout事件,冒泡到container上-->container.focusout隐藏了ul-->你在ul之外释放mouseup

这个现象说明了一个元素的focusout事件包括冒泡上去的focusout事件,执行完了,才会执行这个元素上的click事件!

想一想,为什么要这么设计?

为什么一个先触发的事件,需要完成其整个冒泡之后,再触发后一个事件?

posted @ 2015-12-20 17:45  ViCanary  阅读(357)  评论(0编辑  收藏  举报