JS笔记(17):事件

栏目: JavaScript · 发布时间: 5年前

内容简介:[单手指操作:touch]可自主扩展其他事件给当前元素的某个事件绑定方法(不管是基于DOM0还是DOM2),都是为了触发元素的相关行为的时候,能把绑定的方法执行,不仅把方法执行,而且浏览器还给方法传递了一个实参信息 ===>事件对象
  • 事件:一件事情或者一种行为(对于元素来说,他的很多事件都是天生自带的),只要我们去操作这个元素,就会触发这些行为
    • 如果没有事件函数赋值,事件默认值为null
    • 系统默认事件:null
    • 自定义事件:undefined

元素天生自带的事件(事件本身不带on):

1) 【鼠标事件】

click
dblclick
mouseover
mouseout
mouseenter
mouseleave
mousemove
mousedown
mouseup
mousewheel

2) 【键盘事件】

  • keydown :键盘按下(获取不到最新的value值,只能获取到上一个)
  • keyup :键盘抬起(可以获取到新的value值)
  • keypress :键盘长按(一般不用),和 keydown 类似,但 keydown 返回的是键盘码, keypress 返回的是ASCII码
  • input :移动端的内容改变事件。由于PC端有物理键盘,可以监听到键盘的按下和抬起,但是移动端时虚拟键盘,所以 keydownkeyup 在大部分手机上都没有,所以使用 input 事件统一代替他们

3) 【表单元素常用的事件】

focus
blur
change

4)【其他常用事件】

load
unload
beforeunload
scroll
resize
error
hashchange

5) 【移动端手指事件】

[单手指操作:touch]

touchstart
touchmove
touchend
touchcancel
gesturestart
gesturechange
gestureend

6) 【H5中audio/video音视频事件】

canplay
canplaythrough

可自主扩展其他事件

二、事件绑定

  • 事件绑定: 给元素天生自带的事件行为绑定方法,当事件触发,会把对应的方法执行

【DOM0级事件绑定】

element.onxxx = function(){}

【DOM2级事件绑定】

  • element.addEventListener('xxx', function(){}, false);
  • 该方法接受三个参数。
    • 第一个参数:事件名称,大小写敏感。
    • 第二个参数:监听函数。事件发生时,会调用该监听函数。
    • 第三个参数:布尔值,表示监听函数是否在捕获阶段(capture)触发,默认为false(监听函数只在冒泡阶段被触发)。该参数可选。
  • 移除事件的监听函数: ele.removeEventListener('click', listener, false);
    • 注意: listener 参数如果为 fn.bind(this) ,则不能移除,因为 bind 会改变函数地址

事件绑定的目的:

给当前元素的某个事件绑定方法(不管是基于DOM0还是DOM2),都是为了触发元素的相关行为的时候,能把绑定的方法执行,不仅把方法执行,而且浏览器还给方法传递了一个实参信息 ===>事件对象

三、事件对象:

事件对象的种类

MouseEvent
KeyboardEvent
Event
TouchEvent

事件对象的目的:

  • 事件对象中记录了很多属性名和属性值,这些信息中心包含了当前操作的基础信息,例如:
    • 鼠标点击位置的X/Y轴坐标
    • 鼠标点击的是谁(事件源)

事件对象中常用的属性:

1) 【 MouseEvent :鼠标事件对象中的属性】

  • ev.target : 事件源(操作的是哪个元素),在嵌套关系中,给上层元素绑定事件,可以通过事件源,查到事件触发的对象(元素)
  • ev.target.tagName : 'LI' 查看标签名(大写)
  • ev.clientX / ev.clientY : 当前鼠标触发点距离当前窗口可视区左上角的X/Y轴坐标
  • ev.pageX / ev.pageY : 当前鼠标触发点距离body(第一屏幕)左上角X/Y轴坐标
    pageXOffset + event.clientX
    pageYOffset + event.clientY
    
  • ev.preventDefault() : 阻止默认行为
  • ev.stopPropagation() : 阻止事件的冒泡传播(低版本浏览器不兼容)
  • ev.cnacelbubble = true : 阻止事件的冒泡传播(chrome/IE: window中都有一个event对象,火狐中没有event对象)
  • ev.type : 当前事件类型

2) 【 KeyboardEvent : 键盘事件对象中的属性】

