js事件应用 —— 拖拽、自定义滚动条

js事件本身有较多的知识点,如事件类型、事件处理函数的注册等等,不多赘述。

强调一点,原教程中说到阻止默认行为时只提及返回 false 这种方法。除此方法之外,《JavaScript权威指南》中说到:在支持addEventListener()的浏览器中,也能通过调用事件对象的preventDefault() 方法取消事件的默认操作。不过,在IE9之前的IE中,可以通过设置对象的returnValue属性为false来达到同样的效果。

下面对原教程中js事件的一些应用进行总结。

1. 拖拽

1.1 基础

拖拽动作可分解为鼠标按下、鼠标移动、鼠标抬起三个动作,整个动作可理解为事件目标的位置与鼠标位置之间的差一定。这个位置差的计算比较简单,可以在二维空间上定义该位置差。用鼠标的Y坐标减去事件目标的top属性可得纵向差,用鼠标的X坐标减去事件目标的left属性可得横向差,即

diffx = oEvent.clientX - oDiv.offsetLeft;
diffy = oEvent.clientY - oDiv.offsetTop;

上述三个动作有一定的逻辑顺序,如先鼠标按下之后鼠标移动才能拖拽事件目标、鼠标抬起应当终止鼠标移动操作等,因此鼠标移动和鼠标抬起应该放在鼠标按下的处理程序中。

// not final
window.onload =  function(){
	var oDiv = document.getElementById("div1");
	var diffx = 0;
	var diffy = 0;
	oDiv.onmousedown = function(evt){
		var oEvent = evt|| window.event;
		
		diffx = oEvent.clientX - oDiv.offsetLeft;
		diffy = oEvent.clientY - oDiv.offsetTop;
		
		oDiv.onmousemove = function(evt){
			var oEvent = evt|| window.event;
			oDiv.style.top = oEvent.clientY - diffy + 'px';
			oDiv.style.left = oEvent.clientX - diffx + 'px';
		};	
		
		 oDiv.onmouseup =  function(){
			oDiv.onmousemove = null;
			oDiv.onmouseup = null; 
		};
	};
};

1.2 进阶

1.2.1 一些小问题的解决

基础的拖拽小程序中蕴藏着一些小问题,总结如下。

  1. 当鼠标拖动事件目标过快时,因为事件目标很小,鼠标容易移出事件目标。可使用的解决方法为:对文档本身进行鼠标移动事件监听而非事件目标。
  2. 当鼠标移出可视区时,因为鼠标已经不在事件目标上,所以鼠标抬起事件目标处理程序将不再起作用。可使用的解决方法为:对文档本身进行鼠标抬起事件监听而非事件目标。
  3. 鼠标移动有可能将事件目标拖出可视区。可使用的解决方法为:限制事件目标的可活动范围。本例中,事件目标的父级为文档,所以用最大的范围为可视区宽高减去事件目标宽高。在现实开发中,如果事件目标的父级非文档,则用父级宽高减去事件目标宽高即可。
  4. 在某些浏览器上出现默认行为。可使用的解决方法为:取消默认行为。
  5. 在IE早期版本中,拖动事件目标会选中事件目标周围的文字等元素。可使用的解决方法为:在返回false之前使用 setCapture() 进行事件捕获,这样,所有的事件都会被集成到鼠标按下事件中,其他元素就不会被选中。注意,在鼠标抬起事件处理程序中需调用releaseCapture()方法释放事件捕获。用if…else…将IE处理与其它浏览器处理分开(程序略。)

则,程序修改如下。

window.onload =  function(){
	var oDiv = document.getElementById("div1");
	var diffx = 0;
	var diffy = 0;
	oDiv.onmousedown = function(evt){
		var oEvent = evt|| window.event;
		
		diffx = oEvent.clientX - oDiv.offsetLeft;
		diffy = oEvent.clientY - oDiv.offsetTop;
		
		
		oDiv.onmousemove = function(evt){
			var oEvent = evt|| window.event;
			
			var legalTop = oEvent.clientY - diffy;
			var legalLeft = oEvent.clientX - diffx;
			
			var clientHeight =  document.documentElement.clientHeight || document.body.clientHeight;
			var clientWidth =  document.documentElement.clientWidth || document.body.clientWidth;
			
			if(legalLeft < 0)
				legalLeft = 0;
			else if(legalLeft > clientWidth - oDiv.offsetWidth)
				legalLeft = clientWidth - oDiv.offsetWidth;
			
			if(legalTop < 0)
				legalTop = 0;
			else if(legalTop > clientHeight - oDiv.offsetHeight)
				legalTop = clientHeight - oDiv.offsetHeight;
				
				
			oDiv.style.top = legalTop + 'px';
			oDiv.style.left = legalLeft + 'px';
			
		};
		
		
		 oDiv.onmouseup =  function(){
			oDiv.onmousemove = null;
			oDiv.onmouseup = null; 
		};
		return false;
	}; 
};

