JavaScript WebAPI学习
事件高级
注册事件
- 注册事件分为两种方式:传统注册方式和监听注册方式
传统注册事件
- 特点:注册事件唯一性 (同一个元素同一个事件只能设置一个处理函数,后注册的函数回把先注册的给覆盖掉)
<body>
<button>传统注册事件</button>
<script>
var btn = document.querySeleter('button');
btn.onclick = function() {
alert('传统注册事件');
}
btn.onclick = function() {
alert('我会把前面覆盖掉');
}
</script>
</body>
方法监听注册方式 (addEventListener)
-
监听注册:
eventTarget.addEventListener(type,listener,[useCapture])
- eventTarget:目标对象
- type:事件处理函数,例如click,mouseover,这里不需要带on
- listener:事件处理函数,事件发生时会调用监听函数
- useCapture:可选参数,默认为 false (这个参数下面DOM参数流会讲)
-
有兼容性问题:IE9 之前不支持,可以使用
attachEvent()
代替 -
同一个元素同一个事件可以注册多个监听器
-
使用实例:
<body>
<button>传统注册事件</button>
<script>
var btn = document.querySeleter('button');
btn.addEventListener('click', function() {
alert('方法监听注册');
})
</script>
</body>
兼容注册方式(attachEvent)
-
这种方法是非标准的,不推荐使用
-
注册事件:
eventTarget.attachEvent(eventNameWithOn, callback)
- eventNameWithOn:事件类型字符串,如onclick,这里要带on
- callback:事件处理函数,当目标触发事件时回调函数被调用
-
代码示例:
<body>
<button>传统注册事件</button>
<script>
var btn = document.querySeleter('button');
btn.attachEvent('onclick',function() {
alert('只有ie9才能支持这种方法');
})
</script>
</body>
- 只有ie9以下才支持,其他浏览器都会报错
删除事件(解绑事件)
-
传统删除方式:
eventTarget.onclick = null
-
方法监听删除方式:
eventTarget.removeEventListener(type,listener,[useCapture])
- 删除事件的时候不能使用匿名函数,函数必须有名字才可以删除
-
与 ie9 以前的浏览器兼容的删除方式:
eventTarget.detachEvent(eventNameWithOn,callback)
- 同样,只有 IE9 之前能运行
-
代码示例:
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var divs = document.querySeletorAll('div');
// 传统删除方法
divs[0].onclick = function() {
alert('传统方法删除事件');
divs[0].onclick = null;
}
// 方法监听删除方式
divs[1].addEventListener('click',fn)
function fn() {
alert('方法监听删除事件');
div[1].removeEventListener('click',fn);
}
// 与 ie9 以前的浏览器兼容的删除方式
div[2].attachEvent('onclick',fn1);
function fn1() {
alert('与 ie9 以前的浏览器兼容的删除方式');
div[2].detachEvent('onclick',fn1);
}
</script>
</body>
DOM事件流
- 事件传播的过程叫做DOM事件流
-
从 document -> html -> body -> ···
-
DOM事件流分为三个阶段:
- 捕获阶段 (一层一层去找绑定事件的元素)
- 当前目标阶段 (找到了!)
- 冒泡阶段 (一层一层去冒泡去返回到 document)
-
个人感觉可以把网页想象成一层一层的状态,document 在最外层,div等元素在最里层,当我们点击鼠标后,他会一层一层向下寻找,直到找到绑定点击事件的元素
事件冒泡是IE提出的!IE队 + 1分
事件捕获是网景提出的,网景是一家美国的浏览器公司
DOM事件流示例
- js代码中只能执行捕获或者冒泡其中的一个阶段
- onclick 和 attachEvent 只能得到冒泡阶段
- addEventListener 既能获得捕获也能获得冒泡阶段
- 第三个参数是 true 时则事件处于捕获阶段时调用程序
- 第三个参数为 false 时则事件处于冒泡阶段时调用程序
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
/ 捕获阶段调用 /
// 假如父亲和儿子都有一个onclick事件,捕获阶段调用程序时,先捕获到父亲,后捕获到儿子
var son = document.querySelector('.son');
son.addEventListener('click',function() {
alert('儿子捕获阶段');
},true);
var father = document.querySelector('.father');
father.addEventListener('click',function() {
alert('父亲捕获阶段');
},true);
// 输出:
// 父亲捕获阶段
// 儿子捕获阶段
/ 冒泡阶段调用 /
// 这时和捕获阶段正好相反,冒泡是先从儿子这里冒泡,然后再到父亲冒泡
son.addEventListener('click',function() {
alert('儿子冒泡阶段');
},false);
father.addEventListener('click',function() {
alert('父亲冒泡阶段');
},false);
// 输出:
// 儿子冒泡阶段
// 父亲冒泡阶段
</script>
</body>
- 有些事件不会冒泡,例如:
onblur, onfocus, onmouseenter, onmouseleave
事件对象
div.onclick = function(event) {}
-
上面代码里的 event 就是事件对象 (感觉像形参)
-
事件对象有了事件才会存在 (例如 onclick 等),他是系统自动创建的,不需要我们传递参数
-
事件对象是事件相关数据的集合,例如鼠标点击就包含鼠标的相关信息,如果是键盘事件就包含键盘的信息
-
event 不是关键字,可以起任意的名字
-
同样有兼容性问题:IE9 以下不支持
- IE9 通过
window.event
来调用事件对象 (必须一模一样) - 解决兼容性问题:
event = event || window.event
- IE9 通过
事件对象常见的属性和方法
参数值 | 说明 |
---|---|
e.target | 返回触发事件的对象 |
e.type | 返回事件类型,不带 on |
e.stopPropagation() | 阻止冒泡 |
e.preventDefault() | 阻止默认事件,例如不让链接跳转 |
e.srcElement | 返回触发事件的对象 (IE6-8使用) |
e.cancelBubble | 阻止冒泡 (IE6-8使用) |
e.returnValue | 阻止默认事件,例如不让链接跳转 (IE6-8使用) |
this 和 e.target 的区别
-
e.target 返回的是触发事件的对象,this 返回的是绑定事件的对象
-
下面的例子能更帮助理解
-
这里我们给 ul 绑定点击事件,但是我们点击的是 li ,this 输出的是绑定事件的元素,所以他还是会输出 ul ,但是 e.target 返回的是触发事件的元素,所以他会返回的是 li
<body>
<ul>
<li>123</li>
</ul>
<script>
var ul = document.querySeletor('ul');
ul.addEventListener('click', function(e) {
// 这里 this 指向ul
console.log(this);
// 我们如果点击的是 li, target 返回的是 li
console.log(e.target);
})
</script>
</body>
- 与 this 有一个相似的属性
currentTarget
(同样有兼容性问题,所以用的较少)
阻止默认行为
- 可以让链接不在跳转,或者阻止表单提交
<body>
<a href="#">123</a>
<script>
var a = document.querySeletor('a');
a.addEventListener('click', function(e) {
// 例如让链接不跳转
e.preventDefault();
})
a.onclick = function() {
// return false 也能阻止默认行为,并且没有兼容性问题 但是 return 后面的代码不执行 并且这种方法仅限于传统注册方式
return false;
}
</script>
</body>
阻止事件冒泡
stopPropagation()
方法阻止事件冒泡- 接着沿用上面的一个实例
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
son.addEventListener('click',function() {
alert('儿子冒泡阶段');
// 有下面一句代码以后点击事件就不在向上传播了,也就是只输出 儿子冒泡阶段 一句话
e.stopPropagation();
},false);
father.addEventListener('click',function() {
alert('父亲冒泡阶段');
},false);
</script>
</body>
事件委托
-
使用场景:假如很多个 li 都需要绑定一个点击事件,这时注册事件所花费的时间就很多
-
事件委托的原理:假如很多子节点都需要绑定同一个事件,我们可以把这个事件绑定在父节点上,然后我们利用冒泡影响每一个子节点
-
代码示例:我们要给一堆 li 绑定点击事件,我们可以把事件绑定在父元素 ul 上,根据冒泡原理,我们点击 li 会向父元素冒泡,也就触发了点击事件
<body>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<script>
// 点击 li 让那个 li 变色
var ul = document.querySelector('ul');
ul.addEventListener('click',function(e) {
e.target.style.backgroundColor = 'pink';
})
</script>
</body>
一些小知识
-
小知识:禁止鼠标右键菜单
document.addEventListener('contextmenu',function(e) { e.preventDefault(); })
-
小知识:禁止选中
// selectstart 开始选中 document.addEventListener('selectstart',function(e) { e.preventDefault(); })
鼠标事件对象
- 鼠标事件对象
MouseEvent
鼠标事件对象 | 说明 |
---|---|
e.clientX(e.clientY) | 返回鼠标相对于浏览器窗口可视区域的 X / Y 坐标 |
e.pageX(e.pageY) | 返回鼠标相对于文档页面的X / Y 坐标 (有兼容性问题) |
e.screenX(e.screenY) | 返回鼠标相对于电脑屏幕的 X / Y 坐标 |
- e.clientX:无论页面滚动到那里,都是相对于页面可视区域的左上角的坐标
- 相对的e.pageX获取的就是整个网页的坐标
鼠标事件对象实例:跟随鼠标的天使
-
鼠标移动事件:
mousemove
-
给 document 注册事件
-
gif 图要使用绝对定位,不能占用位置
-
当使用绝对定位时千万不要忘了单位
-
代码示例:
<head>
<style>
img {
position: absolute;
}
</style>
</head>
<body>
<img src="images/angel.gif">
<script>
var img = document.querySelector('img');
document.addEventListener('mousemove', function (e) {
// 千万不能忘了单位
img.style.top = e.pageY + 'px';
img.style.left = e.pageX + 'px';
})
</script>
</body>
- 商城里的商品放大图就是用的这个原理
常用键盘事件
键盘事件 | 说明 |
---|---|
onkeyup | 某个键被松开时触发 |
onkeydown | 某个键按下时触发 |
onkeypress | 某个按键按下时触发 (但是他不能识别功能键,例如 ctrl shift 回退等) |
- 按下触发是按下时一直触发
- 执行顺序:down > press
- 注意:回退键也是功能键
键盘事件对象
-
获取按下的是哪个键:key (兼容性较差),keycode 返回按键的 ASSII 码值
-
注意:keyup 和 keydown 不区分字母大小写
-
keypress区分字母大小写
document.addEventListener('keyup', function (e) {
console.log(e.keyCode);
// 输出 97
})
document.addEventListener('keypress', function (e) {
console.log(e.keyCode);
// 输出 65
})
- 注意:keydown 和 keypress 是在文本框还没有输入的时候触发
键盘事件示例:按键输入案例
- 用户输入时自动把光标定位到文本框中
- 聚焦文本框:使用 js 里面的
focus()
方法
<body>
<input type="text">
<script>
var search = document.querySelector('input');
document.addEventListener('keydown', function () {
search.focus();
})
</script>
</body>
模拟京东快递单号查询案例
- 当我们输入快递单号时,上面会出现一个框里面的字会更大方便观看,获得焦点时出现盒子,失去焦点隐藏盒子
- 注意不要命名不要命名为保留字 (尤其是top)
<style>
.con {
height: 25px;
width: 100px;
text-align: center;
line-height: 25px;
font-size: 22px;
background-color: rgb(163, 79, 79);
display: none;
}
</style>
<body>
<div class="search">
<input type="text" placeholder="请输入您的快递单号">
<div class="con">123</div>
</div>
<script>
var con = document.querySelector('.con');
var text = document.querySelector('.search').querySelector('input');
text.addEventListener('keyup', function () {
// 如果内容为空,则隐藏
if (this.value == '') {
con.style.display = 'none';
} else {
con.style.display = 'block';
con.innerText = this.value;
}
})
// 失去焦点隐藏
text.addEventListener('blur', function () {
con.style.display = 'none';
})
// 获得焦点出现
text.addEventListener('focus', function () {
// 防止没有焦点时也出现
if (this.value !== '') {
con.style.display = 'block';
}
})
</script>
</body>
-
这时如果把 keyup 改成 keydown 或者 keypress 可以吗?
-
我们尝试修改一下,结果发现 keydown 和 keypress 每次都和文本框里的数字差一位
-
这是因为 keydown 和 keypress 在事件触发时,文字还没有落入文本框中
-
也就是按下键盘后立即执行了一次事件,事件运行结束后才把文字落入文本框中
-
keyup 正好相反,是先让文字落入,后执行事件,所以这里选用 keyup