>>>>> 事件

事件冒泡

事件冒泡 : 当一个元素接收到事件的时候,会把他接收到的所有传播给他的父级,一直到顶层window.事件冒泡机制

<style>
div {padding: 40px;}
#div1 {background:red}
#div2 {background:green}
#div3 {background:blue}
</style>

<div id="div1">
    <div id="div2">
        <div id="div3"></div>
    </div>
</div>

<script>
var oDiv1 = document.getElementById('div1');
var oDiv2 = document.getElementById('div2');
var oDiv3 = document.getElementById('div3');
function fn1() {
    alert( this.id );
}
oDiv1.onclick = fn1;
oDiv2.onclick = fn1;
oDiv3.onclick = fn1;
</script>

点击div3, div3 div2 div1上的点击事件都会被触发

事件冒泡与样式无关,与html结构相关

事件对象

在触发DOM上的某个事件的时候会产生一个事件对象event,这个对象包含着所有与事件有关的信息,包括产生事件的元素、事件类型等相关信息。所有浏览器都支持event对象,但支持方式不同。

事件对象必须在一个事件调用的函数里面使用才有内容
事件函数:事件调用的函数,一个函数是不是事件函数,不在定义的决定,而是取决于这个调用的时候

兼容

ie/chrome : event是一个内置全局对象
标准下 : 事件对象是通过事件函数的第一个参数传入

如果一个函数是被事件调用的那么,这个函数定义的第一个参数就是事件对象

ie/chrome下

function fn(){
    alert(event);
}

document.onclick = fn;

标准下:

function fn(ev){
    alert(ev);
}
document.onclick = fn;

兼容性写法:

function fn(ev){
    var ev = ev || window.enent;
    alert(ev);
}
document.onclick = fn;

clientX clientY

clientX 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的水平坐标。
clientY 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的垂直坐标。

例如,当你点击客户端区域的左上角时,鼠标事件的 clientX 值为 0 ,这一值与页面是否有水平滚动无关。

document.onclick = function(e){
    var e = e||window.event;
    alert(e.clientX +' '+e.clientY);
}

示例: 方块跟着鼠标移动

取消冒泡

标准下:

event.stopPropagation()

ie下:

e.cancelBubble = true;

兼容性写法:

function stopPropagation(e) {
    if (e.stopPropagation)
        e.stopPropagation();
    else
        e.cancelBubble = true;
}

阻止默认行为

标准下:
event.preventDefault();

ie下:
event.returnValue = false;

兼容性写法:

function preventDefault(e) {
    if (e.preventDefault)
        e.preventDefault();
    else
        e.returnValue = false;
}

target

Element 只读 触发事件的目标元素

function getTarget(e) {
    return e.target || e.srcElement;//ie下为srcElement;
}

dom对象 事件处理函数

方法一

绑定事件处理函数

function fn(){
    alert(1);
}
div.onclick = fn;

缺点: 只能绑定一个处理函数

function fn1(){
    alert(1);
}

function fn2(){
    alert(1);
}

div.onclick = fn1;
div.onclick = fn2;

div.onclick 属性中的值会被覆盖

移除事件绑定函数

div.onclick = null;

方法二

DOM2级事件定义了两个方法用于处理指定和删除事件处理程序的操作:

  1. 绑定事件处理函数: addEventListener
  2. 移除事件绑定函数: removeEventListener

所有的DOM节点都包含这两个方法,并且它们都接受三个参数:

  1. 事件类型
  2. 事件处理方法
  3. 布尔参数,如果是true表示在捕获阶段调用事件处理程序,如果是false,则是在事件冒泡阶段处理

绑定事件处理函数

function fn1(){
    alert(1);
}

function fn2(){
    alert(2);
}

oDiv.addEventListener('click', fn1, false);
oDiv.addEventListener('click', fn2, false);

移除事件绑定函数

function fn1() {
    alert(1);
}
function fn2() {
    alert(2);
}

document.addEventListener('click', fn1, false);
document.addEventListener('click', fn2, false);

document.removeEventListener('click', fn1, false);

IE兼容性

IE并不支持addEventListener和removeEventListener方法,而是实现了两个类似的方法

  1. attachEvent
  2. detachEvent

