Appearance
事件
事件流
事件冒泡与事件捕获
1、事件冒泡(IE事件流)
由事件点击的具体元素逐级向上接受,直到Document (DOM树的上下级,非显示上下级)
div -> body -> html -> document
1、事件捕获(网景)
从document开始 逐级触发 到 触发元素
document -> html -> body -> div
3、DOM事件流
先捕获 -> 处于目标阶段 -> 冒泡阶段
浏览器支持 : IE9+ chrome Opera safari firefox
事件处理方式
分为DOM0、DOM2、IE
1、DOM0
el.onclick = fn;
2、DOM2
el.addEventListener('click',handler, 是否捕获)
el.removeEventListener('click',handler, 是否捕获)
浏览器支持 : IE9+ chrome Opera safari firefox
3、IE事件处理方法
el.attachEvent('onclick',hander); // 只支持冒泡
el.detachEvent('onclick',hander); // 只支持冒泡
浏览器支持 : IE
重点:
- 1、 浏览器支持 所有的浏览器都支持 el.onclick = fn 的DOM0的方式; chrome、firefox、 IE9+ 都支持addEventListener , 但是IE8-不支持; IE10-都支持attachEvent ,但是chrome、firefox、IE11+不支持attachEvent。
- 2、 el.onclick = fn 的方式 导致元素支持绑定一个触发函数,而attachEvent,addEventListener支持绑定多个触发函数。
- 3、el.attachEvent() 不支持 2种事件流 支持默认IE的冒泡流,所以addEventListener() 第三个参数需要设置成false
- 4、触发函数中this的指向 onclick 和addEventListener 都指向 元素本身; attachEvent指向Window(需要修改)
var div3 = document.getElementById("div3");
var fn3 = function(e){
console.log(3);
}
var fn2 = function(e){
console.log(2);
}
div3.onclick = fn3;
div3.onclick = fn2; //结果只触发 2;测试 所有的浏览器都支持
div3.addEventListener('click',fn3,false);
div3.addEventListener('click',fn2,false); // 发现 先触发3 在触发2
// chrome、firefox、 IE9+ 都支持 , 但是IE8-就报 对象不支持“addEventListener”属性或方法
div3.attachEvent('onclick',fn3);
div3.attachEvent('onclick',fn2); // 发现 先触发3 在触发2
// IE10-都支持 , 但是就报 chrome、firefox、 IE11+对象不支持“addEventListener”属性或方法
跨浏览器事件处理方式
(function() {
var utils = utils || {};
if (document.body.addEventListener) {
utils.addEvent = function(el, type, handler) {
el.addEventListener(type, function(e) {
handler.call(this, e);
}, false)
}
} else if (document.body.attachEvent) {
utils.addEvent = function(el, type, handler) {
el.attachEvent('on' + type, function(e) {
e.target = e.target || e.srcElement;
handler.call(el, e);
})
}
} else {
utils.addEvent = function(el, type, handler) {
el['on' + type] = handler;
}
}
// 取消事件
if (document.body.removeEventListener) {
utils.removeEvent = function(el, type, handler) {
el.removeEventListener(type, handler, false)
}
} else if (document.body.detachEvent) {
utils.removeEvent = function(el, type, handler) {
el.detachEvent('on' + type, handler)
}
} else {
utils.removeEvent = function(el, type, handler) {
el['on' + type] = null;
}
}
// 阻止事件的默认行为
utils.stopDefault = function(e) {
if (e && e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
//
return false;
}
// 阻止事件的冒泡或者捕获 等进一步行为
utils.stopPropagation = function(e) {
if (e && e.stopPropagation) {
e.stopPropagation();
} else {
e.cancelBubble = true;
}
}
window.utils = utils;
})()
事件对象event
ie/chrome : event是一个全局的对象
console.log(event); // chrome : undefined / ie : null
- type 事件的类型(不区分大小写)。
- event.target | IE 为 event.srcElement Element 事件的目标。
- event.currentTarget 事件处理程序中当前正在处理事件的那个元素。 ***
- event.bubbles Boolean 表明事件是否冒泡;
- event.stopPropagation() 取消事件的进一步捕获或者冒泡; event.bubbles === true; IE event.cancelBubble = true;
- event.cancelable Boolean 表明是否可以取消事件的默认行为
- event.preventDefault() 取消事件的默认行为; event.cancelable === true; IE event.returnValue = false;
<font class="j-font-red j-font-blod" >注意:</font>
1. 事件默认行为与事件流 是两个概念,一个元素可以同时触发这两件事,比如 a标签可以定义点击事件和href链接跳转,那么我们点击的时候就会先触发事件,再触发默认行为;
2. 事件流虽然分为事件冒泡和事件捕获两种 但是IE只支持事件的冒泡 所以我们常用的都是事件冒泡来处理方法;
var div2 = document.getElementById('div2');
div2.addEventListener('click',function(event){
console.log(event);
console.log(event.type); //click
console.log(event.target); // button#div3 // 点击触发的元素
console.log(event.currentTarget); // div#div2 绑定事件的元素
},false)
utils.addEvent(div2,'click',function(e){
console.log(e);
console.log(e.target.tagName); // BUTTON
})
utils.addEvent(document.getElementById("a1"),'click',function(e){
console.log('触发了默认的行为');
console.log(e); // BUTTON
})
var btn1 = document.getElementById("btn1");
utils.addEvent(btn1,'click',function(e){
console.log(e);
// 鼠标事件 属性
// e.clientX/e.clientY
// 返回事件发生时的应用客户端区域的水平坐标/垂直坐标 (与页面坐标不同)
})
事件的类型
- 1、UI事件 load、unload、about、error、select、resize、scroll
- 2、焦点事件
- 3、鼠标事件
- 4、滚轮事件
- 5、文本事件
- 6、键盘事件
- 7、合成事件
1、UI事件
1.1、load事件
资源加载后触发load事件,其中一般包含 window script style img
javascript
utils.addEvent(window, 'load', function(e) {
console.log('load事件-----')
console.log(e);
})
//如我们常见的加载图片
var loadImg = document.createElement('img');
loadImg.src = "../Node类型/childNodes1.png";
utils.addEvent(loadImg, 'load', function(e) {
console.log('图片已经加载完成...');
console.log(e)
})
document.getElementById('load').appendChild(loadImg);
// 会输出 图片已经加载完成..
// 会输出 load事件----
1.2 unload事件
资源卸载后触发unload事件,其中一般包含 window script style img
<font class="j-font-red j-font-blod" >注意:</font>
1. 请小心编写onunload事件处理函数,因为资源已经卸载,所以页面存在中的元素不一定存在,特别是window的onunload处理函数。
1.3 resize事件
当浏览器窗口调整到一个新的高度或者宽度时触发。
<font class="j-font-red j-font-blod" >注意:</font>
1. 何时触发resize,不同浏览器不同,IE、chrome..会在浏览器窗口变化1px的时候就触发,firefox会在用户停止调整窗口的时候触发,所以不要在处理程序中加入大量代码,浏览器最小化最大化也会触发,,,建议使用 事件防抖debounce 或者 requestAnimationFrame。
javascript
utils.addEvent(window, 'resize', function(e) {
// 当然也不要 在function里面添加requestAnimationFrame获取防抖处理函数,因为
var raf = requestAnimationFrame(function(e1) {
console.log('浏览器窗口修改了');
console.log(e);
//获取浏览器可视区的大小
console.log(document.documentElement.clientWidth + 'px');
console.log(document.documentElement.clientHeight + 'px');
})
})
utils.addEvent(window, 'resize', raf);
1.4 scroll 事件
文档或者元素进行滚动的时候触发。
<font class="j-font-red j-font-blod" >注意:</font>
1.在文档滚动期间重复触发。
javascript
utils.addEvent(window, 'scroll', function(e) {
console.log('文档滚动了');
console.log(e);
// 返回事件相对于整个文档的水平坐标。
console.log(e.pageX);
//返回事件相对于整个文档的垂直坐标。 只有firefox有效 所以没用
console.log(e.pageY);
// 文档滚动的高度
console.log(document.documentElement.scrollTop);
})
utils.addEvent(document.getElementById('scroll'), 'scroll', function(e) {
console.log('scroll元素滚动了');
console.log(this.id);
// 元素滚动的高度
console.log(this.scrollTop);
})
1.5 select 事件
用户选择了文本 或者 input/textarea调用了select() 方法。
<font class="j-font-red j-font-blod" >注意:</font>
1.IE9+/其他 只有用户选择了文本并且释放鼠标才触发; IE8- 只要用户选择了一个字母不必释放鼠标就触发。
javascript
utils.addEvent(document.getElementById("select1"), 'select', function(e) {
console.log("select1选择了文本");
console.log(e);
})
utils.addEvent(document.getElementById("select2"), 'click', function(e) {
document.getElementById("select2value").select();
})
// chrome 触发了 2次
// IE、firefox 触发了1次
utils.addEvent(document.getElementById("select2value"), 'select', function(e) {
console.log("select2value选择了文本");
console.log(e);
console.log(e.target.selectionStart + ' ---' + e.target.selectionEnd);
if (typeof e.target.selectionStart === 'number') {
console.log(e.target.value.substring(e.target.selectionStart, e.target.selectionEnd)) //这是默认的文本 IE9+/其他
} else {
console.log(document.selection.createRange().text); // IE8-
}
})
2、焦点事件
- blur()
- focus()
- focusin()
- focusout()
2.1 focus 事件
元素获得焦点时触发
<font class="j-font-red j-font-blod" >注意:</font>
1. 对于 window、document.documentElement、document.body 无效。
2. 事件不会冒泡。
javascript
utils.addEvent(window, 'focus', function(e) {
console.log('HTML元素获得焦点了');
console.log(e);
})
utils.addEvent(document.getElementById("focus1"), 'focus', function(e) {
console.log('focus1元素获得焦点了');
console.log(e);
})
2.2 blur 事件
元素获得失去焦点时触发
<font class="j-font-red j-font-blod" >注意:</font>
1. 对于 window可以判断文档是否失去焦点,document.documentElement、document.body 无效。
2. 事件不会冒泡。
javascript
utils.addEvent(window, 'blur', function(e) {
console.log('HTML元素获得焦点了');
console.log(e);
})
utils.addEvent(document.getElementById("focus1"), 'blur', function(e) {
console.log('focus1元素获得焦点了');
console.log(e);
})
2.3 focusin 事件
元素获得失去焦点时触发
<font class="j-font-red j-font-blod" >注意:</font>
1. 对于 window可以判断文档是否失去焦点,document.documentElement、document.body 无效。
2. 事件会冒泡。
2.4 focusout 事件
元素获得失去焦点时触发
<font class="j-font-red j-font-blod" >注意:</font>
1. 对于 window可以判断文档是否失去焦点,document.documentElement、document.body 无效。
2. 事件会冒泡。
javascript
utils.addEvent(window, 'blur', function(e) {
console.log('HTML元素获得焦点了');
console.log(e);
})
utils.addEvent(document.getElementById("focus"), 'focusin', function(e) {
console.log('focus元素获得focusin焦点');
})
utils.addEvent(document.getElementById("focus1"), 'focusin', function(e) {
console.log('focus1元素获得focusin焦点');
})
utils.addEvent(document.getElementById("focus"), 'focus', function(e) {
console.log('focus元素获得focus焦点');
})
utils.addEvent(document.getElementById("focus1"), 'focus', function(e) {
console.log('focus1元素获得focus焦点');
})
// chrome firefox focus1元素获得focus焦点 -> focus1元素获得focusin焦点 -> focus元素获得focusin焦点
// IE focus1元素获得focusin焦点 -> focus元素获得focusin焦点 -> focus1元素获得focus焦点
// 可以发现 非IE浏览器 先focus焦点事件触发 再focusin触发 -> 再冒泡 focusin触发
// IE 浏览器 是先 focusin 触发 -> 再冒泡 focusin触发 -> 最后 focus焦点事件触发
// 两个元素间的 焦点获得与失去 顺序
var foucs1 = document.getElementById("focus1"),
focus2 = document.getElementById("focus2");
utils.addEvent(foucs1, 'focusin', function(e) {
console.log('foucs1元素获得focusin焦点');
})
utils.addEvent(foucs1, 'focusout', function(e) {
console.log('foucs1元素获得focusout焦点');
})
utils.addEvent(foucs1, 'focus', function(e) {
console.log('foucs1元素获得focus焦点');
})
utils.addEvent(foucs1, 'blur', function(e) {
console.log('foucs1元素获得blur焦点');
})
utils.addEvent(focus2, 'focusin', function(e) {
console.log('focus2元素获得focusin焦点');
})
utils.addEvent(focus2, 'focusout', function(e) {
console.log('focus2元素获得focusout焦点');
})
utils.addEvent(focus2, 'focus', function(e) {
console.log('focus2元素获得focus焦点');
})
utils.addEvent(focus2, 'blur', function(e) {
console.log('focus2元素获得blur焦点');
})
// 一个元素从获得到失去焦点
// chrome,firefox focus -> focusin -> blur -> focusout
// IE focusin -> focus -> focusout -> blur
// 一个元素到里一个元素
// chrome,firefox blur -> focusout -> focus -> focusin
// IE focusout -> focusin -> blur -> focus
//
<font class="j-font-red j-font-blod" >重点:</font>
1. focus()、blur()不会触发事件冒泡, focusin()、focusout() 会触发事件冒泡。
2. 元素从获得到失去 在IE和其他都不一样 如上面。
3. 元素从一个元素到另一个元素 也不一样。
4. 建议要么用focus、blur 要么用focusin focusout 不要合用。
鼠标和滚轮事件
- click() 用户鼠标点击或者回车键触发 冒泡
- dbclick() 双击事件 冒泡
- mousedown() 用户按下鼠标按钮是触发 冒泡
- mouseup() 用户释放鼠标按钮时触发 冒泡
- mouseenter() 鼠标光标从元素外部首次移入元素时触发,移动到后代元素也不会触发。 不冒泡
- mouseleave() 鼠标光标从元素上首次移到元素范围之外时触发,移动到后代元素也不会触发。 不冒泡
- mousemove() 当鼠标在元素内部移动是重复触发,即元素移动到后代也会触发 冒泡
- mouseout() 当鼠标从元素上方移动到其他元素时触发,到子元素也触发 冒泡
- mouseover() 当鼠标位于一个元素的外部,然后首次移入元素之内触发 冒泡
鼠标触发顺序 mousedown() -> mouseup() -> click() -> mousedown() -> mouseuo() -> click() -> dbclick()
鼠标事件继承于MouseEvents接口
- e.clientX e.clientY 保存鼠标指针在视口区的水平和垂直坐标
- e.pageX e.pageY 保存鼠标指针在页面水平和垂直坐标 IE8-不支持
- e.screenX e.screenY 保存鼠标指针在整个浏览器屏幕水平和垂直坐标
<font class="j-font-red j-font-blod" >重点:</font>
1. 我们常用的自定义下拉菜单等移入展示,移出隐藏 一般使用 mouseenter mouseleave (最常用)。
2.记住鼠标事件的 视口距离 页面文档距离 屏幕坐标。
var button1 = document.getElementById("mouse1");
utils.addEvent(button1, 'click', function(e) {
console.log(e);
// 当前鼠标 在 视口 的水平距离
console.log("clientX = " + e.clientX);
// 当前鼠标 在 视口 的垂直距离
console.log("clientY = " + e.clientY);
// 鼠标指针 距离页面文档 的水平距离 IE8-不支持
console.log("pageX = " + (e.clientX + document.documentElement.scrollLeft))
console.log("pageX = " + e.pageX);
// 鼠标指针 距离页面文档 的垂直距离 IE8-不支持
console.log("pageY = " + (e.clientY + document.documentElement.scrollTop))
console.log("pageY = " + e.pageY);
// 鼠标指针 距离浏览器 的水平距离
console.log("screenX = " + e.screenX);
// 鼠标指针 距离浏览器 的垂直距离
console.log("screenY = " + e.screenY);
// 当前鼠标 在 视口 的水平距离
console.log("x = " + e.x);
// 当前鼠标 在 视口 的垂直距离
console.log("y = " + e.y);
// 修改键
console.log(e.shiftKey); // 是否按下 shift键 按下为true IE8-不支持
console.log(e.ctrlKey); // 是否按下 ctrl键
console.log(e.altKey); // 是否按下 alt键
console.log(e.metaKey); // 是否按下 meta键
// 鼠标按钮
console.log(e.button) //鼠标按钮 数字
<!--})-->
键盘和文本事件
- keydown() 用户按下键盘任意键时触发,按住不放会重复触发
- keypress() 用户按下键盘上的字符键时触发,持续按住重复触发
- keyup() 用户释放键盘按键触发
- textInput() 在可编辑去输入字符时才会触发 与keypress 不同 可获得焦点的就可以触发keypress 但是只有可编辑区域才能触发textInput 且实际输入字符 ie9+ Safari Chrome支持 其他不支持 基本没用
<font class="j-font-red j-font-blod" >重点:</font>
1. 不是鼠标放在上面就可以又条件触发事件,是元素必须获得焦点。
2. 记住鼠标事件的 视口距离 页面文档距离 屏幕坐标。
javascript
var key1 = document.getElementById("key1"), //button
key2 = document.getElementById("key2"); //input
utils.addEvent(key1,'keypress',function(e){
//
console.log('button is keypress ');
console.log(e);
console.log(e.keyCode); //13
})
HTML5事件
- contextmenu() 用户上下文菜单(右键显示菜单) 鼠标事件、在元素上
- beforeunload() 用户卸载页面前触发事件,可以阻止页面的关闭
- DOMContentLoaded() 文档形成完整的DOM数之后就触发,不理会图像,script,link等资源 IE9+
- readystatechange() 文档或者元素加载状态的信息
javascript
var readystatechange1 = document.getElementById("readystatechange1");
utils.addEvent(window,'DOMContentLoaded',function(e){
console.log('DOMContentLoaded');
})
utils.addEvent(window,'load',function(e){
console.log('load');
})
utils.addEvent(document,'readystatechange',function(e){
console.log('readystatechange');
console.log(document.readyState);
})
var insertImg = document.createElement('img');
insertImg.src= 'https://note.youdao.com/yws/public/resource/d4e10a6cea85cbf9da7dfd3ee06141f3/xmlnote/6D1107C647ED46609EB70AF97FAAA500/3473';
utils.addEvent(insertImg,'readystatechange',function(e){
console.log('image is readystatechange');
console.log(insertImg.readyState);
})
utils.addEvent(insertImg,'load',function(e){
console.log('image is load');
})
utils.addEvent(insertImg,'DOMContentLoaded',function(e){
console.log('image is DOMContentLoaded');
})
document.getElementById("html5").appendChild(insertImg);
readystatechange
其元素上有一个readyState属性
- uninitialized(未初始化) :对象存在但尚未初始化
- loading(正在加载中)
- loaded(加载完毕)
- interactive(交互)
- compltet(完成)
chrome : readystatechange(interactive) -> DOMContentLoaded -> readystatechange(complete) -> load
IE 支持 加载元素 的readystatechange事件 readystatechange(interactive) -> DOMContentLoaded -> image readystatechange(loading) -> image load -> readystatechange(complete) -> load -> image readystatechange(complete)
先出现 readystatechange-> DOMContentLoaded -> load
触摸手势事件
- touchstart()
- touchmove()
- touchend()
- touchcancel()
touchstart()
当手指触摸屏幕时触发,即使已有手指放在屏幕也会触发。
event对象
bubbles cancelable view clientX clientY screenX screenY detail altKey shiftKey ctrlKey metaKey
- touches : 表示当前跟踪的触摸操作的Touch对象的数组
- targetTouchs : 特定于事件目标的Touch对象的数组
- changeTouches : 表示 自上次触摸以来发生了改变的Touch对象的数组
每一个touch对象都是由下列信息组成
- clientX/clientY // 触摸点相对于浏览器窗口的位置
- pageX/pageY // 触摸点相对于页面的位置
- screenX/screenY //触摸点相对于屏幕的位置
- identifier // touch对象的ID
- target // 触发的DOM对象
- radiusX,radiusY,rotationAngle // 移动端点击是手指形状的椭圆形,此为椭圆的两个半径和角度
<font class="j-font-red j-font-blod" >注意:</font>
1.手指在滑动整个屏幕时,会影响浏览器的行为,如滚动或缩放,所以记得取消元素的默认行为。
- 1.1 禁止缩放
<meta name="viewport" content="target-densitydpi=320,width=640,user-scalable=no">
- 1.2 禁止滚动
preventDefault是阻止默认行为,touch事件的默认行为就是滚动。 event.preventDefault();
javascript
var x = {};
utils.addEvent(document.getElementById("touch1"),'touchstart',function(e){
console.log('touchstart');
console.log(e);
x = e.targetTouches[0];
console.dir(e.targetTouches[0]);
// { clientX:125.44000244140625,clientY:614.1329956054688,pageX:125.44000244140625,pageY:614.1329956054688,screenX:2501,screenY:393,identifier:0,
// radiusX:30.053333282470703,radiusY:30.053333282470703,force:1,rotationAngle:0}
})
utils.addEvent(document.getElementById("touch1"),'touchmove',function(e){
console.log('touchmove');
console.log(e);
})
utils.addEvent(document.getElementById("touch1"),'touchend',function(e){
console.log('touchend');
console.log(e);
})
事件委托
什么是事件委托
js高级程序设计说: 利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件 jQuery中使用 $().on('click...','子元素',function(){})
<font class="j-font-red j-font-blod" >注意:</font>
1. 支持冒泡的事件才可以代理,触发元素绑定同样的事件的时候不能阻止事件冒泡。
2. 通过 e.target || e.srcElement 获取 触发元素不一定是 指定的子元素 可能是其子元素。
javascript
utils.addEvent(document.getElementById("onul"),'click',function(e){
// console.log(e.target || e.srcElement) //<p>这是列1</p>
// 我们就可以通过e.target来判断 那个p触发了 点击事件 然后分别处理,
// 但是我们存在一个问题 我们需要的li而不是其子元素p
var target = e.target || e.srcElement;
while(target.tagName.toLowerCase() !== 'li' && target.id !== 'onul'){
target = target.parentNode;
}
console.log(target);
// 当然这只是简易的
})