您现在的位置是:首页 > 技术学习 > javascript 网站首页 技术学习 javascript

JS事件绑定、冒泡/捕获、常见的兼容处理、委托、阻止默认行为和冒泡

简介 我们常见的事件一般是类似obj.onclick = function () {};这种,如果给同一个obj写同一个事件,那后面写的事件会把之前的事件给覆盖掉。而事件绑定多次绑定对应的事件函数都会执行。

1. 事件绑定

我们常见的事件一般是类似 obj.onclick = function () {}; 这种,这种情况下,如果给同一个obj写同一个事件,那后面写的事件会把之前的事件给覆盖掉,只执行后加的事件函数。

而事件绑定不会出现这种情况,用事件绑定方式给同一个对象加同一个事件,多次绑定的事件对应的事件函数都会执行。

IE8及以下:
    事件绑定:attachEvent("on"事件名称,事件函数);
    解除绑定:detachEvent("on"事件名称,事件函数);
    ps: 上面的'"on"事件名称'是指:类似onclick、onmouseup这种 on+事件名称 的写法。
        默认是冒泡阶段执行。

Dom主流浏览器:
    事件绑定:addEventListener(事件名称,事件函数,捕获/冒泡);    
    解除绑定:removeEventListener(事件名称,事件函数,捕获/冒泡);
    ps: 这里的'事件名称'是指:类似click、mouseup这种不加'on',直接写事件名的写法。
        '捕获/冒泡'是指:绑定的事件是在捕获还是冒泡阶段执行,该值为布尔值,
        捕获为true,冒泡为false,默认是false冒泡。

兼容方法:
    if (x.addEventListener) { // 所有主流浏览器,除了 IE 8 及更早 IE版本
        x.addEventListener("click", myFunction);
    } else if (x.attachEvent) { // IE 8 及更早 IE 版本
        x.attachEvent("onclick", myFunction);
    }

注:为了移除方便,可将事件函数赋值给一个变量,然后在绑定时候,将该变量写进里面,这样移除时可以直接指定这个变量。

<script type="text/javascript">
    // 不用事件绑定时
    var oDiv = document.getElementById("mydiv");
    oDiv.onclick = function () {
        alert(1);
    };
    oDiv.onclick = function () {
        alert(2);
    };
    // 点击只会弹2;
</script>
<script type="text/javascript">
    // 事件绑定时
    var oDiv = document.getElementById("mydiv");
    var myFnA = function () {
        alert("a");
    };
    var myFnB = function () {
        alert("b");
    };
    oDiv.addEventListener("click", myFnA);
    oDiv.addEventListener("click", myFnB, true);
    // a和b都能弹出来,先弹a;

    // oDiv.removeEventListener("click", myFnB, true); 
    // 不能弹出b,解除事件成功

    // oDiv.removeEventListener("click",myFnB,false); 
    // 仍能弹出b,可见解除绑定时需要针对一样的true或false进行解除。
</script>

2. 冒泡和捕获

冒泡:
事件由里到外传递。当一个dom节点发生事件,这个事件向父级方向传递,直到达到最顶层document(有的是window)。
也就是说,该元素的父级也能监听的到该事件的,如果父级恰好对该事件有执行函数,那么是会触发这个函数的。执行顺序是从里到外。

 

捕获:
事件由外到里传递。当一个dom节点发生事件,这个事件从顶层document(有的是window)向该dom节点方向传递,执行顺序是从外到里。

注:

W3C中,浏览器对事件的处理是先进行捕获,再进行冒泡。事件先从顶层一级一级到事件源,然后再一级一级到顶层。
但是,针对事件源(该事件最里层的那个节点),捕获和冒泡执行顺序是谁写在前面先执行谁(事件源不分捕获冒泡先后顺序,当做普通绑定事件对待,按绑定先后执行)。

<div id="mydiv">
    <div id="mydiv2"></div>
</div>
<script type="text/javascript">
    var oUl = document.getElementById('myul');
    var oLi = document.getElementById('li1');
    oUl.addEventListener('click',function(){
        alert(0);
        return false;
    },true);
    oLi.addEventListener('click',function(){
        alert(1);
    });
    // 给父子div绑定点击事件,分别在他们捕获和冒泡阶段触发。

    var oDiv = document.getElementById("mydiv");
    var oDiv2 = document.getElementById("mydiv2");
    oDiv.addEventListener("click",function () {alert("a");},false);
    oDiv.addEventListener("click",function () {alert("b");},true);
    oDiv2.addEventListener("click",function () {alert("c");},false);
    oDiv2.addEventListener("click",function () {alert("d");},true);
    /* 
        点击oDiv2,弹出顺序是b、c、d、a;
        由a、b的顺序可知:是先捕获,再冒泡,
        c、d的顺序可知:
            事件源是oDiv2(该事件最里层是oDiv2)
            所以针对oDiv2的捕获和冒泡,不是先执行捕获,而是谁写在前面先执行谁。 
    */
</script>

3. 常见的兼容处理

3-1 ev和event

e = ev || window.event;
或者 e = ev || event;
或者 e = arguments[0] || window.event;

由于不同浏览器解释js代码方式不同,当我们把触发的事件对象赋予变量e的时候,一些浏览器像火狐,是直接把事件对象作为第一个参数传进去就行,我们可以把参数写作ev,也可以用arguments[0];一些浏览器像IE、chrome这种,事件对象的定义是window.event,因为所有对象都是window的属性,因此window.event == event;

<script type="text/javascript">
    function test1 (ev) {
        var e = ev || window.event;
    };
    function test2 (ev) {
        var e = ev || event;
    };
    function test3 () {
        var e = arguments[0] || window.event; 
        // js函数,即使不传参数,但是还是有一个arguments数组用来入参,
    };