这两个方法都接收两个相同的参数

  1. 事件处理程序名称
  2. 事件处理程序方法

由于IE只支持事件冒泡,所以添加的程序会被添加到冒泡阶段,使用attachEvent添加事件处理程序可以如下

<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');
    var handler=function() {
        alert(this.id);
    }
    btnClick.attachEvent('onclick', handler);
</script>

结果是undefined,很奇怪,一会儿我们会介绍到

使用attachEvent添加的事件处理程序可以通过detachEvent移除,条件也是相同的参数,匿名函数不能被移除。

<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');

    var handler=function() {
        alert(this.id);
    }

    btnClick.attachEvent('onclick', handler);
    btnClick.detachEvent('onclick', handler);
</script>

跨浏览器的事件处理程序

前面内容我们可以看到,在不同的浏览器下,添加和移除事件处理程序方式不相同,要想写出跨浏览器的事件处理程序,首先我们要了解不同的浏览器下处理事件处理程序的区别

在添加事件处理程序事addEventListener和attachEvent主要有几个区别

  1. 参数个数不相同

这个最直观,addEventListener有三个参数,attachEvent只有两个,attachEvent添加的事件处理程序只能发生在冒泡阶段,addEventListener第三个参数可以决定添加的事件处理程序是在捕获阶段还是冒泡阶段处理(我们一般为了浏览器兼容性都设置为冒泡阶段)

  1. 事件名称不相同

addEventListener下事件名称没有on,比如说click,mouseover, attachEvent下有on,比如说onclick
onmousevoer

  1. 事件函数触发时, this的指向不相同

addEventListener的作用域是元素本身,this是指的触发元素,而attachEvent事件处理程序会在全局变量内运行,this是window,所以刚才例子才会返回undefined,而不是元素id

  1. 为一个事件添加多个事件处理程序时,执行顺序不同,

addEventListener添加会按照添加顺序执行,而attachEvent添加多个事件处理程序时顺序无规律(添加的方法少的时候大多是按添加顺序的反顺序执行的,但是添加的多了就无规律了),所以添加多个的时候,不依赖执行顺序的还好,若是依赖于函数执行顺序,最好自己处理,不要指望浏览器

了解了这四点区别后我们可以尝试写一个浏览器兼容性比较好的添加事件处理程序方法

function addEvent(node, type, handler) {
    if (!node) return false;
    if (node.addEventListener) {
        node.addEventListener(type, handler, false);
        return true;
    }
    else if (node.attachEvent) {
        node.attachEvent('on' + type, handler, );
        return true;
    }
    return false;
}

这样,首先我们解决了第一个问题参数个数不同,现在三个参数,采用事件冒泡阶段触发

第二个问题也得以解决,如果是IE,我们给type添加上on

第四个问题目前还没有解决方案,需要用户自己注意,一般情况下,大家也不会添加很多事件处理程序

试试这个方法感觉很不错,但是我们没有解决第三个问题,由于处理程序作用域不同,如果handler内有this之类操作,那么就会出错。在IE下,实际上大多数函数都会有this操作

function addEvent(node, type, handler) {
    if (!node) return false;
    if (node.addEventListener) {
        node.addEventListener(type, handler, false);
        return true;
    }
    else if (node.attachEvent) {
        node.attachEvent('on' + type, function() { handler.call(node); });
        return true;
    }
    return false;
}

这样处理就可以解决this的问题了,但是新的问题又来了,我们这样等于添加了一个匿名的事件处理程序,无法用detachEvent取消事件处理程序,有很多解决方案,我们可以借鉴大师的处理方式,jQuery创始人John Resig是这样做的

function addEvent(node, type, handler) {
    if (!node) return false;
    if (node.addEventListener) {
        node.addEventListener(type, handler, false);
        return true;
    }
    else if (node.attachEvent) {
        node['e' + type + handler] = handler;
        node[type + handler] = function() {
            node['e' + type + handler](window.event);
        };
        node.attachEvent('on' + type, node[type + handler]);
        return true;
    }
    return false;
}

在取消事件处理程序的时候

function removeEvent(node, type, handler) {
    if (!node) return false;
    if (node.removeEventListener) {
        node.removeEventListener(type, handler, false);
        return true;
    }
    else if (node.detachEvent) {
        node.detachEvent('on' + type, node[type + handler]);
        node[type + handler] = null;
    }
    return false;
}

