冒泡捕获流
事件流:描述从页面中接收事件的顺序: 冒泡 捕获
IE 提出的 事件冒泡流(Event Bubbing)
Netspace 提出的 事件捕获流(Event Capturing)
分为三个阶段 顺序 事件捕获阶段 处于目标阶段 冒泡阶段
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.wrapper {
width: 300px;
height: 300px;
background-color: green;
}
.outer {
width: 200px;
height: 200px;
background-color: red;
}
.inner {
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="outer">
<div class="inner"></div>
</div>
</div>
<script>
var wrapper = document.getElementsByClassName('wrapper')[0],
outer = wrapper.getElementsByClassName('outer')[0],
inner = outer.getElementsByClassName('inner')[0];
wrapper.addEventListener(
'click',
function () {
console.log('捕获wrapper');
},
true
);
outer.addEventListener(
'click',
function () {
console.log('捕获outer');
},
true
);
inner.addEventListener(
'click',
function () {
console.log('目标(捕获)inner');
},
true
);
wrapper.addEventListener(
'click',
function () {
console.log('冒泡wrapper');
},
false
);
outer.addEventListener(
'click',
function () {
console.log('冒泡outer');
},
false
);
inner.addEventListener(
'click',
function () {
console.log('目标(冒泡)inner');
},
false
);
</script>
</body>
</html>
事件源对象
target srcElement 事件源对象
火狐只有target
IE只有srcElement
<body>
<div>哈哈哈</div>
<script>
var oDiv = document.getElementsByTagName('div')[0];
oDiv.onclick = function (e) {
console.log(e);
};
</script>
</body>
事件委托
点击li获取li的内容 这是就要循环遍历li 给每一个li都绑定上click事件
但是当点击按钮增加一个新的li时 又要手动绑定事件
当li非常多的时候这种方法是不可取的
此时就要把点击事件委托给父级ul 通过事件冒泡的形式 来优化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<button>增加</button>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script>
var oList = document.getElementsByTagName('ul')[0],
oLi = oList.getElementsByTagName('li'),
oBtn = document.getElementsByTagName('button')[0],
len = oLi.length,
item;
oList.onclick = function (e) {
var e = e || window.event,
tar = e.target || e.srcElement;
console.log(tar.innerText);
};
oBtn.onclick = function () {
var li = document.createElement('li');
li.innerText = oLi.length + 1;
oList.appendChild(li);
};
</script>
</body>
</html>
获取所点击li元素的下标
下面很多人会想到的方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<button>增加</button>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script>
var oList = document.getElementsByTagName('ul')[0],
oLi = oList.getElementsByTagName('li'),
len = oLi.length,
item;
oList.onclick = function (e) {
var e = e || window.event,
tar = e.target || e.srcElement;
for (var i = 0; i < len; i++) {
item = oLi[i];
if (tar === item) {
console.log(i);
}
}
};
</script>
</body>
</html>
这里推荐一种方法 使用indexOf
因为indexOf是数组上的方法 而获取到的li是类数组所不能直接使用 需要使用下面的方式
Array.prototype.indexOf.call(DOM对象集合, 当前事件源)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<button>增加</button>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script>
var oList = document.getElementsByTagName('ul')[0],
oLi = oList.getElementsByTagName('li'),
len = oLi.length,
item;
oList.onclick = function (e) {
var e = e || window.event,
tar = e.target || e.srcElement;
var index = Array.prototype.indexOf.call(oLi, tar);
console.log(index);
};
</script>
</body>
</html>
鼠标行为
clientX/Y 鼠标位置相对于当前可视区域的坐标 不包括滚动条的距离
layerX/Y 同pageX/Y相同 IE11下同clientX/Y
screenX/Y 鼠标位置相对于屏幕的坐标
X/Y 与clientX/Y相同
pageX/pageY 鼠标位置相对于当前文档的坐标 包含滚动条的距离 IE9下不支持
offsetX/Y 鼠标位置相对于块元素的坐标 包含边框
鼠标按键
e.button
左0 中1 右2
mouseUp 与 mouseDowm可以检测到鼠标按键事件
封装获取鼠标相对于可视区域坐标的方法
这里我们使用之前文章封装过的获取滚动条距离的函数getScrollOffset
function pagePos(e) {
var sLeft = getScrollOffset().left,
sTop = getScrollOffset().top,
cLeft = document.documentElement.clientLeft || 0,
cTop = document.documentElement.clientTop || 0;
return {
X: e.clientX + sLeft - cLeft,
Y: e.clientY + sTop - cTop,
};
}
封装多功能事件函数(拖拽元素 唤出menu 双击事件elemClick)
功能
单击拖拽元素 右击唤出menu 双击开启事件elemClick
这里使用我们之前文章封装过的获取元素样式属性值的函数getStyles
还有之前文章封装的添加事件的方法addEvent和取消事件的removeEvent
阻止默认事件的preventDefaultEvent取消冒泡的cancelBubble
还有上文获取鼠标相对于可视窗口坐标的pagePos
Element.prototype.dragNclick = function (menu, elemClick) {
var bTime = 0,
eTime = 0,
oPos = [],
//双击开始时间
cbTime = 0,
ceTime = 0,
// 点击次数
counter = 0,
t = null,
// 页面的宽高
wWidth = getViewportSize().width,
wHeight = getViewportSize().height,
//元素的宽高
eleWidth = getStyles(this, 'width'),
eleHeight = getStyles(this, 'height'),
mWidth = getStyles(menu, 'width'),
mHeight = getStyles(menu, 'height');
drag.call(this);
function drag() {
var x,
y,
_self = this;
addEvent(this, '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.left = 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);
cancelBubble(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) {
var e = e || window.event;
cancelBubble(e);
});
function mouseMove(e) {
var e = e || window.event,
eleLeft = pagePos(e).X - x,
eleTop = pagePos(e).Y - y;
// 如果鼠标离开了左侧
if (eleLeft <= 0) {
eleLeft = 0;
// 如果鼠标距离元素左边界的距离大于页面的宽度减去元素距离左侧的距离 就不变 使元素靠在最右侧
} else if (eleLeft >= wWidth - eleWidth) {
eleLeft = wWidth - eleWidth;
}
if (eleTop <= 0) {
eleTop = 0;
} else if (eleTop >= wHeight - eleHeight) {
eleTop = wHeight - eleHeight;
}
_self.top = pagePos(e).X - x + 'px';
_self.left = pagePos(e).Y - y + 'px';
}
function mouseUp(e) {
var e = e || window.event;
eTime = new Date().getTime();
if (eTime - bTime < 100) {
_self.left = oPos[0] + 'px';
_self.top = oPos[1] + 'px';
counter++;
if (counter === 1) {
cbTime = new Date().getTime();
}
if (counter === 2) {
ceTime = new Date().getTime();
}
if (cbTime && ceTime && ceTime - bTime < 200) {
elemClick();
}
t = setTimeout(function () {
ebTime = 0;
ceTime = 0;
counter = 0;
clearTimeout(t);
}, 200);
}
removeEvent(document, 'mousemove', mouseMove);
removeEvent(document, 'mouseup', mouseUp);
}
}
};