ev.keycode
v.ctrlKey
ev.shiftKey
ev.altKey
// MouseEvent 鼠标事件对象
box.onclick = function(ev){
    //定义一个形参ev用来接收方法执行的时候,浏览器传递的信息值(事件对象)
    console.log(ev); 
    //MouseEvent {isTrusted: true, screenX: 52, screenY: 161, clientX: 52, clientY: 58, …}
}
复制代码
// KeyboardEvent 键盘事件对象
box.onkeydown = function(ev){
    console.log(ev); 
}
input.onkeydown = function(ev){
    console.log(ev); //KeyboardEvent {isTrusted: true, key: "Enter", code: "Enter", location: 0, ctrlKey: false, …}
}
复制代码
// Event 普通事件对象
window.onload = function(ev){
    console.log(ev); //Event {isTrusted: true, type: "load", target: document, currentTarget: Window, eventPhase: 2, …}
}
复制代码

键盘事件小例子

结构和样式

<input type="text" id="txt">
<ul id="ul"></ul>
复制代码
let ul = document.getElementById('ul')
txt.addEventListener('keyup',function(ev){
    console.log(ev);
    // 同时按住ctrl键和enter键 就把input输入框中输入的内容插入到已有li元素的前面
    if(ev.keyCode === 13 && ev.ctrlKey){
        ul.insertAdjacentHTML('afterbegin',`<li>${this.value}</li>`)
    }
})
复制代码

关于mouseenter和mouseover的区别:

  • mouseover : 鼠标经过
  • mouseout :鼠标移出
  • mouseenter :鼠标进入(阻止冒泡传播)
  • mouseleave :鼠标离开(阻止冒泡传播) 真实项目中多用 mouseenter / mouseleave

结构和样式

<style>
    #outer {
        position: absolute;
        top: 30px;
        left: 30px;
        width: 500px;
        height: 500px;
        background: red;
        cursor: pointer;
    }
    #inner {
        position: absolute;
        top: 30px;
        left: 30px;
        width: 150px;
        height: 150px;
        background: #fff;
        cursor: pointer;
    }
</style>
复制代码
<div id="outer">
    <div id="inner"></div>
</div>
复制代码
// 用onmouseover / onmouseout 
inner.onmouseover = function () {
    console.log('进入白盒子')
}
inner.onmouseout = function () {
    console.log('离开白盒子')
}
outer.onmouseover = function () {
    console.log('进入红盒子')
}
outer.onmouseout = function () {
    console.log('离开红盒子')
}
复制代码
// 用onmouseenter / onmouseleave
inner.onmouseenter = function () {
    console.log('进入白盒子')
}
inner.onmouseleave = function () {
    console.log('离开白盒子')
}
outer.onmouseenter = function () {
    console.log('进入红盒子')
}
outer.onmouseleave = function () {
    console.log('离开红盒子')
}
复制代码

四、事件的默认行为

事件的默认行为:

事件本身就是天生就有的,某些事件触发,即使没有绑定方法,也会存在一些效果,这些默认的效果就是 事件的默认行为

A. 例如:a标签的点击操作就存在默认行为

  • 1.页面跳转
    • <a href="http://www.baidu.com" target="_blank"><a/>
  • 2.描点定位(hash定位)
    <a href="#box" target="_blank"><a/>
    hash
    

B. 例如:input标签的默认行为:

  • 1.输入内容可以呈现到文本框当中
  • 2.输入内容时,会把之前输入的一些信息呈现出来(并不是所有浏览器和所有情况都有)

C. 例如:submit标签的默认行为:

form 当中设置 anction ,点击 submit ,会默认按照 anction 的地址进行页面跳转,并且把表单中的信息传递进去(非前后端分离项目中,有服务器进行页面渲染,由其他语言实现数据交互)

<form action="https://www.baidu.com/">
<input type="submit" value="提交"/>
</form>
复制代码

五、阻止事件的默认行为

A. 阻止a标签的默认行为

很多时候我们使用a标签仅仅是想做一个普通的按钮,点击实现一个功能,不想页面跳转,也不想锚点定位

    1. 在结构中阻止:

    <a href="javascript:void 0/undefined/null/false;">珠峰最新视频</a>

    1. 在JS中阻止:

给其click事件绑定方法,当我们点击a标签,先触发click事件,其次才会执行自己的默认行为

// 阻止a标签的默认行为
link.onclick = function(ev){
    ev = ev || window.event;
    // return false;
    ev.preventDefault ? ev.preventDefault():ev.returnValue = false;
}
复制代码

