事件基础,事件绑定,DOM事件流与事件的默认行为,键盘事件,滚轮事件,事件委托(js)
事件是可以被 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'; } }