注意,原教程中并未对 clientHeight 和 clientWidth 进行兼容性处理。

1.2.2 磁性吸附

磁性吸附,即事件目标自动被吸附到父级边儿上。实现方法很简单,当距离在一定范围内时,直接将距离赋值到终点即可,如

if(legalLeft < 100)
	legalLeft = 0;
1.2.3 带框拖拽

带框拖拽的做法:创建一个与事件目标一样大小的框,鼠标移动时只移动框,鼠标抬起时删除框并移动事件目标到框所在的位置。

<style>
.box {border:1px dashed black; position:absolute;}
</style>

<script>
window.onload = function ()
{
	var oDiv = document.getElementById('div1');
	
	var diffx = 0;
	var diffy = 0;
	
	oDiv.onmousedown = function (evt)
	{
		var oEvent = evt || window.event;
		
		diffx = oEvent.clientX - oDiv.offsetLeft;
		diffy = oEvent.clientY - oDiv.offsetTop;
		
		var oBox = document.createElement('div');
		
		oBox.className = 'box';
		oBox.style.width = oDiv.offsetWidth - 2 + 'px';
		oBox.style.height = oDiv.offsetHeight - 2 + 'px';
		
		oBox.style.left = oDiv.offsetLeft + 'px';
		oBox.style.top = oDiv.offsetTop + 'px';
		
		document.body.appendChild(oBox);
		
		document.onmousemove = function (evt)
		{
			var oEvent = evt || window.event;
			var legalLeft = oEvent.clientX - diffx;
			var legalTop = oEvent.clientY - diffy;
			
			var clientHeight =  document.documentElement.clientHeight || document.body.clientHeight;	
			var clientWidth =  document.documentElement.clientWidth || document.body.clientWidth;

			if(legalLeft < 0)
				legalLeft = 0;
			else if(legalLeft > clientWidth - oDiv.offsetWidth)
				legalLeft = clientWidth - oDiv.offsetWidth;
				
			if(legalTop < 0)
				legalTop = 0;
			else if(legalTop > clientHeight - oDiv.offsetHeight)
				legalTop = clientHeight - oDiv.offsetHeight;
				
			oBox.style.left = legalLeft  + 'px';
			oBox.style.top = legalTop + 'px';
		};
		
		document.onmouseup=function ()
		{
			document.onmousemove = null;
			document.onmouseup = null;
			
			oDiv.style.left = oBox.offsetLeft + 'px';
			oDiv.style.top = oBox.offsetTop + 'px';
			
			document.body.removeChild(oBox);
		};
		
		return false;
	};
};
</script>

2. 自定义滚动条

自定义滚动条可以理解为具有父子级别关系的两个元素,子元素在父元素范围内进行拖拽。

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>ScrollBar</title>
<style>
#parent {width:600px; height:20px; background:#CCC; position:relative; margin:10px auto;}
#div1 {width:20px; height:20px; background:red; position:absolute; left:0; top:0;}
#div2 {width:400px; height:300px; border:1px solid black; overflow:hidden; position:relative;}
#div3 {position:absolute; left:0; top:0; padding:4px;}
</style>
<script>
window.onload = function (){
	var oDiv = document.getElementById('div1');
	var oDiv2 = document.getElementById('div2');
	var oDiv3 = document.getElementById('div3');
	var oParent = document.getElementById('parent');
	
	var diffx = 0;
	
	oDiv.onmousedown=function (evt){
		var oEvent = evt || window.event;
		disX = oEvent.clientX - oDiv.offsetLeft;
		
		document.onmousemove = function (evt){
			var oEvent = evt || window.event;
			var legalLeft = oEvent.clientX - diffx;
			
			if(legalLeft < 0){
				legalLeft = 0;
			}else if(legalLeft > oParent.offsetWidth - oDiv.offsetWidth){
				l = oParent.offsetWidth - oDiv.offsetWidth;
			}
			oDiv.style.left = legalLeft + 'px';
			
			var scale = legalLeft / (oParent.offsetWidth - oDiv.offsetWidth);	
			oDiv3.style.top = - scale * (oDiv3.offsetHeight - oDiv2.offsetHeight) + 'px';
		};
		
		document.onmouseup = function (){
			document.onmousemove = null;
			document.onmouseup = null;
		};
		
		return false;
	};
};
</script>
</head>

<body>
<div id="parent">
	<div id="div1"></div>
</div>
<div id="div2">
	<div id="div3">
		这里有很多文字,文字内容略。
    </div>
</div>
</body>
</html>

需要注意:

  1. 本例中假设滚动条在水平方向上滚动,所以限制范围时只需要限制且设置事件目标的left属性。
  2. div3实际可滚动的范围为其本身的宽高减去div2的宽高。

参考文献

【1】《JavaScript权威指南》
【2】智能社:JavaScript教程——从入门到精通 ( https://ke.qq.com/course/152997?taid=766917950461349

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值