事件基础,事件绑定,DOM事件流与事件的默认行为,键盘事件,滚轮事件,事件委托(js)

1、事件基础

事件是可以被 javaScript 侦测到的行为。 网页中的每个元素都可以产生某些可以触发javaScript函数的事件。比方说,我们可以在用户点击某按钮时产生一个 onclick 事件来触发某个函数。

1、事件函数

事件函数:被事件触发的才是事件函数

var box = document.getElementById('box');
function fn() { }
​
box.onclick = fn; // fn就是事件函数
​
fn(); // 不是事件函数
​
box.onclick = function () { // 事件函数
    fn(); // 不是事件函数
}

2、事件对象

事件对象:当一个事件发生的时候,跟这个事件有关的一些详细信息,被保存在一个对象中,这个对象,就是事件对象

  • IE和谷歌:全局的event对象

  • 标准浏览器:事件函数的第一个参数

  • 兼容:var ev = ev || event;

var box = document.getElementById('box');
​
box.onclick = function (ev) {
    // console.log(event); // IE和谷歌支持
    // console.log(ev); // 标准浏览器支持
    var ev = ev || event; // 事件对象的兼容
​
    console.log(ev.type); // 事件类型
    // console.log(ev.target); // 事件源 标准浏览器支持
    // console.log(ev.srcElement); // 事件源 IE8及以下
    var target = ev.target || ev.srcElement; // 事件源的兼容
    target.style.backgroundColor = 'green';
​
    console.log(ev.clientX, ev.clientY); // 鼠标相对可视区的位置
    console.log(ev.pageX, ev.pageY); // 鼠标相对文档的距离 IE8及以下没有
    console.log(ev.shiftKey); // 这个事件发生的时候,shift键是否按下
    console.log(ev.ctrlKey); // 这个事件发生的时候,ctrl键是否按下
    console.log(ev.altKey); // 这个事件发生的时候,alt键是否按下
}

2、事件的绑定

需求:给同一个元素的同一个事件绑定不同的处理函数

var box = document.getElementById('box');
function fn1() {
    console.log(1);
    // console.log(this === window);
}
function fn2() {
    console.log(2);
}

// 这种写法是一种赋值的写法,后面的会覆盖前面的
box.onclick = fn1;
box.onclick = fn2;

// 标准浏览器支持
// 元素.addEventListener(不要on的事件名, 函数, 是否捕获);
// 是否捕获:如果不写,默认false
box.addEventListener('click', fn1, false);
box.addEventListener('click', fn2, false);

// IE8及以下支持
// 元素.attachEvent(要on的事件名, 函数);
box.attachEvent('onclick', fn1);
box.attachEvent('onclick', fn2);

// addEventListener和attachEvent的区别:
1、标准事件名不要on,而非标准要on
2、标准的可以捕获,而非标准没有捕获
3、标准的是顺序执行,而非标准是倒序执行
4、标准执行的函数中的this是触发这个函数的元素,而非标准执行的函数中的this是window

// 事件绑定的封装的原理
console.log(box.addEventListener); // 在标准浏览器下返回一个函数,在IE8及以下返回undefined
// 封装 参数:元素 事件 函数
function bind(ele, event, callback) {
    if (ele.addEventListener) {
        // 标准浏览器
        ele.addEventListener(event, callback, false);
    } else {
        // IE8及以下
        ele.attachEvent('on' + event, callback);
    }
}
​
bind(box, 'click', fn1);
bind(box, 'click', fn2);

事件的取消:

// 这种写法是一种赋值的写法,后面的会覆盖前面的
box.onclick = fn1;
box.onclick = fn2;
box.onclick = null; // 取消

// 标准浏览器支持
// 元素.addEventListener(不要on的事件名, 函数, 是否捕获);
// 元素.removeEventListener(不要on的事件名, 函数, 是否捕获);
// 是否捕获:如果不写,默认false
box.addEventListener('click', fn1, false);
box.addEventListener('click', fn2, false);
box.removeEventListener('click', fn2, false); // 取消

// IE8及以下支持
// 元素.attachEvent(要on的事件名, 函数);
// 元素.detachEvent(要on的事件名, 函数);
box.attachEvent('onclick', fn1);
box.attachEvent('onclick', fn2);
box.detachEvent('onclick', fn2); // 取消

