一、鼠标事件深入
(1)执行顺序
mousedown > mouseup > click
(2)鼠标对应的码
- 左键 = 0
- 中间 = 1
- 右键 = 2
事件对象的button属性里面可以访问
二、点击与拖拽分离
(1)模块的两种写法
等待执行的模块
var dragNclick = (function (a, b) {
console.log(a, b);
})
dragNclick(1, 2)
立即执行的模块
init();
function init() {
dragNclick;
}
var dragNclick = (function (a, b) {
console.log(a, b);
})(1, 2);
(2)模块拖动案例
业务需求:
- 点击的时候跳转
- 拖动鼠标抬起的时候不跳转
- 鼠标拖动并跳转链接的时候回到原位
等待执行模块化
var dragNclcik = (function (elem, elemclick) {
var bTime = 0, // 记录点击时间
eTime = 0,
oPos = []; // 记录原先位置
drag();
function drag() {
var x,
y;
addEvent(elem, 'mousedown', function (e) {
var e = e || window.event;
bTime = new Date().getTime();
oPos = [getStyles(elem, 'left'), getStyles(elem, 'top')]
x = pagePos(e).X - getStyles(elem, 'left');
y = pagePos(e).Y - getStyles(elem, 'top');
addEvent(document, 'mousemove', mouseMove);
addEvent(document, 'mouseup', mouseUp);
cancleBubble(e);
preventDefaultEvent(e);
})
function mouseMove(e) {
var e = e || window.event;
elem.style.top = pagePos(e).Y - y + 'px';
elem.style.left = pagePos(e).X - x + 'px';
}
function mouseUp(e) {
var e = e || window.event;
eTime = new Date().getTime();
console.log(eTime - bTime);
console.log(oPos[0]);
if (eTime - bTime < 100) {
elem.style.left = oPos[0] + 'px';
elem.style.top = oPos[1] + 'px';
elemclick();
}
removeEvent(document, 'mousemove', mouseMove);
removeEvent(document, 'mouseup', mouseUp);
}
}
})
执行调用:
<script type="text/javascript">
var oLink = document.getElementsByTagName('a')[0];
dragNclcik(oLink, function () {
window.open('http://www.baidu.com');
});
</script>
写到原形上面
Element.prototype.dragNclcik = (function (elemclick) {
var bTime = 0, // 记录点击时间
eTime = 0,
oPos = []; // 记录原先位置
drag.call(this);
function drag() {
var x,
y,
_self = this;
addEvent(_self, 'mousedown', function (e) {
var e = e || window.event;
bTime = new Date().getTime();
oPos = [getStyles(_self, 'left'), getStyles(_self, 'top')]
x = pagePos(e).X - getStyles(_self, 'left');
y = pagePos(e).Y - getStyles(_self, 'top');
addEvent(document, 'mousemove', mouseMove);
addEvent(document, 'mouseup', mouseUp);
cancleBubble(e);
preventDefaultEvent(e);
})
function mouseMove(e) {
var e = e || window.event;
_self.style.top = pagePos(e).Y - y + 'px';
_self.style.left = pagePos(e).X - x + 'px';
}
function mouseUp(e) {
var e = e || window.event;
eTime = new Date().getTime();
console.log(eTime - bTime);
console.log(oPos[0]);
if (eTime - bTime < 100) {
_self.style.left = oPos[0] + 'px';
_self.style.top = oPos[1] + 'px';
elemclick();
}
removeEvent(document, 'mousemove', mouseMove);
removeEvent(document, 'mouseup', mouseUp);
}
}
})
执行调用:
<script type="text/javascript">
var oLink = document.getElementsByTagName('a')[0];
oLink.dragNclcik(function () {
window.open('http://www.baidu.com');
});
</script>
注意: this指向问题
模块边界问题
Element.prototype.dragNclcik = (function (elemclick) {
var bTime = 0, // 记录点击时间
eTime = 0,
oPos = [], // 记录原先位置
wWidth = getViewportSize().width,
wHeight = getViewportSize().height,
elemWidth = getStyles(this, 'width'),
elemHeight = getStyles(this, 'height');
drag.call(this);
function drag() {
var x,
y,
_self = this;
addEvent(_self, 'mousedown', function (e) {
var e = e || window.event;
bTime = new Date().getTime();
oPos = [getStyles(_self, 'left'), getStyles(_self, 'top')]
x = pagePos(e).X - getStyles(_self, 'left');
y = pagePos(e).Y - getStyles(_self, 'top');
addEvent(document, 'mousemove', mouseMove);
addEvent(document, 'mouseup', mouseUp);
cancleBubble(e);
preventDefaultEvent(e);
})
function mouseMove(e) {
var e = e || window.event,
elemLeft = pagePos(e).X - x,
elemTop = pagePos(e).Y - y;
if (elemLeft <= 0) {
elemLeft = 0;
} else if (elemLeft >= wWidth - elemWidth) {
elemLeft = wWidth - elemWidth - 1; // 避免误差
}
if (elemTop <= 0) {
elemTop = 0;
} else if (elemTop >= wHeight - elemHeight) {
elemTop = wHeight - elemHeight - 1;
}
_self.style.top = elemTop + 'px';
_self.style.left = elemLeft + 'px';
}
function mouseUp(e) {
var e = e || window.event;
eTime = new Date().getTime();
console.log(eTime - bTime);
console.log(oPos[0]);
if (eTime - bTime < 100) {
_self.style.left = oPos[0] + 'px';
_self.style.top = oPos[1] + 'px';
elemclick();
}
removeEvent(document, 'mousemove', mouseMove);
removeEvent(document, 'mouseup', mouseUp);
}
}
})
执行调用:
<script type="text/javascript">
var oLink = document.getElementsByTagName('a')[0];
oLink.dragNclcik(function () {
window.open('http://www.baidu.com');
});
</script>
右击菜单边界问题
Element.prototype.dragNclcik = (function (menu, elemclick) {
var bTime = 0, // 记录点击时间
eTime = 0,
oPos = [], // 记录原先位置
wWidth = getViewportSize().width,
wHeight = getViewportSize().height,
elemWidth = getStyles(this, 'width'),
elemHeight = getStyles(this, 'height'),
mWidth = getStyles(menu, 'width'),
mHeight = getStyles(menu, 'height');
drag.call(this);
function drag() {
var x,
y,
_self = this;
addEvent(_self, 'mousedown', function (e) {
var e = e || window.event,
btnCode = e.button;
if (btnCode === 2) {
var mLeft = pagePos(e).X,
mTop = pagePos(e).Y;
if (mLeft <= 0) {
mLeft = 0;
} else if (mLeft >= wWidth - mWidth) {
mLeft = pagePos(e).X - mWidth;
}
if (mTop <= 0) {
mTop = 0;
} else if (mTop >= wHeight - mHeight) {
mTop = pagePos(e).Y - mHeight;
}
menu.style.left = mLeft + 'px';
menu.style.top = mTop + 'px';
menu.style.display = 'block';
} else if (btnCode === 0) {
bTime = new Date().getTime();
oPos = [getStyles(_self, 'left'), getStyles(_self, 'top')]
menu.style.display = 'none';
x = pagePos(e).X - getStyles(_self, 'left');
y = pagePos(e).Y - getStyles(_self, 'top');
addEvent(document, 'mousemove', mouseMove);
addEvent(document, 'mouseup', mouseUp);
cancleBubble(e);
preventDefaultEvent(e);
}
})
addEvent(document, 'contextmenu', function (e) {
var e = e || window.event;
preventDefaultEvent(e);
})
addEvent(document, 'click', function (e) {
menu.style.display = 'none';
})
addEvent(menu, 'click', function (e) {
cancleBubble(e)
})
function mouseMove(e) {
var e = e || window.event,
elemLeft = pagePos(e).X - x,
elemTop = pagePos(e).Y - y;
if (elemLeft <= 0) {
elemLeft = 0;
} else if (elemLeft >= wWidth - elemWidth) {
elemLeft = wWidth - elemWidth - 1; // 避免误差
}
if (elemTop <= 0) {
elemTop = 0;
} else if (elemTop >= wHeight - elemHeight) {
elemTop = wHeight - elemHeight - 1;
}
_self.style.top = elemTop + 'px';
_self.style.left = elemLeft + 'px';
}
function mouseUp(e) {
var e = e || window.event;
eTime = new Date().getTime();
console.log(eTime - bTime);
console.log(oPos[0]);
if (eTime - bTime < 100) {
_self.style.left = oPos[0] + 'px';
_self.style.top = oPos[1] + 'px';
elemclick();
}
removeEvent(document, 'mousemove', mouseMove);
removeEvent(document, 'mouseup', mouseUp);
}
}
})
执行调用:
<script type="text/javascript">
var oLink = document.getElementsByTagName('a')[0],
oMenu = document.getElementsByTagName('div')[0];
oLink.dragNclcik(oMenu, function () {
window.open('http://www.baidu.com');
});
</script>
三、双击事件
Element.prototype.dragNclcik = (function (menu, elemclick) {
var bTime = 0, // 记录点击时间
eTime = 0,
oPos = [], // 记录原先位置
cbTime = 0,
ceTime = 0,
counter = 0,
t = null,
wWidth = getViewportSize().width,
wHeight = getViewportSize().height,
elemWidth = getStyles(this, 'width'),
elemHeight = getStyles(this, 'height'),
mWidth = getStyles(menu, 'width'),
mHeight = getStyles(menu, 'height');
drag.call(this);
function drag() {
var x,
y,
_self = this;
addEvent(_self, 'mousedown', function (e) {
var e = e || window.event,
btnCode = e.button;
if (btnCode === 2) {
var mLeft = pagePos(e).X,
mTop = pagePos(e).Y;
if (mLeft <= 0) {
mLeft = 0;
} else if (mLeft >= wWidth - mWidth) {
mLeft = pagePos(e).X - mWidth;
}
if (mTop <= 0) {
mTop = 0;
} else if (mTop >= wHeight - mHeight) {
mTop = pagePos(e).Y - mHeight;
}
menu.style.left = mLeft + 'px';
menu.style.top = mTop + 'px';
menu.style.display = 'block';
} else if (btnCode === 0) {
bTime = new Date().getTime();
oPos = [getStyles(_self, 'left'), getStyles(_self, 'top')]
menu.style.display = 'none';
x = pagePos(e).X - getStyles(_self, 'left');
y = pagePos(e).Y - getStyles(_self, 'top');
addEvent(document, 'mousemove', mouseMove);
addEvent(document, 'mouseup', mouseUp);
cancleBubble(e);
preventDefaultEvent(e);
}
})
addEvent(document, 'contextmenu', function (e) {
var e = e || window.event;
preventDefaultEvent(e);
})
addEvent(document, 'click', function (e) {
menu.style.display = 'none';
})
addEvent(menu, 'click', function (e) {
cancleBubble(e)
})
function mouseMove(e) {
var e = e || window.event,
elemLeft = pagePos(e).X - x,
elemTop = pagePos(e).Y - y;
if (elemLeft <= 0) {
elemLeft = 0;
} else if (elemLeft >= wWidth - elemWidth) {
elemLeft = wWidth - elemWidth - 1; // 避免误差
}
if (elemTop <= 0) {
elemTop = 0;
} else if (elemTop >= wHeight - elemHeight) {
elemTop = wHeight - elemHeight - 1;
}
_self.style.top = elemTop + 'px';
_self.style.left = elemLeft + 'px';
}
function mouseUp(e) {
var e = e || window.event;
eTime = new Date().getTime();
if (eTime - bTime < 200) {
_self.style.left = oPos[0] + 'px';
_self.style.top = oPos[1] + 'px';
counter++;
if (counter === 1) {
cbTime = new Date().getTime();
}
if (counter === 2) {
ceTime = new Date().getTime();
}
if (cbTime && ceTime && (ceTime - cbTime < 200)) {
console.log(ceTime - cbTime, counter);
elemclick();
}
t = setTimeout(function () {
cbTime = 0;
ceTime = 0;
counter = 0;
clearTimeout(t)
}, 500);
}
removeEvent(document, 'mousemove', mouseMove);
removeEvent(document, 'mouseup', mouseUp);
}
}
})
执行调用:
<script type="text/javascript">
var oLink = document.getElementsByTagName('a')[0],
oMenu = document.getElementsByTagName('div')[0];
oLink.dragNclcik(oMenu, function () {
window.open('http://www.baidu.com');
});
</script>
(1)为什么要清除计时器?
因为计时器是保存在内存里面的,不用了把它清除是一种好习惯
(2)为什么要清零
cbTime = 0;
ceTime = 0;
counter = 0;
因为你点击第一次的时候会保存cbTime
第二次点击会保存ceTime
但是如果ceTime - cbTime ≮ 200的话,cbTime、ceTime、counter就不会清零,也不会执行函数
往后点counter就会从2一直往后加,往后加就不会重新获取时间戳,然后cbTime、ceTime一直都是固定的,而且ceTime - cbTime ≮ 200,就一直进入不到if语句里面
所以要进行清零,因为当ceTime - cbTime ≮ 200的时候,就可以重新获取时间戳,然后计算它们的差值
(3)为什么要用延时器清零?
t = setTimeout(function () {
cbTime = 0;
ceTime = 0;
counter = 0;
clearTimeout(t)
}, 500);
因为当你只点击了一次的时候,它没有清除,然后下一次双击的时候就会出现问题,因为counter是从第一次点击时开始计算的,就是从1开始加,加到2、3,当你第二次进行双击的时候ceTime - cbTime 肯定就大于 200毫妙了,然后就进入不到if语句,只要counter不清零,就永远不会重新获取时间戳,不重新获取时间戳就无法重新计算ceTime - cbTime,就进入不到语句。
所以设置延时器清零,就是为当你只点击了1次的情况,它会延迟500毫妙自动给你清零,然后你下一次双击的时候,就可以重新获取时间戳,重新计算ceTime - cbTime,从而进入语句。
四、多人协作开发插件
; (function () {
var Test = function (opt) {
this.num1 = opt.num1;
this.num2 = opt.num2;
this.btnGroup = opt.btnGroup;
}
Test.prototype = {
// 执行bindEvent
init: function () {
this.bindEvent();
},
// 写所有的事件、事件处理函数
bindEvent: function () {
var btns = this.btnGroup,
_self = this;
addEvent(btns, 'click', function (e) {
_self.compute.call(_self, e)
})
},
// 自己写的插件
compute: function (e) {
var e = e || window.event,
tar = e.target || e.srcElement,
val1 = parseInt(this.num1.value),
val2 = parseInt(this.num2.value),
btns = this.btnGroup,
sign;
sign = tar.getAttribute('data-sign')
switch (sign) {
case 'plus':
console.log(val1 + val2);
break;
case 'minus':
console.log(val1 - val2);
break;
case 'mul':
console.log(val1 * val2);
break;
case 'div':
console.log(val1 / val2);
break;
default:
console.log('出错了');
}
}
}
window.Test = Test;
})()
执行调用:
<script type="text/javascript">
var test = new Test({
num1: document.getElementById('num1'),
num2: document.getElementById('num2'),
btnGroup: document.getElementsByClassName('btn-group')[0]
}).init(); // 启用插件
</script>
(1)this访问问题
方法一:提升到封闭作用域的最顶端
; (function () {
var _self;
var Test = function (opt) {
_self = this;
}
Test.prototype = {
}
window.Test = Test;
})()
方法二:call/apply执行事件处理函数里面的函数
bindEvent: function () {
var btns = this.btnGroup,
_self = this;
addEvent(btns, 'click', function (e) {
_self.compute.call(_self, e)
})
}
(2)init & bindEvent
这两个是写在原形上面的,init用来控制插件是否执行,bindEvent里面放所有的事件处理函数,init里面写执行bindEvent方法
五、作业
(1)windows桌面
要求:
- 写四个图标可以随意拖动
- 鼠标hover到图标上面的时候有一个透明的背景,点击的时候聚集
- 右键图标会有一个菜单
- 点击这个图标会弹出来一个窗口,可以关闭这个窗口
- 桌面底部写一个任务栏