B. 阻止input标签的默认行为

// 阻止input标签的默认行为,即不能输入内容 

// DOM0
input1.onkeydown = function(ev){
    // ev = ev || window.event;
    ev.preventDefault() ? ev.preventDefault() : ev.returnValue = false;
    // return false;
}

// DOM2
input1.addEventListener('keydown',function(ev){
    ev.preventDefault();
})
复制代码

六、事件的传播机制

1) 事件的传播机制:(事件流/事件模型机制)

  • 冒泡传播:

触发当前元素的某一个事件(比如点击事件)行为,不仅当前元素事件行为触发,而且其祖先元素的相关事件行为也会被依次触发,这种机制就是事件的传播机制

  • 这种传播分成三个阶段。
    window
    window
    

这三个阶段的传播模型,使得同一个事件会在多个节点上触发

2) DOM0级事件与DOM2级事件绑定方式

  • xxx.onxxx = function(){};

    • DOM0 级事件绑定方式,只有冒泡阶段。如果事件绑定的是目标元素,就按照目标事件的先后顺序依次绑定
  • xxx.addEventListener('xxx',function(){},false);

    第三个参数:

    • 为false:是控制绑定的方法在事件传播的冒泡阶段(或者目标阶段)执行;
    • 为true:代表让当前方法在事件传播的捕获阶段触发执行(这种捕获阶段

3) 事件对象的一些理解:

  • 1.事件对象是用来存储当前本次操作的相关信息的,和操作有关,和元素无必然关联。
  • 2.当我们基于鼠标或者键盘等操作的时候,浏览器会把本次操作的信息存储起来(标准浏览器存储到默认的内存当中(自己找不到),IE低版本存储到 window.event 中),存储的值是一个对象(堆内存);操作会触发元素的某个行为,把绑定的方法执行,此时标准浏览器会把之前存储的对象(内存地址)当做实参传递给每一个执行的方法,所以操作一次,即使再多方法中都有 ev ,但是存储的值都是一个(本次操作的信息对象)

4) 事件的传播机制代码演示

公共样式:

<style>
#outer {
    position: absolute;
    top: 30px;
    left: 30px;
    width: 500px;
    height: 500px;
    background: red;
    cursor: pointer;
}

#center {
    position: absolute;
    top: 30px;
    left: 30px;
    width: 300px;
    height: 300px;
    background: pink;
    cursor: pointer;
}

#inner {
    position: absolute;
    top: 30px;
    left: 30px;
    width: 150px;
    height: 150px;
    background: #fff;
    cursor: pointer;
}
</style>

复制代码
<div id="outer">
    <div id="center">
        <div id="inner">
            <button id="btn">按钮</button>
        </div>
    </div>
</div>
复制代码
let aa = null;
window.onclick = function(ev){
    console.log(ev === aa); //true
    alert('window')
}
outer.onclick = function(ev){
    console.log(ev === aa); //true
    alert('红盒子')
}
center.onclick = function(ev){
    console.log(ev === aa); //true
    alert('粉盒子')
}
inner.onclick = function(ev){

    aa = ev;
    alert('白盒子')
}
btn.onclick = function(ev){
    ev = ev || window.event;
    ev.stopPropagation()?ev.stopPropagation():ev.cancelBubble = true;
    // 阻止冒泡传播 ev.stopPropagation()低版本不兼容  ev.cancelBubble = true 所有浏览器都兼容
    alert('按钮')
}
复制代码

5) 事件的传播机制练习题

//结构和样式同上
function fn() { alert('btn1'); }
btn.addEventListener('click', fn, true);
outer.addEventListener('click',function(){alert('red');});
btn.addEventListener('click', fn, true);
inner.addEventListener('click', function () { alert('yellow'); }, true);
center.addEventListener('click', function () { alert('green'); });
btn.addEventListener('click', function () { alert('btn捕获'); }, true);
btn.addEventListener('click', function () { alert('btn冒泡'); });
btn.addEventListener('click', fn, false);
// 答案:yellow btn1 btn捕获 btn冒泡 btn1 green red
复制代码

七。事件委托(事件代理)

  • 事件委托(事件代理):