// 事件绑定封装 参数:元素 事件 函数
function bind(ele, event, callback) {
    if (ele.addEventListener) {
        // 标准浏览器
        ele.addEventListener(event, callback, false);
    } else {
        // IE8及以下
        ele.attachEvent('on' + event, callback);
    }
}
// 事件取消封装 参数:元素 事件 函数
function unbind(ele, event, callback) {
    if (ele.removeEventListener) {
        // 标准浏览器
        ele.removeEventListener(event, callback, false);
    } else {
        // IE8及以下
        ele.detachEvent('on' + event, callback);
    }
}
​
bind(box, 'click', fn1);
bind(box, 'click', fn2);
unbind(box, 'click', fn2); // 取消

3、DOM事件流

1、事件流

分为三个阶段

1、捕获阶段:从最外面不具体的元素,到最具体的元素。document - html - body - box1 - box2 - box3

2、处于目标阶段

3、冒泡阶段:从最里面具体的元素,到最外面不具体的元素。box3 - box2 - box1 - body - html - document

  • 当经过某个元素时,同时这个元素上面也绑定着事件,哪这个事件就会被触发

  • 如果要捕获,就必须addEventListener绑定,而冒泡是默认存在的,即"元素.事件"和"attacheEvent"这种绑定都只有冒泡

 

<div id="box1">
    <div id="box2">
        <div id="box3"></div>
    </div>
</div>

var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = document.getElementById('box3');
​
// 元素.事件 = 函数; 这种方式只能冒泡触发 (冒泡是默认存在的)
box1.onclick = function () {
    console.log(1);
}
box2.onclick = function () {
    console.log(2);
}
box3.onclick = function () {
    console.log(3);
}
​
// --------------------
// 第三个参数为true,则是捕获触发
box1.addEventListener('click', function () {
    console.log(1);
}, true);
box2.addEventListener('click', function () {
    console.log(2);
}, true);
box3.addEventListener('click', function () {
    console.log(3);
}, true);
​
box1.addEventListener('click', function () {
    console.log(1);
}, false);
box2.addEventListener('click', function () {
    console.log(2);
}, false);
box3.addEventListener('click', function () {
    console.log(3);
}, false);

2、阻止事件冒泡

  • 标准浏览器:ev.stopPropagation();

  • IE浏览器:ev.cancelBubble = true;

// 阻止冒泡的兼容
function stopPropagation(ev) {
    if (ev.stopPropagation) {
        ev.stopPropagation();
    } else {
        ev.cancelBubble = true;
    }
}

二级菜单

var btn = document.getElementsByTagName('button')[0];
var box = document.getElementById('box');
​
btn.onclick = function (ev) {
    var ev = ev || event;
    box.style.display = 'block';
​
    // 阻止冒泡
    // ev.stopPropagation(); // 标准浏览器
    // ev.cancelBubble = true; // IE8及以下
    stopPropagation(ev); // 兼容
}
​
// 点页面其它的地方,document
document.onclick = function () {
    box.style.display = 'none';
}
​
// 阻止冒泡的兼容
function stopPropagation(ev) {
    if (ev.stopPropagation) {
        ev.stopPropagation();
    } else {
        ev.cancelBubble = true;
    }
}
 

4、事件的默认行为

事件的默认行为,即赋予了元素特殊的操作,如点击链接会跳转到其他的网页,在浏览器中右击鼠标会弹出菜单,当我们不需要这些默认行为的时候,可以手动阻止

  • 标准:ev.preventDefault();

  • IE8及以下:ev.returnValue = false;

var a = document.getElementsByTagName('a')[0];
a.onclick = function (ev) {
    var ev = ev || event;
​
    // ev.preventDefault();
    // ev.returnValue = false;
    preventDefault(ev);
}
​
function preventDefault(ev) {
    if (ev.preventDefault) {
        ev.preventDefault();
    } else {
        ev.returnValue = false;
    }
}

案例:自定义右键菜单

5、键盘事件

  • onkeydown代表键盘被按下

  • onkeyup代表键盘被抬起

  • oninput 只要内容发生变化就触发

