效果预览
源码地址
一、事件流
1、冒泡
- 什么是事件冒泡
官方的定义就是从最特定的事件目标到最不特定的事件目标。
意思就是说,假如用户单击了一个元素,该元素拥有一个click事件,那么同样的事件也将会被它的祖先触发,这个事件从该元素开始一直冒泡到DOM树的最上层,这一过程称为事件冒泡
示例:
html:
自定义右键菜单
js
document.oncontextmenu =function (){
alert('触发了);
}
效果
2、捕获
- 什么是事件捕获
事件捕获和事件是相反的,也就是说,当用户触发了一个事件的时候,这个事件是从DOM树的最上层开始触发一直到捕获到事件源.
标准的监听事件方式(标准浏览器都可使用,IE9以上)
element.addEventListener(eventType, fn, false)
说明:
- 第一个参数: eventType:事件类型
- 第二个参数:fn 触发的回调函数
- 第三个参数:一个布尔值,表示冒泡还是捕获。false表示冒泡,true表示捕获
IE下的监听事件方式(IE专有)
·attachEvent(eventType,fn)· IE专有。
说明:
此种监听方式只有冒泡没有捕获。
注意:
- 标准监听方式不需加on
document.addEventlistener('contextmenu',function (){
alert('我是标准的我不需要on')
},false)
- IE监听事件方式必须加on
document.attachEvent('oncontextmenu',function(){
alert('我是IE的我需要on')
})
一般使用标准即可,如果兼容IE 8以下再考虑第二种,做兼容方式的书写。
二、oncontextmenu
属于鼠标事件,鼠标右键点击即触发。
用法:
我在document
上绑定了此事件,通过事件冒泡机制触发。
document.oncontextmenu =function (){
alert('触发了);
}
3、事件对象event
概念:
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。
事件通常与函数结合使用,函数不会在事件发生前被执行
- 标准浏览器中作为事件回调函数的第一个参数
document.addEventlistener('contextmenu',function(ev){
alert(ev)
})
- 低版本的IE作为window对象的一个属性
document.attachEvent('onc ontextmenu',function(){
alert(window.event)
})
一般如果不兼容ie 8以下,用标准下的event即可
4、取消默认行为
event.preventDefault()
5、阻止冒泡
event.stopPropagation()
说明:
return false
既可以阻止默认行为,也可以阻止冒泡
6、获取滚动距离
- chrome:
document.body.scrollTop(scrollLeft)
- 非chrome:
document.documentElement.scrollTop(scrollLeft)
- 兼容写法:
var scrollTop = document.documentElement.scrollTop||document.body.scrollTop,
var scrollLeft = document.documentElement.scrollLeft||document.body.scrollLeft,
7、clientX(Y)
它提供事件发生时的应用客户端区域的水平坐标 (与页面坐标不同)。例如,当你点击客户端区域的左上角时,鼠标事件的 clientX 值为 0 ,这一值与页面是否有水平滚动无关
也就是说到浏览器可视窗口的距离(不包括浏览器的工具栏)
8、 获取元素具体尺寸
offsetHeight/offsetWidth
通常,元素的offsetHeight是一种衡量标准,包括元素的边框、垂直(水平)内边距和元素的水平(垂直)滚动条(如果存在且渲染的话)和元素的CSS高度(宽度)
注意:
与clientWidth/clientHeight
的区别是,`clientWidth/clientHeight 不包括边框,和垂直(水平)滚动条的宽度(高度)
9、获取浏览器视口的尺寸
var browserHeight = document.documentElement.clientHeight,//浏览器视口的高度
browserWidth = document.documentElement.clientWidth;
注意:
一开始使用了window.innerWidth/window.innerHeight
来获取视口尺寸,结果在极限情况下被滚动条覆盖一部分,后来找到原因是:window.innerWidth/window.innerHeight
是把滚动条也算在内的
10、类数组转换成数组
Array.prototype.slice.call(类数组,0)
11、自定义右键菜单实现思路
1、让自定义菜单相对于浏览器视口做绝对定位通过改变left/top
值来改变每次菜单的位置
2、取消原来的右键菜单默认行为
3、考虑极限情况,判断event.clientX+菜单的offsetWidth/event.clientY+offsetHeight
是否大于等于 document.documentElement.clientWidth/document.documemtElement.clientHeight
,如果大于等于,则把left/top
赋值为event.clientX-菜单的offsetWidth/event.clientY-菜单的offsetHeight
,否则赋值为event.clientX/event.clientY
js代码:
window.onload = function (){
var oClick = document.getElementById('click_region'),
oMenu = document.getElementById('menu'),
aLi = oMenu.getElementsByTagName('li'),
browserHeight = document.documentElement.clientHeight,//浏览器视口的高度
browserWidth = document.documentElement.clientWidth; //浏览器视口的快读,不包括垂直滚动天的宽度
document.oncontextmenu = function (ev){
oMenu.style.display = 'block';
var ev = ev||window.event,
scrollTop = document.documentElement.scrollTop||document.body.scrollTop,
scrollLeft = document.documentElement.scrollLeft||document.body.scrollLeft,
clientX = ev.clientX,
clientY = ev.clientY,
// 注意:只有在隐藏的元素变成display:block的状态才能获取他的宽度和高度
offsetWidth = oMenu.offsetWidth,
offsetHeight = oMenu.offsetHeight,
top,
left;
if(clientY+offsetHeight>=browserHeight){
top = clientY-offsetHeight
}else{
top = clientY
}
if(clientX+offsetWidth>=browserWidth){
left = clientX-offsetWidth
console.log(left);
}else{
left = clientX
}
oMenu.style.left = left+'px';
oMenu.style.top =scrollTop+top+'px';
return false//阻止默认行为,并且阻止冒泡
}
// 取消自定义菜单
document.onclick = function (){
oMenu.style.display = 'none';
}
var lis = Array.prototype.slice.call(aLi,0); //类数组转成数组
//遍历数组
lis.forEach(function (item,index,arr){
aLi[index].onclick = function (event){
alert(this.innerHTML)
event.stopPropagation();
}
})
}