JavaScript课程——Day12(事件基础、事件绑定与取消、DOM事件流、事件委托行为、事件委托、键盘事件、滚轮事件)
1、事件基础
1.1、事件函数
- 当事件被触发时调用的函数就是事件函数
function fn() { console.log('我执行了'); } div.onclick = fn; // fn是一个事件函数 fn(); // fn不是事件函数
1.2、事件对象
- 事件对象:当一个事件发生的时候,跟这个事件有关的一些信息保存在一个全局的event对象中,这个对象就是事件对象。
- IE和谷歌:全局的event对象
- 标准浏览器:事件函数的第一个函数
- 兼容:IE9及以上,用事件函数的第一个参数,IE8及以下,用全局的event
var box = document.getElementById('box'); box.onclick = function (ev) { // console.log(event); // IE和谷歌 // console.log(ev); // 标准浏览器 var ev = ev || event; // 兼容 console.log(ev); // 事件对象 // 事件对象详情 console.log(ev.type); // 事件类型 // console.log(ev.target); // 事件源,IE8及以下不支持 // console.log(ev.srcElement); // 事件源,IE8及以下支持 var target = ev.target || ev.srcElement; // 事件源兼容 target.style.background = 'yellow'; console.log(ev.clientX, ev.clientY); // 鼠标相对可视区的距离 console.log(ev.pageX, ev.pageY); // 鼠标相对文档的距离(IE8及以下不支持) console.log(ev.altKey); // 事件发生的时候,alt键是否按下 console.log(ev.shiftKey); // 事件发生的时候,shift键是否按下 console.log(ev.ctrlKey); // 事件发生的时候,ctrl键是否按下 }
2、事件绑定与取消
2.1、事件绑定
- 格式:元素.addEventListener(不要on的事件名,函数,是否捕获); IE9及以上支持
- 格式:元素.attachEvent(要on的事件名,函数); IE8及以下支持
<script> // 需求:给同一个元素的同一个事件添加不同的处理函数 var box = document.getElementById('box'); function fn1() { console.log(this === window); console.log(1); } function fn2() { console.log(2); } // 赋值的写法,后面的覆盖前面的 box.onclick = fn1; box.onclick = fn2; // --------------------------------------------- // IE9及以上支持 // Listener 监听 // 格式:元素.addEventListener(不要on的事件名, 函数, 是否捕获); // 第三个参数默认为false,false冒泡,true捕获 box.addEventListener('click', fn1, false); box.addEventListener('click', fn2, false); // ---------------------------------------- // IE8及以下 // 格式:元素.attachEvent(要on的事件名, 函数); box.attachEvent('onclick', fn1); box.attachEvent('onclick', fn2); // --------------------------------------------- // 区别: // 1、ie 没有捕获,标准有捕获 // 2、ie 的事件名称前面有 on,标准没有 // 3、标准的会根据事件的顺序正序执行,IE逆序执行 // 4、ie 的 this 是 window,标准的是触发这个事件的对象 // --------------------------- // 兼容原理(浏览器能力判断) // console.log(box.addEventListener); // IE9及以上返回一个函数,IE8及以下返回undefined if (box.addEventListener) { // IE9及以上 box.addEventListener('click', fn1, false); box.addEventListener('click', fn2, false); } else { // IE8及以下 box.attachEvent('onclick', fn1); box.attachEvent('onclick', fn2); } // -------------------------------- // 事件绑定封装 function bind(ele, event, callback) { if (ele.addEventListener) { // IE9及以上 ele.addEventListener(event, callback, false); } else { // IE8及以下 ele.attachEvent('on' + event, callback); } } bind(box, 'click', fn1); bind(box, 'click', fn2); </script>
2.2、取消事件绑定
标准浏览器
- 绑定格式:元素.addEventListener(不要on的事件名,函数,是否捕获);
- 解绑格式:元素.removeEventListenter(不要on的事件名,函数,是否捕获);
IE8及以下
- 绑定格式:元素.attachEvent(要on的事件名,函数);
- 解绑格式:元素.detachEvent(要on的事件名,函数);
<script> // 需求:给同一个元素的同一个事件添加不同的处理函数 var box = document.getElementById('box'); function fn1() { console.log(1); } function fn2() { console.log(2); } // 赋值的写法,后面的覆盖前面的 // box.onclick = fn1; // box.onclick = null; // 取消事件绑定 // --------------------------------------------- // IE9及以上支持 // 格式:元素.addEventListener(不要on的事件名, 函数, 是否捕获); // 解绑:元素.removeEventListener(不要on的事件名, 函数, 是否捕获); 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) { // IE9及以上 ele.addEventListener(event, callback, false); } else { // IE8及以下 ele.attachEvent('on' + event, callback); } } // 解绑 function unbind(ele, event, callback) { if (ele.removeEventListener) { // IE9及以上 ele.removeEventListener(event, callback, false); } else { // IE8及以下 ele.detachEvent('on' + event, callback); } } bind(box, 'click', fn1); bind(box, 'click', fn2); unbind(box, 'click', fn2); // 解绑 </script>
3、DOM事件流
3.1、事件流
- 事件流:当事件发生的时候,事件会按一定的顺序在根节点和各元素之间传播,所经过的节点,都会出发对应的事件。
- 事件流分为三个阶段:
- 1、捕获阶段:从最外层的document > html > body > box1 > box2 > box3的过程,从外到里
- 2、处于目标阶段:到达了box3的上面
- 3、冒泡阶段:从最里面的box3 > box2 > box1 > body > html > document的过程,从里到外
<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'); function fn() { console.log(this.id); } box1.onclick = fn; // 这种形式的绑定,是在冒泡阶段触发 box2.onclick = fn; box3.onclick = fn;
3.2、捕获与冒泡
- 冒泡,事件在子元素发生,子元素先处理,然后子元素传递到父元素
- 捕获,事件在子元素发生,先经过父元素,父元素先处理,再分发到子元素
var box1 = document.getElementById('box1'); var box2 = document.getElementById('box2'); var box3 = document.getElementById('box3'); function fn() { console.log(this.id); } box1.addEventListener('click', fn, false); // false 事件会在冒泡阶段触发 box2.addEventListener('click', fn, false); // false 事件会在冒泡阶段触发 box3.addEventListener('click', fn, false); // false 事件会在冒泡阶段触发 box1.addEventListener('click', fn, true); // true 事件会在捕获阶段触发 box2.addEventListener('click', fn, true); // true 事件会在捕获阶段触发 box3.addEventListener('click', fn, true); // true 事件会在捕获阶段触发 // -------------------------------- // 元素.事件 和 attachEvent只有冒泡(冒泡是默认存在的) // addEventListener可以选择捕获或冒泡
3.3、阻止冒泡
- 标准浏览器:event.stopPropagation();
- IE浏览器:event.cancelBubble = true;
<style> span { display: block; width: 100px; height: 30px; background-color: green; text-align: center; line-height: 30px; color: white; cursor: pointer; } #box { width: 200px; height: 500px; background-color: yellow; display: none; } </style> </head> <body> <span>标题</span> <div id="box"></div> <script> // 阻止冒泡 // Propagation 传播 cancel取消 Bubble泡泡 // 标准浏览器:event.stopPropagation(); // IE 浏览器:event.cancelBubble = true; var span = document.querySelector('span'); var box = document.getElementById('box'); // 1、点击span,box展示 span.onclick = function (ev) { var ev = ev || event; box.style.display = 'block'; // ev.stopPropagation(); // 标准浏览器 // ev.cancelBubble = true; // IE 浏览器 stopPropagation(ev); // 阻止冒泡的兼容 } // 2、点击页面的任何地方,box隐藏 document.onclick = function () { box.style.display = 'none'; } // 阻止冒泡的兼容 function stopPropagation(ev) { if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } } </script> </body>
4、事件委托行为
某些事件,即赋予了元素特殊的操作
- 元素.事件添加的事件:return false;
- 元素.addEventListener():ev.preventDefault();
- 元素.attachEvent():ev.returnValue = false;
<body style="height: 3000px;"> <a href="https://www.baidu.com">百度</a> <script> var a = document.querySelector('a'); a.onclick = function (ev) { var ev = ev || event; preventDefault(ev); } // 阻止默认行为的兼容 function preventDefault(ev) { if (ev.preventDefault) { ev.preventDefault(); } else { ev.returnValue = false; } } </script> </body>
5、事件委托
事件委托:也叫事件代理,利用事件冒泡原理,只指定一个事件处理程序,就可以管理某一类型的所有事件。
实现:事件加给父级,找到事件源,对事件源进行判断,然后做相应的操作
好处:
- 节省性能
- 新添加的元素也有之前的事件
<ul> <li>吃饭</li> <li>睡觉</li> <li>打豆豆</li> </ul>
var ul = document.querySelector('ul'); var li = ul.getElementsByTagName('li'); // 通常做法(新添加的元素,没有之前的事件) // for (var i = 0; i < li.length; i++) { // li[i].onclick = function () { // this.style.background = 'red'; // } // } // 事件代理 ul.onclick = function (ev) { var ev = ev || event; var target = ev.target || ev.srcElement; // 事件源 if (target.nodeName === 'LI') { target.style.background = 'yellow'; } } // 添加一个li var item = document.createElement('li'); item.innerHTML = '我是新来的'; ul.appendChild(item);
6、键盘事件
6.1、键盘事件
- onkeydown 键盘按下
- onkeyup 键盘抬起
在能响应用户输入的元素上触发(表单元素、document都可以响应键盘事件)
var input = document.querySelector('input'); // 按下 input.onkeydown = function () { console.log(this.value); } // 抬起 input.onkeyup = function () { console.log(this.value); }
6.2、键盘事件和事件对象
var input = document.querySelector('input'); // 抬起 input.onkeyup = function (ev) { var ev = ev || event; console.log(ev); console.log(ev.keyCode); // 键码 按键的键码 // a 65 z 90 回车 13 esc 27 空格 32 console.log(ev.key); // 键值 IE8及以下不支持 console.log(ev.altKey); console.log(ev.shiftKey); console.log(ev.ctrlKey); }
7、滚轮事件
标准和IE:
- 事件:onmousewheel
- 方向:ev.wheelDelta 上:120/150 下:-120/-150
火狐:
- 事件:DOMMouseScroll 必须用addEventListener绑定
- 方向:ev.detail 上:-3 下:3
var box = document.getElementById('box'); var h = box.clientHeight; function fn(ev) { var ev = ev || event; if (wheelDelta(ev) > 0) { h--; } else { h++; }; box.style.height = h + 'px'; } bind(box, 'mousewheel', fn); bind(box, 'DOMMouseScroll', fn); // 滚轮方向的兼容:上:120 下:-120 function wheelDelta(ev) { if (ev.wheelDelta) { return ev.wheelDelta; } else { return -40 * ev.detail; } } // 事件绑定封装 function bind(ele, event, callback) { if (ele.addEventListener) { // IE9及以上 ele.addEventListener(event, callback, false); } else { // IE8及以下 ele.attachEvent('on' + event, callback); } }