利用事件冒泡传播机制,如果一个容器的后代元素当中,很多元素的点击行为(其他事件行为也是)都要做一些处理,此时我们不需要再像以前一样一个个获取一个个绑定,只需要给容器的click绑定方法即可,这样不管点击的是哪一个后代元素,都会根据冒泡传播机制把容器的click行为触发,把绑定的方法执行,根据事件源我们可以知道点击的是谁,从而做不同的事情即可

  • ev.target : 事件源(操作的是哪个元素),在嵌套关系中,给上层元素绑定事件,可以通过事件源,查到事件触发的对象(元素)
    • tagName: 'LI' 查看标签名(大写)

公共样式:

<style>
ul {
    margin: 0 auto;
    width: 100px;
    border: 2px solid fuchsia;
}

li {
    width: 50px;
    height: 50px;
    border: 1px solid #000;
}
</style>
复制代码
<ul id="ul">
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
</ul>
复制代码
// 给所有li的上级元素ul绑定点击事件,则其所有后代元素都会执行这个方法
ul.addEventListener('click',function(ev){
   ev.target.style.background = 'green'
})
复制代码
// 点击一个变色,点击下一个变色,同时上一个取消变色
let lis = document.querySelectorAll('#ul>li')
ul.onclick = function (ev) {
    // console.log(ev.target.tagName); // 'LI'
    // ev.target.tagName 即当前点击的标签的标签名(大写)
    if (ev.target.tagName === 'LI') {
        for (let i = 0; i < lis.length; i++) {
            // 循环所有的li元素,如果当前循环的li元素不等于当前点击的li元素,就把当前循环的li元素的背景颜色清空,如果当前循环的li元素等于当前点击的元素,就把当前点击的li元素加上颜色
            if (lis[i] !== ev.target) {
                lis[i].style.background = '';
            } else {
                ev.target.style.background = 'green';
            }
        }
    }
}
复制代码

八、DOM0和DOM2的运行机制

DOM0: element.onxxx = function(){}

每一个元素对象都是对应类的实例,浏览器天生为其设置了很多私有属性和公有的属性方法,而 onclick 就是其中的一个私有属性(事件类似私有属性,还有很多其他的事件私有属性),这些属性默认值是 null

  • DOM0事件绑定原理:
    • 就是给元素的某一个事件私有属性赋值(浏览器会建立监听机制,当我们触发元素的某个行为,浏览器会自己把属性中赋的值去执行)

DOM2:

  • element.addEventListener('xxx', function(){}, false);
  • 移除事件的监听函数: ele.removeEventListener('click', listener, false);
  • 使用的方法都是 EventTarget.prototype 上定义的
  • DOM2事件池: 当我们触发box的click行为后,浏览器会到事件池中“按照顺序”依次把之前监听的方法执行
    • 1.每一个被执行的方法,浏览器都会把事件对象传递给他
    • 2.方法中的this是当前操作的元素
    • 3.执行的方法不会出现重复,在向事件池增加的时候就去重了
  • 完整事件池机制:
    • DOM2事件绑定可以给当前元素的某一个行为绑定“多个不同的方法”

DOM0和DOM2事件绑定的区别:

  • 1.机制不一样
    • DOM0采用的是给私有属性赋值,所以只能绑定一个方法,DOM2采用的是事件池机制,所以能绑定多个不同的方法
  • 2.关于移除的操作
    • DOM0移除,将事件赋值为null即可,不需要考虑绑定的是谁
    • DOM2移除,必须清楚要移除的是哪一个方法,才能在事件池当中移除掉。所以基于DOM2做事件绑定,要有“瞻前顾后”的思路,也就是绑定的时候要考虑一下如何移除(不要绑定匿名函数)
  • 3.DOM2事件绑定中增加了一些DOM0无法操作的事件行为;例如: DOMContentLoaded 事件(当页面中的页面中HTML结构加载完成就会触发执行)
// DOM0事件绑定:只允许给当前元素的某个事件行为绑定一个方法,多次绑定,后面绑定的内容会替换前面绑定的,以最后一次绑定的方法为主
box.onclick = function(){
    console.log(1)
}  
box.onclick = function(){
    console.log(2);// 触发点击行为,只输出2
}  
复制代码
// DOM2事件绑定
function fn1(){console.log(1)}
function fn2(){console.log(2)}
function fn3(){console.log(3)}
function fn4(ev){
    console.log(4,this === box,ev.target)
    box.removeEventListener('click',fn5);
    box.removeEventListener('click',fn8);
}
function fn5(){console.log(5)}
function fn6(){console.log(6)}
function fn7(){console.log(7)}
function fn8(){console.log(8)}
function fn9(){console.log(9)}
function fn10(){console.log(10)}

