目录
注册事件(2种方式)
给元素添加事件,称为注册事件和绑定事件
注册事件有两种方式:传统方式和监听注册方式
<body>
<button>传统方式</button>
<button>监听注册方式</button>
<button>IE9之前支持</button>
<script>
var btn = document.querySelectorAll('button');
// 1、传统方式
/**
* 特点:注册事件的唯一性
* 同一个元素同一个事件只能设置一个处理函数,最后的
* 处理函数会覆盖前面的处理函数
*/
btn[0].onclick = function () {
alert('按下了1');
}
btn[0].onclick = function () {
alert('按下了2');
}
// 2、监听注册方式
/**
* w3c推荐方式
* IE9之前不支持此方法,可使用attachEvent()代替
* 特点:同一个元素可以注册多个监听器,按注册顺序依次执行
* 三个参数:
* 参数一:事件类型 省略开头on
* 参数二:要调用的函数
* 参数三:可选参数
*/
btn[1].addEventListener('click', function () {
alert('按下了1')
});
btn[1].addEventListener('click', function () {
alert('按下了2')
});
// 3、attachEvent() IE9之前支持
/**
* 事件类型开头要带on
*/
btn[2].attachEvent('onclick', function () {
alert('按下了');
})
</script>
</body>
事件监听兼容性解决方案
function addEventListener(element, eventName, fn) {
// 判断浏览器是否支持addEventListener方法
if (element.addEventListener) {
// 监听注册方式
element.addEventListener(eventName, fn); // 第三个参数默认为false
} else if (element.attachEvent) {
// IE9之前支持
element.attachEvent('on' + eventName, fn);
} else {
// 传统方式
element['on' + eventName] = fn;
}
}
删除事件(解绑事件)
传统方式:
(1)eventTarget.onclick = null;
监听注册方式:
(1)eventTarget.removeEventListener(事件类型,处理函数);
(2)eventTarget.detachEvent(事件类型,处理函数);
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var box = document.querySelectorAll('div');
// 1、onclick = null;
box[0].onclick = function () {
console.log('0');
// this.onclick = null;
box[0].onclick = null;
}
// 2、removeEventListener()
/**
* 注意:
* (1) 这种方式想要删除事件,参数二必须是外部调用的函数
* (2) 函数不用加小括号
*/
box[1].addEventListener('click', fn);
function fn() {
console.log('1');
box[1].removeEventListener('click', fn);
}
// 3. detachEvent IE9之前支持
box[2].attachEvent('onclick', fn1);
function fn1() {
alert(33);
box[2].detachEvent('onclick', fn1);
}
</script>
</body>
删除事件兼容性解决方案
function removeEventListener(element, eventName, fn) {
// 判断浏览器是否支持removeEventListener方法
if (element.removeEventListener) {
// 监听注册方式
element.removeEventListener(eventName, fn); // 第三个参数默认为false
} else if (element.detachEvent) {
// IE9之前支持
element.detachEvent('on' + eventName, fn);
} else {
// 传统方式
element['on' + eventName] = null;
}
}
DOM事件流
html中的标签都是相互嵌套的,我们可以将元素想象成一个盒子装一个盒子,document是最外面的大盒子。
当你单击一个div时,同时你也单击了div的父元素,甚至整个页面。那么是先执行父元素的单击事件,还是先执行div的单击事件 ???
事件流描绘的是从页面中接受事件的顺序
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程为DOM事件流
比如:我们给页面一个按钮注册了点击事件,点击了按钮时,也就点击了body,点击了html,点击了document。
DOM事件流会经历三个阶段:
1、捕获阶段
2、当前目标阶段
3、冒泡阶段
举个例子:往水里丢石头,石头往水中下沉的过程称为捕获。石头落到水地产生泡泡,往水面上浮的过程称为冒泡。
注意:
1、JS代码中只能执行捕获或者冒泡其中一个阶段。
2、onclick和attachEvent只能得到冒泡阶段。
3、addEventListener()第三个参数如果是true,表示在事件捕获阶段,如果是空或者false,表示在冒泡阶段。
4、实际开发更关注事件冒泡。
5、有些事件没有冒泡。比如:onblur、onfocus、onmouseenter、onmouseleave
6、事件冒泡有时候会带来麻烦,有时候又可以巧妙做某些事情。
<body>
<div id="container">
<div id="box">
</div>
</div>
<script>
// 事件冒泡
// onclick 和 attachEvent 在冒泡阶段触发
var con = document.querySelector('#container');
var box = document.getElementById('box')
// 里面盒子绑定事件
con.addEventListener('click', function () {
alert('0');
}, false);
// 外面盒子绑定事件
box.addEventListener('click', function () {
alert('1');
}, false)
// document注册事件
document.addEventListener('click', function () {
alert('2');
})
// 事件会以由内而外触发
// 执行顺序:.box -> .container -> body -> html -> element
</script>
</body>
<body>
<div id="container">
<div id="box">
</div>
</div>
<script>
// 事件捕获
// addEventListener第三个参数是true,那么在捕获阶段触发
var con = document.querySelector('#container');
var box = document.getElementById('box')
// 里面盒子绑定事件 第三个参数为true
con.addEventListener('click', function () {
alert('0');
}, true);
// 外面盒子绑定事件 第三个参数为true
box.addEventListener('click', function () {
alert('1');
}, true)
// document注册事件 第三个参数为true
document.addEventListener('click', function () {
alert('2');
}, true)
// 事件会以从外到内触发
// 执行顺序:element -> html -> body -> .container -> .box
// 2 -> 0 -> 1
</script>
</body>
事件对象
什么是事件对象?
事件发生后,跟事件有关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象。
比如:
1、谁绑定了这个事件。
2、鼠标触发事件,会得到鼠标的相关信息,如鼠标位置
3、键盘触发事件的话,会得到键盘的相关信息,如按了哪个键
事件对象的使用
事件触发发生时就会产生事件对象,并且系统会以实参的形式传给事件处理函数。
所以,在事件处理函数中声明一个形参用来接收事件对象
var box = document.getElementById('box');
box.onclick = function (event) {
// 这个event就是事件对象,还可以命名为 e 或 evt
console.log(event);
}
事件对象的兼容性处理
事件对象本身的获取存在兼容问题
1、标准浏览器中是浏览器给方法传递的参数,只需要定义形参e中就可以获取到
2、在IE6 ~ 8 中,浏览器不会给方法传递参数,如果需要的话,需要到window.event中查找
解决办法: e = e || window.event;
<body>
<div id="box">
</div>
<script>
var box = document.getElementById('box');
box.onclick = function (event) {
event = event || window.event;
console.log(event);
}
</script>
</body>
事件对象的属性和方法
事件对象属性方法 | 说明 |
e.target | 返回触发事件的对象 标准 |
e.srcElement | 返回触发事件的对象 非标准 IE6 ~ 8使用 |
e.type | 返回事件的类型 比如click mouseover 不带on |
e.cancelBubble | 该属性阻止冒泡 非标准 IE6 ~ 8使用 |
e.returnValue | 该属性阻止默认事件(默认行为)非标准 IE6 ~ 8使用 |
e.preventDefault() | 该属性阻止默认事件(默认行为)标准 比如不让链接跳转 |
e.stopPropagation() | 阻止冒泡 标准 |
e.target 和 this 的区别
-
this 是事件绑定的元素(绑定这个事件处理函数的元素) 。
-
e.target 是事件触发的元素。
常情况下terget 和 this是一致的,
但有一种情况不同,那就是在事件冒泡时(父子元素有相同事件,单击子元素,父元素的事件处理函数也会被触发执行),
这时候this指向的是父元素,因为它是绑定事件的元素对象,
而target指向的是子元素,因为他是触发事件的那个具体元素对象。
<body>
<ul>
<li>1</li>
<li>2</li>
</ul>
<script>
var ul = document.querySelector('ul');
ul.onclick = function (event) {
/**
* 一般情况下是一致的,但是在冒泡情况下是如下这样
*/
console.log(this); // 返回绑定事件的元素对象 ul
console.log(event.target); // 返回触发事件的元素对象 li
}
</script>
</body>
阻止默认行为
html中有一些标签有默认行为,例如a标签点击后,默认会进行页面跳转。
<body>
<a href="http://www.baidu.com">链接</a>
<script>
var a = document.querySelector('a');
a.addEventListener('click', function (e) {
e.preventDefault(); // 阻止默认事件
})
// 传统的注册方式
a.onclick = function (e) {
// 普通浏览器使用
e.preventDefault();
// IE678使用
e.returnValue = false;
// 利用返回false也可以阻止默认事件
return false;
}
</script>
</body>
阻止冒泡
事件冒本身的特性,会带来坏处,也会带来好处.、
标准写法:利用事件对象里面的stopPropgation方法
e.stopPropgation( )
非标准写法:IE6 ~ 8 利用事件对象cancelBubble属性
e.cancelBubble = true;
<body>
<div class="container">
<div class="box">
</div>
</div>
<script>
var con = document.querySelector('.container');
var box = document.querySelector('.box');
// 给外面盒子注册事件
con.addEventListener('click', function (e) {
alert(0);
}, false)
// 给里面盒子注册事件
box.addEventListener('click', function (e) {
alert(1);
// 标准阻止冒泡方法
e.stopPropagation();
// 非标准阻止传播IE 6 ~ 8
window.event.cancelBubble = true;
}, false)
</script>
</body>
阻止事件冒泡的兼容性处理
// 判断存在事件对象而且有事件对象的stopPropagation方法
if (e && e.stopPropagation) {
e.stopPropagation();
} else {
window.event.cancelBubble = true;
}
事件委托
事件冒泡本身的特性,会带来坏处,也会带来好处
什么是事件委托?
就是把事件委托给别人,代为处理
事件委托也称为事件代理,在jQuery里面称为事件委派。
说白了就是,不给子元素注册事件,给父元素注册事件,把处理代码在父元素的事件中执行。
生活中的代理:
班上有100个学生,快递员有100个快递,如果一个一个送花费的时间很长。同时每个同学排队领取的时候,花费的时间也长。
解决方案:
快递员把100个快递,委托给班主任,班主任把这些快递放在办公室,同学们下课自行领取即可。
优势:
快递员省事,委托给班主任就走了,同学们领取也方便。
js事件中的代理:
<ul>
<li>这是li</li>
<li>这是li</li>
<li>这是li</li>
<li>这是li</li>
<li>这是li</li>
</ul>
点击每个li都会弹出对话框,以前需要给每个li注册事件,不方便,而且访问DOM的次数越多,就会延长整个页面的交互就绪时间。
事件委托的原理
给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。
事件委托的作用
只操作了一次DOM,提高了程序的性能。
动态新创建的子元素,也拥有事件。
<body>
<ul>
<li>这是li</li>
<li>这是li</li>
<li>这是li</li>
<li>这是li</li>
<li>这是li</li>
</ul>
<script>
/**
* 事件委托的原理:给父元素添加事件,利用事件冒泡影响每一个子节点
*/
var ul = document.querySelector('ul');
ul.onclick = function (e) {
console.log(e.target); // 打印事件的触发元素
e.target.className = 'bgc'; // 改变事件触发者的类名
}
</script>
</body>
常用鼠标事件
案例:禁止文字选中和禁止右键菜单
1、禁止鼠标右键菜单
contextmenu主要控制应该何时显示上下文菜单,用于程序员取消默认的上下文菜单。
btn.addEventListener('contextmenu', function (e) {
e.stopPropagation();
})
2、禁止鼠标选中(selectstart 开始选中)
btn.addEventListener('selectstart', function (e) {
e.stopPropagation();
})
<body>
<p>这是一段不愿意分享的文字</p>
<script>
/**
* 可以实现不允许复制的效果
*/
document.addEventListener('contextmenu', function (e) {
// 禁止右键菜单
e.preventDefault();
})
document.addEventListener('selectstart', function (e) {
// 禁止鼠标选中
e.preventDefault();
})
</script>
</body>
鼠标事件对象
event事件对象是事件相关的一系列信息的集合
现阶段主要是用鼠标事件对象MouseEvent和键盘事件对象KeyboardEvent
获取鼠标在页面的坐标
<body>
<script>
document.addEventListener('click', function (e) {
// 返回相对于浏览器窗口可视区的坐标
console.log(e.clientX);
console.log(e.clientY);
console.log('-------------------');
// 返回相当于文档页面的坐标
console.log(e.pageX);
console.log(e.pageY);
console.log('-------------------');
// 返回相对于电脑屏幕的坐标
console.log(e.screenX);
console.log(e.screenY);
})
</script>
</body>
跟随鼠标的小天使案例
<style>
img {
position: absolute;
}
</style>
<body>
<img src="./angel.gif" alt="小天使">
<script>
/**
* 核心思路:
* 给图片添加绝对定位,document添加鼠标移动事件,鼠标的x坐标、Y坐标,当做天使的top值left值
*/
var img = document.querySelector('img');
document.addEventListener('mousemove', function (e) {
// 返回相当于浏览器窗口可视区的坐标
console.log(e.clientX);
console.log(e.clientY);
var x = e.clientX;
var y = e.clientY;
img.style.left = x - 40 + 'px';
img.style.top = y - 34 + 'px';
})
</script>
</body>
常用的键盘事件
键盘事件 | 触发条件 |
onkeyup | 某个按键被松开时触发 |
onkeydown | 某个按键被按下时触发 |
onkeypress | 某个按键被按下时触发,不识别功能键,比如:箭头、Ctrl、Shift |
<script>
// onkeyup 键盘弹起触发
document.addEventListener('keyup', function () {
console.log('我弹起了');
}, false)
// onkeydown 键盘按下触发 识别功能键
document.onkeydown = function () {
console.log('我按下了');
}
// onkeypress 键盘按下触发 不识别功能键
document.onkeypress = function () {
console.log('press按下');
}
// 三个事件的执行顺序:onkeydown -> onkeypress -> onkeyup
</script>
键盘事件对象
键盘事件对象属性 | 说明 |
keyCode | 返回该键的ASCII值 |
注意:
(1)onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。
(2)在我们实际开发中,我们更多的使用keydown 和 keyup。
(3)keypress 不识别功能键,但是keyCode 属性能区分大小写,返回不同的ASCII值。
<script>
// 键盘事件对象可以得到相应键位的ASCII值
document.addEventListener('keyup', function (e) {
// console.log(e.keyCode);
if (e.keyCode === 65) {
console.log('按下了a键');
} else {
console.log('没有按下a键');
}
})
// onkeypress 区分大小写
document.addEventListener('keypress', function (e) {
console.log(e.keyCode);
})
</script>
模拟京东按下S键,自动获取焦点
<body>
<input type="text">
<script>
/**
* 当按下s键时,利用输入框的focus()来获取焦点
*/
// 获取输入框
var input = document.querySelector('input');
// 文档添加键盘事件
document.addEventListener('keyup', function (e) {
// 判断是否按下s键
if (e.keyCode === 83) {
// 输入框获取焦点
input.focus();
}
})
</script>
</body>
模拟京东查询快递单号
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
div:nth-last-child(2) {
width: 200px;
height: 30px;
background-color: #f1f1f1;
border-radius: 5px;
position: relative;
}
input {
margin-top: 5px;
width: 200px;
height: 25px;
}
</style>
<body>
<input type="text">
<div class="con"></div>
<script>
var con = document.querySelector('.con');
var input = document.querySelector('input');
// 键盘事件
/**
* 键盘按下判断文本框是否为空
* 空则不显示提示框
* 不空则显示提示框,且将文本框的内容赋值给提示框
*/
document.addEventListener('keyup', function () {
if (input.value.length == 0) {
con.style.display = 'none';
} else {
con.style.display = 'block';
con.innerHTML = input.value;
}
})
// 失去焦点事件
// 当失去焦点时,不显示提示框
input.onblur = function () {
con.style.display = 'none';
}
// 获取焦点事件
// 获取焦点时,文本框有内容则显示提示框,否则不显示
input.onfocus = function () {
if (this.value != '') {
con.style.display = 'block';
} else {
con.style.display = 'none';
}
}
</script>
</body>
效果: