目录 |
1.什么是事件
2.事件对象及兼容处理
3.事件的传播机制
4.案例-鼠标跟随jQuery
5.案例-鼠标跟随JS版
6.鼠标跟随、深入理解事件的传播机制
7.事件委托/事件代理
8.案例-京东商城放大镜
9.案例-百度搜索框
10.案例-多级菜单JS版
11.案例-多级菜单jQuery版
12.拖拽demo实现基本的效果
13.拖拽demo解决鼠标丢失
1.什么是事件 |
事件分为两部分:
1.行为本身:浏览器天生就赋予其的行为。onclick、onmouseover(onmouserenter)、onmouseout(onmouseleave)、onmousemove、ommousemove、onmousedown、onmouseup、onmousewheel(鼠标滚轮滚动行为)、onscroll(滚动条滚动行为)、onresize(window.onresize 浏览器窗口大小改变事件)、onload、onunload、onfouse文本框获取焦点行为)、onblur(浏览器失去焦点)、onkeydown(键盘按下行为)、onkeyup……
哪怕没有给上述行为绑定方法,事件也存在。当点击这个盒子的时候,同样会触发它的onclick行为,只是什么事情都没做而已。
2.事件绑定:给元素的某一个行为绑定一个方法
//DOM0级事件绑定
var oDiv = document.getElementById("div1");
oDiv.onclick = function () {
//当触发oDiv的onclick行为的时候,会把绑定的这个函数执行
};
//DOM2级事件绑定
oDiv.addEventListener("click", function () {
console.log("ok");
}, false);
onclick这个行为定义在当前元素的私有属性上;
addEventListener这个属性是定义在当前元素所属EventTarget这个类的原型上的
2.事件对象及兼容处理 |
oDiv.onclick = function () {
};
把匿名函数定义的部分当做一个值赋值给oDiv的点击行为(函数表达式)
当触发div的点击行为的时候,会执行对应绑定上的方法
不仅仅把绑定的方法执行了,而且浏览器还默认的给这个方法传递了一个参数值:MouseEvent:鼠标事件对象。
- MouseEvent是一个对象数据类型值,里面包含了很多的属性名和属性值,这些都是用来记录当前鼠标的相关信息的
- MouseEvent -> UIEvent -> Event -> Object
- MouseEvent记录的是页面中唯一一个鼠标每一次触发时候的相关信息,和到底是在哪个元素上触发的没有关系
关于事件对象(MouseEvent)的兼容性问题:
1、事件对象本身的获取存在兼容问题:标准浏览器中是浏览器给方法传递的参数,只需要定义形参e就可以获取到
oDiv.onclick = function (e) {
console.log(e);
};
2、在IE6-8中,浏览器不会给方法传递参数,如果需要的话,需要到window.event中获取查找。
解决方法:
oDiv.onclick = function (e) {
e = e || window.event;
console.log(e);
};
常用信息:
- e.clientX/e.clientY:鼠标触发点距离当前屏幕左上角的坐标值
- e.type:存储的是当前鼠标触发的行为类型,如“click”
- e.target:事件源,当前鼠标触发的是哪个元素,那么它存储的就是哪个元素。存在兼容性问题,在IE6-8中不存在这个属性,即为undefined,可以使用e.srcElement来获取事件源。解决方法:
e.target = e.target || e.srcElement;
- e.preventDefault:阻止浏览器的默认行为。如a标签的默认行为就是跳转页面,但是有时候使用a标签,只是想应用它的特殊性,并不想点击的时候跳转。存在兼容性问题,在IE6-8中不存在这个属性,需要使用e.returnValue = false;来代替。
a.onclick = function (e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : e.returnValue = false;
//或者:return false;
//或者:见下
};
<li><a href="javascript:;"></a></li>
/*
直接在HTML结构上阻止
或者:href="javascript:void 0;"
href="javascript:void 1;"
*/
- e.pageX/e.pageY:当前鼠标触发点距离body左上角(页面第一屏幕最左上角)的x/y轴的坐标。存在兼容性问题,在IE6-8中不存在这个属性,即为undefined,解决方法:
e.pageX = e.pageX || (e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
e.pageY = e.pageY || (e.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
- e.stopPropagation:阻止事件的冒泡传播,在IE6-8中不存在这个属性,解决方法如下:
e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
- e.keycode:当前键盘上每一个键对应的值,如:空格:32;回退键:8;回车键:13;delete:46;左:37;上:38;右:39;下:40
3.事件的传播机制 |
有以下代码:
<body>
<div id="outer">
<div id="inner">
<div id="center"></div>
</div>
</div>
<script type="text/javascript">
var outer = document.getElementById("outer"),
inner = document.getElementById("inner"),
center = document.getElementById("center");
document.body.onclick = function () {
console.log("body");
};
outer.onclick = function () {
console.log("outer");
};
inner.onclick = function () {
console.log("inner");
};
center.onclick = function () {
console.log("center");
};
</script>
</body>
点击时,控制台会输出:center、inner、outer、body,这就涉及到了事件的默认传播机制。
事件的默认传播机制分为三个阶段:
- 捕获阶段:从外向里依次查找元素
- 目标阶段:当前事件源本身的操作
- 冒泡阶段:从内到外依次触发相关的行为
最常用的就是冒泡阶段。
用DOM0级事件绑定给元素的某一个行为绑定的方法,都是在行为触发后的冒泡阶段把方法执行的。如图:
冒泡阶段:
- 当前center的click行为被触发,如果给其绑定了方法,首先把center对应的方法执行(目标阶段)
- 不仅仅center的click行为被触发了,它所有的父级元素的click行为也会被触发。inner的click行为触发->如果给inner也绑定了方法,inner对应的方法也会执行
- 同理依次查找outer、body、html、document
注意:每个浏览器传播到的最顶层是不一样的。谷歌中可以到document,IE中只能传播到HTML。
document.body.addEventListener("click", function () {
console.log("body");
}, false); //第一个参数是行为的类型,第二个参数是给当前的行为绑定的方法,第三个参数是控制在哪个阶段发生,true是捕获阶段发生,false是冒泡阶段发生
outer.addEventListener("click", function () {
console.log("outer");
}, true);
inner.addEventListener("click", function () {
console.log("inner");
}, false);
点击center的div,输出:outer、inner、body
4.案例-鼠标跟随jQuery |
效果图:
HTML代码:
<div id="box" class="box">
<img src="img/1.jpg" bigImg="img/11.jpg"/>
<img src="img/2.jpg" bigImg="img/22.jpg"/>
<img src="img/3.jpg" bigImg="img/33.jpg"/>
<img src="img/4.jpg" bigImg="img/44.jpg"/>
<div id="mark">
<img src="img/11.jpg"/>
</div>
</div>
CSS代码:
body, div, ing {
margin: 0;
padding: 0;
}
img {
display: block;
border: none;
}
.box {
width: 450px;
margin: 20px auto;
position: relative;
}
.box img {
width: 100px;
border: 1px solid #777777;
float: left;
margin-left: 10px;
}
#mark {
position: absolute;
top: 0px;
left: 0;
width: 384px;
height: 216px;
border: 1px solid #5bc0de;
z-index: 10;
display: none;
}
#mark img {
border: none;
float: none;
margin-left: 0px;
width: 100%;
height: 100%;
}
JavaScript代码(jQuery):
$(function () {
var $box = $("#box"),
$mark = $("#mark"),
$boxOffset = $box.offset(); //获取当前元素距离body的偏移
$box.children("img").mouseover(function (e) {
e = e || window.event;
e.target = e.target || e.srcElement;
var left = e.clientX - $boxOffset.left + 10;
var top = e.clientY - $boxOffset.top + 10;
$mark.stop().show(100).css({left: left, top: top}).find("img").attr("src", e.target.getAttribute("bigImg"));
}).bind("mousemove", function (e) {
e = e || window.event;
e.target = e.target || e.srcElement;
var left = e.clientX - $boxOffset.left + 10;
var top = e.clientY - $boxOffset.top + 10;
$mark.css({left: left, top: top});
}).bind("mouseout", function (e) {
$mark.stop().hide(100);
});
});
5.案例-鼠标跟随JS版 |
效果图:
HTML代码:
<div id="box">
<!--<div id="mark"></div>-->
</div>
CSS代码:
body, div {
margin: 0;
padding: 0;
}
#box {
position: relative;
margin: 20px auto;
width: 300px;
height: 300px;
background: #5bc0de;
}
#mark {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background: #FFAAAA;
}
JS代码:
var box = document.getElementById("box");
box.onmouseover = function (e) {
e = e || window.event;
var mark = document.createElement("div");
mark.id = "mark";
this.appendChild(mark);
mark.style.left = e.clientX - this.offsetLeft + 5 + "px";
mark.style.top = e.clientY - this.offsetTop + 5 + "px";
};
box.onmousemove = function (e) {
e = e || window.event;
var mark = document.getElementById("mark");
if (mark) {
mark.style.left = e.clientX - this.offsetLeft + 5 + "px";
mark.style.top = e.clientY - this.offsetTop + 5 + "px";
}
};
以上代码会出现一个问题:当鼠标移动过快的时候会进入到mark盒子,触发到它的mouseover行为,由于事件的冒泡传播机制,导致box的mouseover会重新触发,导致盒子一直被触发
解决方法:阻止mark盒子的onmouseover行为的冒泡传播
如果增加onmouseout事件,依然有问题,鼠标快速移动,首先会到mark上,此时浏览器在计算mark的位置,计算完成,mark到达指定的位置,此时鼠标又重新回到box上,触发了box的mouseover,也触发了mark的mouseout,mark的mouseout被触发,也会传播到box的mouseout上,会把mark先删除,然后再创建……
box.onmouseout = function (e) {
e = e || window.event;
var mark = document.getElementById("mark");
if (mark) {
this.removeChild(mark);
}
};
onmouseenter和onmouseover的区别:
都是鼠标滑上去的行为,但是onmouseenter默认阻止了浏览器的冒泡传播,即:mark的mouseenter行为触发,不会传播到box。而onmouseover是存在冒泡传播的,想要阻止的话只能自己写代码阻止。
onmouseleave和onmouseout同理。
所以代码可以写成:
var box = document.getElementById("box");
box.onmouseenter = function (e) {
e = e || window.event;
var mark = document.createElement("div");
mark.id = "mark";
this.appendChild(mark);
mark.style.left = e.clientX - this.offsetLeft + 5 + "px";
mark.style.top = e.clientY - this.offsetTop + 5 + "px";
};
box.onmousemove = function (e) {
e = e || window.event;
var mark = document.getElementById("mark");
if (mark) {
mark.style.left = e.clientX - this.offsetLeft + 5 + "px";
mark.style.top = e.clientY - this.offsetTop + 5 + "px";
}
};
box.onmouseleave = function (e) {
e = e || window.event;
var mark = document.getElementById("mark");
if (mark) {
this.removeChild(mark);
}
};
6.鼠标跟随、深入理解事件的传播机制 |
效果图:鼠标滑到粉盒子上时,蓝盒子出现,在蓝盒子上时,不消失。
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
body, div {
margin: 0;
padding: 0;
}
#box {
position: relative;
width: 100px;
height: 30px;
background: #FFAAAA;
}
#mark {
position: absolute;
left: 0;
top: 30px;
width: 300px;
height: 100px;
background: #5bc0de;
display: none;
}
</style>
</head>
<body>
<div id="box">
<div id="mark"></div>
</div>
<script type="text/javascript">
var box = document.getElementById("box");
var mark = document.getElementById("mark");
box.onmouseenter = function () {
mark.style.display = "block";
};
box.onmouseleave = function () {
mark.style.display = "none";
};
</script>
</body>
</html>
7.事件委托、事件代理 |
利用事件的冒泡传播机制,即触发当前元素的某个行为,它父级所有元素的相关行为都会被触发。利用这个机制,如果容器中有很多元素都需要绑定点击事件,没有必要一个个的绑定,只需要给最外层容器绑定一个点击事件即可,在这个方法执行的时候,通过事件源(e.target)的区分来进行不同的操作。
事件委托的简单应用 |
效果图:
说明:
点击购物车,出现粉色框,点击粉色框不消失,点击购物车消失,点击空白处消失。
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
body, div, span {
margin: 0;
padding: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
}
html,body {
width: 100%;
height: 100%;
overflow: hidden;
}
#box {
position: relative;
left: 50%;
top: 50px;
margin-left: -50px;
width: 100px;
height: 30px;
border: 1px solid #9783b9;
line-height: 30px;
text-align: center;
cursor: pointer;
}
#mark {
position: absolute;
top: 30px;
left: -1px;
width: 300px;
height: 100px;
line-height: 100px;
text-align: center;
background: #FFAAAA;
border: 1px solid #9783b9;
}
</style>
</head>
<body>
<div id="box">
<span>购物车</span>
<div id="mark" style="display: none">查看购物车的详细信息</div>
</div>
<script type="text/javascript">
var mark = document.getElementById("mark");
document.body.onclick = function (e) {
console.log("111");
e = e || window.event;
e.target = e.target || e.srcElement;
//如果点击的是box或者#box下的span,判断mark是否显示,显示就隐藏,反之显示
if (e.target.id === "box" || (e.target.tagName.toLowerCase() === "span" && e.target.parentNode.id === "box")) {
if (mark.style.display === "none") {
mark.style.display = "block";
} else {
mark.style.display = "none";
}
return;
}
//如果事件源是#mark,不进行任何操作
if (e.target.id === "mark") {
return;
}
//以上都不是,直接让#mark隐藏
mark.style.display = "none";
};
</script>
</body>
</html>
8.案例-京东商城放大镜 |
效果图:
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
body, div, img {
margin: 0;
padding: 0;
}
img {
display: block;
border: none;
}
#box {
position: absolute;
top: 20px;
left: 20px;
width: 350px;
height: 350px;
border: 1px;
box-shadow: 3px 3px 10px 0 #111;
}
#box img {
width: 350px;
height: 350px;
}
#mark {
position: absolute;
top: 0;
left: 0;
width: 175px;
height: 175px;
background: #000;
opacity: 0.5;
filter: alpha(opacity=50);
cursor: move;
display: none;
}
#boxRight {
display: none;
position: absolute;
width: 350px;
height: 350px;
top: 20px;
left: 440px;
overflow: hidden;
}
/*右侧图片大小是左边2倍*/
#boxRight img {
position: absolute;
width: 700px;
height: 700px;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div id="box">
<img src="img/jd.jpg"/>
<div id="mark"></div>
</div>
<div id="boxRight">
<img src="img/jd.jpg"/>
</div>
<script type="text/javascript">
//放大镜原理:
//mark横向是box的一半,纵向也是box的一半,那么右侧大图的横向和纵向应该是左边小图的二倍
var box = document.getElementById("box"),
mark = document.getElementById("mark"),
boxRight = document.getElementById("boxRight");
//设置mark盒子的位子信息
function setPosition(e) {
//正常情况下获取的top/left,但是需要做边界判断
var top = e.clientY - box.offsetTop - (mark.offsetHeight / 2);
var left = e.clientX - box.offsetLeft - (mark.offsetWidth / 2);
//边界判断:
var tempL = 0, tempT = 0;
var minL = 0, minT = 0, maxL = box.offsetWidth - mark.offsetWidth, maxT = box.offsetHeight - mark.offsetHeight;
if (left < minL) {
mark.style.left = minL + "px";
tempL = minL;
} else if (left > maxL) {
mark.style.left = maxL + "px";
tempL = maxL;
} else {
mark.style.left = left + "px";
tempL = left;
}
if (top < minT) {
mark.style.top = minT + "px";
tempT = minT;
} else if (top > maxT) {
mark.style.top = maxT + "px";
tempT = maxT;
} else {
mark.style.top = top + "px";
tempT = top;
}
//让右侧的图片跟着运动
var oImg = boxRight.getElementsByTagName("img")[0];
oImg.style.left = -tempL * 2 + "px";
oImg.style.top = -tempT * 2 + "px";
}
box.onmouseenter = function (e) {
e = e || window.event;
mark.style.display = "block";
setPosition(e);
boxRight.style.display = "block";
};
box.onmousemove = function (e) {
e = e || window.event;
setPosition(e);
};
box.onmouseleave = function (e) {
e = e || window.event;
mark.style.display = "none";
boxRight.style.display = "none";
};
</script>
</body>
</html>
9.案例-百度搜索框 |
效果图:
分析:
显示时情况:
- 文本框获取焦点,并且文本框中有内容的时候
- 在文本框中输入或者删除内容时,如果内容没有清空就显示,否则就隐藏
隐藏时情况:
- 点击页面中其余的位置(除文本框和searchList里面的每一行),都隐藏
- 点击searchList中的列表隐藏,但是还要把列表中的内容放到文本框中
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
body, div,input, ul, li {
margin: 0;
padding: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
}
input {
display: block;
outline: none; /*清除彩色边框*/
}
a {
display: block;
text-decoration: none;
color: #000;
}
a:hover, a:active, a:target {
text-decoration: none;
color: #000;
}
ul, li {
list-style: none;
}
.box {
width: 500px;
position: absolute;
top: 20px;
left: 50%;
margin-left: -250px;
}
.box input {
width: 300px;
height: 35px;
padding: 0 10px;
border: 1px solid #888;
}
.box ul {
display: none;
border: 1px solid #888;
position: relative;
top: -1px;
}
.box ul li, .box ul a {
height: 30px;
line-height: 35px;
}
.box ul a {
padding: 0 10px;
}
.box ul a:hover {
background: #eee;
}
</style>
</head>
<body>
<div class="box">
<input type="text" id="searchInp"/>
<ul id="searchList">
<li><a href="javascript:;">冰箱</a></li>
<li><a href="javascript:;">彩电</a></li>
<li><a href="javascript:;">洗衣机</a></li>
<li><a href="javascript:;">废品</a></li>
<li><a href="javascript:;">废塑料</a></li>
</ul>
</div>
<script type="text/javascript">
var searchInp = document.getElementById("searchInp"),
searchList = document.getElementById("searchList");
//不管是获取焦点还是在里面编辑内容,都是有内容显示,没内容隐藏
searchInp.onfocus = searchInp.onkeyup = function () {
var val = this.value.replace(/(^ +| +$)/g, ""); //获取文本框中的内容,并且去除它的首尾空格
searchList.style.display = val.length > 0 ? "block" : "none";
};
document.body.onclick = function (e) {
e = e || window.event;
e.target = e.target || e.srcElement;
//如果点击的事件源是searchList下的a标签,让searchList隐藏,并且把当前点击的内容放到文本框中
if (e.target.tagName.toLowerCase() === "a" && e.target.parentNode.parentNode.id === "searchList") {
searchList.style.display = "none";
searchInp.value = e.target.innerHTML;
return;
}
//如果事件源是文本框还需要单独处理
// if(e.target.id==="searchInp"){
// return;
// }
searchList.style.display = "none";
};
//可以阻止一个容器中某些特殊性的元素,让其不在委托的范围内,只需要把这些不需要委托的事件阻止即可
searchInp.onclick = function (e) {
e = e || window.event;
e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
};
</script>
</body>
</html>
10.案例-多级菜单JS版 |
效果图:
完整代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
}
ul, li {
list-style: none;
}
.box {
width: 300px;
margin: 10px;
padding: 10px;
border: 1px dashed #5bc0de;
/*渐进增强:首先设置一个纯色的背景,对于不兼容css3的浏览器来说会使用纯色,对于兼容的浏览器来说,下面再额外的增加一些渐变色,这样的话会把纯色的覆盖掉*/
background: #FFAAAA;
background: -webkit-linear-gradient(top left, #5bc0de, #bce8f1);
}
.box li {
position: relative;
line-height: 30px;
}
.box em {
position: absolute;
width: 16px;
height: 16px;
background: #777777;
top: 7px;
left: 0;
cursor: pointer;
}
.box em.open {
background: #333;
}
.box span {
display: block;
padding-left: 20px;
}
.box .two {
margin-left: 20px;
}
.box .three {
margin-left: 40px;
}
.box .four {
margin-left: 60px;
}
.box .two, .box .three, .box .four {
display: none;
}
</style>
</head>
<body>
<div class="box" id="box">
<ul>
<li>
<em></em><span>第一级第一个</span>
<ul class="two">
<li><span>第二级第一个</span></li>
<li>
<em></em><span>第二级第二个</span>
<ul class="three">
<li><span>第三级第一个</span></li>
<li><span>第三级第二个</span></li>
<li>
<em></em><span>第三级第三个</span>
<ul class="four">
<li><span>第四级第一个</span></li>
<li><span>第四级第二个</span></li>
<li><span>第四级第三个</span></li>
</ul>
</li>
</ul>
</li>
<li>
<em></em><span>第二级第三个</span>
<ul class="three">
<li><span>第三级第一个</span></li>
<li><span>第三级第二个</span></li>
<li><span>第三级第三个</span></li>
</ul>
</li>
</ul>
</li>
<li>
<em></em><span>第一级第二个</span>
<ul class="two">
<li><span>第二级第一个</span></li>
<li>
<em></em><span>第二级第二个</span>
<ul class="three">
<li><span>第三级第一个</span></li>
<li><span>第三级第二个</span></li>
<li>
<em></em><span>第三级第三个</span>
<ul class="four">
<li><span>第四级第一个</span></li>
<li><span>第四级第二个</span></li>
<li><span>第四级第三个</span></li>
</ul>
</li>
</ul>
</li>
<li>
<em></em><span>第二级第三个</span>
<ul class="three">
<li><span>第三级第一个</span></li>
<li><span>第三级第二个</span></li>
<li><span>第三级第三个</span></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<script type="text/javascript" src="utils.js"></script>
<script type="text/javascript">
var box = document.getElementById("box");
//把列表中的span(前面带em的)设置一个cursor: pointer;的样式
var spanList = box.getElementsByTagName("span");
for (var i = 0; i < spanList.length; i++) {
var curSpan = spanList[i];
var curPre = utils.prev(curSpan); //获取当前span的上一个哥哥元素节点
if (curPre && curPre.tagName.toLowerCase() === "em") {
curSpan.style.cursor = "pointer";
}
}
//使用事件委托实现操作
box.onclick = function (e) {
e = e || window.event;
var tar = e.target || e.srcElement;
//只有点击的是em/span标签才进行展开或者收缩的操作
if (/^(em|span)$/i.test(tar.tagName)) {
var parent = tar.parentNode; //获取父亲
var oEm = utils.children(parent, "em")[0];
var firstUl = utils.children(parent, "ul")[0];//获取父亲子集中第一个ul
if (firstUl) { //只有存在才进行相关的操作
//这个ul当前是隐藏让其显示,否则隐藏
var isBlock = utils.css(firstUl, "display") === "block" ? true : false;
if (isBlock) { //当前是显示的
firstUl.style.display = "none";
oEm ? utils.removeClass(oEm, "open") : null;
//当外层的收起,里层所有的ul都隐藏,并且所有的em都要移除open样式
var allUl = parent.getElementsByTagName("ul"), allEm = parent.getElementsByTagName("em");
for (var i = 0; i < allEm.length; i++) {
allUl[i].style.display = "none";
utils.removeClass(allEm[i], "open");
}
} else { //当前是隐藏的
firstUl.style.display = "block";
oEm ? utils.addClass(oEm, "open") : null;
}
}
}
};
</script>
</body>
</html>
utils.js见:http://blog.csdn.net/ruirui_1996/article/details/78116836
11.案例-多级菜单jQuery版 |
var $box = $("#box");
$box.find("span").each(function () {
//$(this) 每一次循环当前循环的这个元素
var $pre = $(this).prev();
if ($pre[0] && $pre[0].tagName.toLowerCase() === "em") {
$(this).css("cursor", "pointer");
}
});
function fn() {
var $par = $(this).parent();
var $ul = $($par.children("ul")[0]);
var $em = $($par.children("em")[0]);
if ($ul.length > 0) {
var isBlock = $ul.css("display") === "block" ? true : false;
$ul.slideToggle();
$em.toggleClass("open");
//当前如果是收缩,需要把子子孙孙中所有的ul/em都隐藏和移除open样式
if (isBlock) {
$par.find("ul").css("display", "none");
$par.find("em").removeClass("open");
}
}
}
$box.delegate("em", "click", fn); //给$box绑定点击事件,如果当前的事件源是em的话执行fn
$box.delegate("span", "click", fn);
12.拖拽demo实现基本的效果 |
原理:
当鼠标在盒子上按下的时候开始拖拽(给盒子绑定onmousemove和onmouseup)。当鼠标移动的时候,计算盒子的最新位置,当鼠标抬起的时候,拖拽已经结束了,move和up就没用了,再把move和up移除。
开始记录一下鼠标的开始位置strX/strY和盒子的开始位置strListrT。
当鼠标移动的过程中,获取最新的鼠标位置,用现有的位置减起始位置等于鼠标移动的距离A
当前盒子的位置=盒子的起始位置+鼠标移动的距离A
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
#box {
position: absolute;
/*top: 50%;*/
/*left:50%;*/
/*margin: -100px 0 0 -100px;*/
/*或者:*/
/*position: absolute;*/
/*top: 0;*/
/*left: 0;*/
/*right: 0;*/
/*bottom: 0;*/
/*margin: auto;*/
/*IE下兼容不好 主要用于移动端的开发*/
width: 200px;
height: 200px;
background: #5bc0de;
cursor: move;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
var box = document.getElementById("box");
box.style.top = ((document.documentElement.clientHeight || document.body.clientHeight) - box.offsetHeight) / 2 + "px";
box.style.left = ((document.documentElement.clientWidth || document.body.clientWidth) - box.offsetWidth) / 2 + "px";
box.onmousedown = down;
function down(e) {
e = e || window.event;
//记录开始位置的信息
this["strX"] = e.clientX;
this["strY"] = e.clientY;
this["strL"] = parseFloat(this.style.left);
this["strT"] = parseFloat(this.style.top);
//给元素绑定移动和抬起的事件
this.onmousemove = move;
this.onmouseup = up;
}
function move(e) {
e = e || window.event;
var curL = e.clientX - this["strX"] + this["strL"];
var curT = e.clientY - this["strY"] + this["strT"];
console.lo
//边界判断
var minL = 0, minT = 0,
maxL = (document.documentElement.clientWidth || document.body.clientWidth) - this.offsetWidth,
maxT = (document.documentElement.clientHeight || document.body.clientHeight) - this.offsetHeight;
curL = curL < minL ? minL : (curL > maxL ? maxL : curL);
curT = curT < minT ? minT : (curT > maxT ? maxT : curT);
this.style.left = curL + "px";
this.style.top = curT + "px";
}
function up(e) {
this.onmousemove = null;
this.onmouseup = null;
}
</script>
</body>
</html>
13.拖拽demo解决鼠标丢失 |
鼠标焦点丢失:
当鼠标移动过快的时候,鼠标会脱离盒子,导致盒子的mouseup和mousemove事件都移除不掉。
在IE和火狐中,可以使用setCapture()方法将鼠标和盒子绑在一起,用releaseCapture()解绑,但是在谷歌下不兼容。
谷歌下解决方法:
鼠标始终在文档中,把mousemove和mouseup绑定给document
优化后JS代码:
<script type="text/javascript">
var box = document.getElementById("box");
box.style.top = ((document.documentElement.clientHeight || document.body.clientHeight) - box.offsetHeight) / 2 + "px";
box.style.left = ((document.documentElement.clientWidth || document.body.clientWidth) - box.offsetWidth) / 2 + "px";
box.onmousedown = down;
function down(e) {
e = e || window.event;
//记录开始位置的信息
this["strX"] = e.clientX;
this["strY"] = e.clientY;
this["strL"] = parseFloat(this.style.left);
this["strT"] = parseFloat(this.style.top);
//给元素绑定移动和抬起的事件
if (this.setCapture) { //把当前鼠标和this绑定在一起
this.setCapture();
this.onmousemove = move;
this.onmouseup = up;
} else {
var _this = this; //_this是#box
document.onmousemove = function (e) {
// move(e); //move中的方法为window
move.call(_this,e);
};
document.onmouseup = function (e) {
up.call(_this,e);
};
}
}
function move(e) {
e = e || window.event;
var curL = e.clientX - this["strX"] + this["strL"];
var curT = e.clientY - this["strY"] + this["strT"];
//边界判断
var minL = 0, minT = 0,
maxL = (document.documentElement.clientWidth || document.body.clientWidth) - this.offsetWidth,
maxT = (document.documentElement.clientHeight || document.body.clientHeight) - this.offsetHeight;
curL = curL < minL ? minL : (curL > maxL ? maxL : curL);
curT = curT < minT ? minT : (curT > maxT ? maxT : curT);
this.style.left = curL + "px";
this.style.top = curT + "px";
}
function up(e) {
if (this.releaseCapture) {//把当前的鼠标和this解绑
this.releaseCapture();
this.onmousemove = null;
this.onmouseup = null;
} else {
document.onmousemove = null;
document.onmouseup = null;
}
}
</script>