</script>

3-2 获取页面的一些值时的兼容

document.documentElement.属性 // chrome不支持

document.body.属性 // chrome能识别

<script type="text/javascript">
    var top = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop; 
    // 或者
    var scrollT = document.documentElement.scrollTop || document.body.scrollTop;
</script>

类似这样获取方式的属性还有:

  1. clientWidth(网页可视区宽)、
  2. clientHeight(网页可视区高)、
  3. scrollWidth(网页正文全文宽)、
  4. scrollHeight(网页正文全文高)、
  5. scrollTop(可视区到网页顶端的距离,及网页被卷去的高)、
  6. scrollLeft(可视区到网页左端的距离,及网页被卷去的宽)

3-3 获取最终样式的兼容

我们用js的style获取的样式只能获取标签元素的行间样式,获取不到非行间样式。如果元素样式没用行间样式定义,那要怎么获取元素的样式呢?

IE下:
    obj.currentStyle.属性; // ie9以下
主流浏览器:
    getComputedStyle(obj,null).属性;
    // 火狐、chrome、ie9以上。第二个参数可为任意值,一般写null就行。
<script type="text/javascript">
    function getStyle(obj,attr){
        if (obj.currentStyle) {
            return obj.currentStyle[attr];
        } else {
            return getComputedStyle(obj,null)[attr];
        };
    };
</script>

4. 事件委托

事件委托:利用冒泡的原理,把事件加在父级上来触发执行效果。
事件源:不管在哪个事件下,操作的对象就是事件源。
好处:

1.提高性能
2.后添加进来的元素也能加上事件

原理:给父元素添加事件监听器。当有事件触发监听器时,检查事件的来源,然后根据不同的事件源来执行相应的代码。
 
兼容写法:

IE下: e.srcElement
标准下: e.target

这样获取到的是触发事件的那个对象
事件中this指向:绑定事件的父元素。

实际使用中,可以根据触发事件的对象的一些特征来进行判定,从而执行相应的代码。
例:

可以判断nodeName;获得的是该对象元素标签的大写形式(为string格式,使用时需要注意),可以用toLowerCase(); 转化为小写后再判断。

也可以判断其他的特征,如className、其他属性等
示例代码:

<div id="mydiv">
    <ul>
        <li></li>
        <li></li>
    </ul>
    <span class="myspan"></span>
</div>
<script type="text/javascript">
    var oDiv = document.getElementById('mydiv');
    oDiv.addEventListener('click',function (e) {
        var e = e || event;
        var target = e.srcElement || e.target;
        if (target.nodeName == 'LI') {
            alert('点击了li');
            console.log(target); // 该li对象
            console.log(e.currentTarget); 
            // 绑定事件的div对象,注意这里用的是e,(注意currentTarget和target的区别)

            console.log(this); // 绑定事件的div对象
        } else if (target.className == 'myspan') {
            alert('点击了span');
            console.log(target); // 该span对象
            console.log(this);// 绑定事件的div对象
        };
    });
</script>

5. 阻止默认行为和冒泡

我们有时候会遇见这样的问题:

  1. 一个submit按钮,点击会提交表单数据,但是我们不希望什么数据都能够提交,想进行一些前端校验,合法数据才能提交,不合法数据不让提交。那怎样才能阻止不合法数据提交呢?这时候就需要阻止提交行为的发生。
  2. 有时候当我们给父子元素都加了点击事件,但我们不希望点击子元素时父元素的点击事件也触发。这时候我们需要阻止冒泡行为。

有的标签具有默认行为,比如a标签的跳转,submit的提交,这些行为在点击时会发生,我们可以在点击事件中进行阻止。

阻止默认行为:

  1. 普通写法:return false;(高版本浏览器都支持)
    注:这句话要写在自己代码的后面。
  2. 兼容写法:
    IE: e.returnValue = false;
    w3c: e.preventDefault();
<a id="mya" href="http://www.baidu.com">百度</a>
<script type="text/javascript">
    var oA = document.getElementById('mya');
    oA.addEventListener('click',function (e) {
    var e = e || event;
    if (e.preventDefault) { 
        // 阻止默认浏览器动作(W3C)
        e.preventDefault();
    } else { 
        // 阻止函数器默认动作(IE)
        e.returnValue = false; 
    };    
    });
</script>

阻止冒泡:
IE方法:e.cancelBubble = true;
w3c方法:e.stopPropagation();

<div>
    <div id="mydiv">子div</div>
<div>
<script type="text/javascript">
    var oDiv = document.getElementById('mydiv');
    oDiv.addEventListener('click',function(e) {  
        e = e || event;  
        if (e.stopPropagation) { 
            // W3C阻止冒泡方法  
            e.stopPropagation();  
        } else {  
            // IE阻止冒泡方法  
            e.cancelBubble = true; 
        };  
    };  
</script>

平常使用过程中,如果用到阻止默认行为和冒泡较多,可以将他们封装成函数,

<script type="text/javascript">
    function preventDefa(e){ 
        var e = e || window.event;
        if (e.preventDefault) { 
            // 阻止默认浏览器动作(W3C)
            e.preventDefault();
        } else { 
            // 阻止函数器默认动作(IE)
            e.returnValue = false; 
        };    
    }; 

    //阻止冒泡事件的兼容性处理 
    function stopBubble(e) {
        var e = e || window.event; 
        if (e.stopPropagation) { 
            // W3C阻止冒泡方法  
            e.stopPropagation(); 
        } else { 
            // IE阻止冒泡方法  
            e.cancelBubble = true; 
        }; 
    }; 
</script>

个人CSDN博客

上一篇:Git 常用命令

下一篇:JS中的正则

Top