键盘事件只能加给能响应键盘输入的元素,能响应键盘输入的元素有:input textarea document...

var input = document.querySelector('input');
​
// 按着不动,会连续触发
input.onkeydown = function () {
    console.log(this.value);
}
​
// 按着不动,只有最后一次抬起触发
input.onkeyup = function () {
    console.log(this.value);
}
​
// 集合它俩的优点(IE8及以下不支持)
input.oninput = function () {
    console.log(this.value);
}

// 键盘事件的事件对象
input.onkeydown = function (ev) {
    var ev = ev || event;
    // console.log(ev); // 事件对象
// console.log(ev.key); // 具体的按键(IE8及以下不支持)
    console.log(ev.keyCode); // 按键对应的编码   
    // a:65  z:90   空格:32  esc:27  回车:13
    // 左键 37 上键38 右键39 下键40
}

案例:方向键控制div的移动

6、滚轮事件

滚轮事件和滚轮方向

// 标准和IE
// 事件:onmousewheel
// 方向:ev.wheelDelta  上120,下-120
box.onmousewheel = function (ev) {
    var ev = ev || event;
    console.log(ev.wheelDelta);
}
​
// 火狐
// 事件:DOMMouseScroll   而且,必须要用addEventListener绑定
// 方向:ev.detail  上-3 下3
box.addEventListener('DOMMouseScroll', function (ev) {
    console.log(ev.detail);
}, false)
 

滚轮方向兼容

// 向上:120  向下:-120
function wheelDelta(ev) {
    if (ev.wheelDelta) {
        // 标准和IE
        return ev.wheelDelta; // 上120 下-120
    } else {
        // 火狐
        return ev.detail * -40; // 上-3 下3
    }
}
 

完整案例实现

var box = document.getElementById('box');
​
function fn(ev) {
    var ev = ev || event;
    var h = box.clientHeight; // 盒子的高
    var w = box.clientWidth; // 盒子的宽
if (wheelDelta(ev) > 0) {
        // console.log('向上');
        h--;
        w--;
    } else {
        // console.log('向下');
        h++
        w++;
    }
​
    box.style.height = h + 'px';
    box.style.width = w + 'px';
}
​
bind(box, 'mousewheel', fn); // 标准和IE
bind(box, 'DOMMouseScroll', fn); // 火狐
// 向上:120  向下:-120
function wheelDelta(ev) {
    if (ev.wheelDelta) {
        // 标准和IE
        return ev.wheelDelta; // 上120 下-120
    } else {
        // 火狐
        return ev.detail * -40; // 上-3 下3
    }
}
​
// 封装 参数:元素 事件 函数
function bind(ele, event, callback) {
    if (ele.addEventListener) {
        // 标准浏览器
        ele.addEventListener(event, callback, false);
    } else {
        // IE8及以下
        ele.attachEvent('on' + event, callback);
    }
}
 

7、事件委托

什么叫事件委托?它还有一个名字叫事件代理,从JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

事件委托原理:利用冒泡的原理,把事件加给父级,在触发事件时,找到事件源,判断事件源,做相应的操作

<ul>
    <li>吃饭</li>
    <li>睡觉</li>
    <li>打篮球</li>
</ul>

var ul = document.querySelector('ul');
var li = ul.querySelectorAll('li'); // 只有原来的三个,原来的三个会有事件
var item = document.createElement('li'); // 新加的一个
item.innerHTML = '平头哥';
ul.appendChild(item);
​
​
// 给li绑定点击事件,点击时,让li的背景变红
// 问题:
// 1、新加的元素没有之前的事件
// 2、添加多个会消耗内存
// for (var i = 0; i < li.length; i++) {
//     li[i].onclick = function () {
//         this.style.backgroundColor = 'red';
//     }
// }
// -------------------------------
// 事件委托
// 1、新加的元素,也有之前的事件
// 2、提高了性能
ul.onclick = function (ev) {
    var ev = ev || event;
    var target = ev.target || ev.srcElement;
    // console.log(target); // 事件源
    // console.log(target.nodeName); // 点击元素的标签名
    if (target.nodeName === 'LI') {
        target.style.background = 'yellow';
    }
}



原文地址:https://www.cnblogs.com/cyf666cool/p/13702709.html