事件的捕获

事件流包括三个阶段,事件捕获阶段,处于目标阶段,事件冒泡阶段,首先发生的是事件捕获,为截取事件提供机会,然后是实际目标接收事件,最后是冒泡阶段

<style>

div {padding: 50px;}
#div1 {background: red;}
#div2 {background: blue;}
#div3 {background: green;}

</style>

<div id="div1">
    <div id="div2">
        <div id="div3"></div>
    </div>
</div>

<script>
var oDiv1 = document.getElementById('div1');
var oDiv2 = document.getElementById('div2');
var oDiv3 = document.getElementById('div3');

function fn1() {
    alert( this.id );
}

oDiv1.addEventListener('click', fn1, true);
oDiv2.addEventListener('click', fn1, true);
oDiv3.addEventListener('click', fn1, true);
</script>

键盘事件

onkeydown : 当键盘按键按下的时候触发
onkeyup : 当键盘按键抬起的时候触发

document.onkeydown = function(ev) {

    var ev = ev || event;
    alert(ev.keyCode);
    
}

与键盘事件相关的事件对象的属性值

  • event.keyCode : 数字类型 键盘按键的值 键值
  • event.ctrlKey, event.shiftKey, event.altKey

这三个键 我们通常称为功能键

当一个事件发生的时候,如果ctrl || shift || alt 是按下的状态,相应的属性值返回true,否则返回false

document.onclick = function(ev) {
    var ev = ev || event;
    
    alert(ev.ctrlKey);
}

当我们点击时,如果是按住ctrl键则弹出true;

示例: 留言板

当输入完成, 并且按住ctrl+enter(回车),添加留言

<input type="text" id="text1" />
<ul id="ul1"></ul>
var oText = document.getElementById('text1');
var oUl = document.getElementById('ul1');

oText.onkeyup = function(ev) {
    
    var ev = ev || event;
    
    //alert(this.value);
    if ( this.value != '' ) {
        
        //不能写成ev.keyCode == 13 && ev.keyCode == 17;
        //应为ev.keyCode不可能同时为13 和17
        if (ev.keyCode == 13 && ev.ctrlKey) {
        
            var oLi = document.createElement('li');
            oLi.innerHTML = this.value;
            
            if ( oUl.children[0] ) {
                oUl.insertBefore( oLi, oUl.children[0] );
            } else {
                oUl.appendChild( oLi );
            }
            
        }
        
    }
    
}

不是所有元素都能够接收键盘事件,能够响应用户输入的元素,能够接收焦点的元素就能够接收键盘事件

onkeydown : 如果按下不抬起,那么会连续触发

示例: 移动div

oncontextmenu

右键菜单事件,当右键菜单(环境菜单)显示出来的时候触发

document.oncontextmenu = function(){
    alert(1);
    return false;//阻止默认行为
}

例子 弹出自定义右键菜单

var oDiv = document.getElementById('div1');
    
document.oncontextmenu = function(ev) {
    var ev = ev || event;
    
    oDiv.style.display = 'block';
    
    oDiv.style.left = ev.clientX + 'px';
    oDiv.style.top = ev.clientY + 'px';
    
    return false;
    
}

document.onclick = function() {
    oDiv.style.display = 'none';
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • JavaScript 程序采用了异步事件驱动编程模型。在这种程序设计风格下,当文档、浏览器、元素或与之相关的对象发...
    劼哥stone阅读 1,232评论 3 11
  • 以下文章为转载,对理解JavaScript中的事件处理机制很有帮助,浅显易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy阅读 2,990评论 1 10
  • 声明:本文来源于http://www.webzsky.com/?p=731我只是在这里作为自己的学习笔记整理一下(...
    angryyan阅读 6,776评论 1 6
  • 如何批量操作 css 如何获取 DOM 计算后的样式 使用getComputedStyle获取元素计算后的样式 实...
    _Dot912阅读 536评论 1 3
  • 事件 JavaScript和HTML的交互是通过事件实现的。JavaScript采用异步事件驱动编程模型,当文档、...
    徐国军_plus阅读 545评论 0 2