box.addEventListener('click',fn1);
box.addEventListener('click',fn3);
box.addEventListener('click',fn5);
box.addEventListener('click',fn7);
box.addEventListener('click',fn9);
box.addEventListener('click',fn2);
box.addEventListener('click',fn2);//重复
box.addEventListener('click',fn2);//重复
box.addEventListener('mouseenter',fn2); //增加到事件池当中的
box.addEventListener('click',fn4);
box.addEventListener('click',fn6);
box.addEventListener('click',fn8);
box.addEventListener('click',fn10);
复制代码

在上个例子中:

首先在事件未执行前,会把box的click的所有方法添加到一个数组里面(即添加到事件池里面),此时数组中有[fn1,fn3,fn5,fn7,fn9,fn2,fn4,fn6,fn8,fn10]

当第一次点击box,会依次输出1,3,5,7,9,2,4(此时移除了数组中的fn5和fn8方法),6,10

当第二次点击box,会依次输出1,3,7,9,2,4,6,10

let fn = function(){ }
box.addEventListener('DOMContentLoaded',fn); //可以
box.onDOMContentLoaded = fn; //不可以,box没有这个属性
复制代码

九、鼠标跟随

结构和样式

<style>
#box {
    width: 100px;
    height: 100px;
    position: absolute;
    top: 0;
    left: 0;
    background: red;
}
body{
    height: 3000px;
}
</style>
复制代码
<div id="box"></div>
复制代码

js代码

document.onmousemove = function (ev) {
    // console.log(ev.clientX);
    // console.log(ev.clientY);
    // 实现鼠标在盒子中间:ev.pageX: 鼠标离窗口左边的距离 - 盒子宽度的一半 此时left:盒子左边到浏览器左边的距离
    let left = ev.pageX - box.clientWidth / 2;
    let top = ev.pageY - box.clientHeight / 2;

    if (left < 0) {
        left = 0;
    } 
    if (left > document.documentElement.clientWidth - box.clientWidth) {
        // document.documentElement.clientWidth - box.clientWidth 即盒子距离窗口左边的最大距离(超过此最大距离,盒子就会溢出窗口)
        // 盒子到浏览器左边的距离 > 最大距离,就让盒子到浏览器左边的距离 = 最大距离
        left = document.documentElement.clientWidth - box.clientWidth;
    }

    if (top < 0) { 
        top = 0;
    } else if (top > document.documentElement.clientHeight - box.clientHeight) {
        top = document.documentElement.clientHeight - box.clientHeight;
    }
    //让盒子距窗口左边/顶部的距离 = 鼠标离窗口左边/顶部的距离
    box.style.left = left + 'px';
    box.style.top = top + 'px';
}
复制代码

十、推箱子

结构和样式

<style>
    #box {
        width: 100px;
        height: 100px;
        position: absolute;
        top: 0;
        left: 0;
        background: red;
    }
</style>
复制代码
<div id="box"></div>
复制代码

js代码

num1 = 0;
num2 = 0
let timer = null;
document.addEventListener('keydown', function move(ev) {
    clearInterval(timer);
    timer = setInterval(() => {
        switch (ev.keyCode) {
            case 39:
                num1 += 10;
                // box.style.left = num1 + 'px';
                break;
            case 40:
                num2 += 10;
                // box.style.top = num2 + 'px';
                break;
            case 37:
                num1 -= 10;
                // box.style.left = num1 + 'px';
                break;
            case 38:
                num2 -= 10;
                // box.style.top = num2 + 'px';
                break;
        }
        num1 < 0 ? num1 = 0 : null;
        box.style.left = num1 + 'px';
        num2 < 0 ? num2 = 0 : null;
        box.style.top = num2 + 'px';
    }, 22)
});
document.addEventListener('keyup',function(){
    clearInterval(timer)
})

复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

GOOGLE HACKS

GOOGLE HACKS

Rael Dornfest、Tara Calishain / 卞军、谢伟华、朱炜 / 电子工业 / 2006-1 / 49.00元

GOOGLE HACKS巧妙使用网络搜索的技巧和工具(第二版)一起来看看 《GOOGLE HACKS》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换