JS事件委托,事件处理函数,冒泡捕获

JS事件委托

JS事件委托,初学这个概念感觉它很神秘,随着多次接触,终于明白,它只不过是运用事件冒泡的底层原理实现的一个功能,事件委托是对这个功能的抽象。
遇到一个场景,文档中ul标签下有若干li 给每一个li 绑定点击事件。有一种做法是,循环遍历所有li 分别给它们绑定上。这种做法的弊端是如果li很多几千几万……会消耗性能,还有就是动态添加的dom元素li的时候,后添加的无法绑定上点击事件。有一种方法可以完美的解决这个问题,而没有上述的两个弊端,那就是事件委托。
就是给ul 绑定点击事件,由于有父子关系嵌套的DOM元素,有事件冒泡的事件流,你点击了li虽然它没有绑定点击事件,事件冒泡,则触发了父级Ul的点击事件,并且事件对象里有事件源可以追寻到,就是你点击的哪个li 元素。这样就完成了给每个li元素绑定点击事件的功能。如果不提事件委托这个概念,只了解了事件冒泡的事件流机制,运用到了这个方法,那就是运用了它的奇技淫巧,很酷炫。而这里把这种方法总结了,叫它事件委托,就好像,子元素li 们的点击事件委托给个父元素ul处理。
事件委托 : 通过给祖先元素注册事件,在程序处理中判断事件源进行不同的处理。

事件

事件: 发生一件事。
事件类型: 发生什么事;点击,拖拽,鼠标按下,鼠标抬起,鼠标移入、移出,键盘按下、抬起……
事件处理程序: 一个函数,当某件事情发生时运行。
事件注册: 将一个事件处理程序挂载到某个事件上

事件是元素天生就具备的能力是它的特性,它的功能;绑定事件,是绑定事件的处理函数,就是给事件定义一个反馈。
事件 + 事件反馈 = 前端交互
交互体验则是前端工作的核心。

//获取事件源
var oDiv =document.getElementByTagName('div')[0];
//句柄的方式绑定事件
oDiv.onclick = function(){this.style.backgroundColor ='blue';}
如何绑定事件处理函数:

绑定事件处理函数的方法:

  1. 句柄的方式,特点是兼容性好,缺点时,同一共元素的句柄只能有一个处理函数,再写一个,下一个会覆盖前一个。
    elem.onclick = function(){}
  2. 内联句柄的方式绑定<button onclick="console.log(1)">按钮</button>
    <button onclick=" test( ) ">按钮</button>结合 function test(){...} 内联事件监听器
    此方法不利于结构,逻辑相分离的开发思想,不建议使用。
    属性句柄绑定的方式优先级低于第一种JavaScript里句柄绑定的方式会被其覆盖
  3. elem.addEventListener(事件类型,事件处理函数,false);这种方式IE9以下不兼容,是现在w3c规范 ,同一个事件源同类型的事件可以通过这种方式绑定多个事件处理函数。
  4. IE8及以下的绑定方法elem.attachEvent(事件类型,事件处理函数) 名字的命名很语义化的,attach,连接,系牢,附上,附加,认为有……记以下单词好了,这个方法运用场景现在变得很少。

来做一个试题:
给一组li 绑定点击事件使点击时输出该li的索引值,下面代码中注释掉的部分是没有考虑到函数执行时,i已经循环到最后一个,取不到对应的索引了,改正的做法是用立即执行函数代替原来的函数写法,这样在绑定的时候,就执行了函数,实际是绑定了当时对应的值,当点击的时候,把这个值输出,而写作原来的函数,点击时才执行函数,函数取i值时i则是循环结束时i的取值。
实际上这个题用文末的方法,也可解决,就是用到Array.prototype.indexOf()方法。

<ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
    </ul>

    <script>
        var oLi = document.getElementsByTagName('li'),
            len = oLi.length,
            item;
        // for (var i = 0; i < len; i++) {
        //     item = oLi[i];
        //     item.addEventListener('click', function() {
        //         console.log(i);
        //     }, false)
        // }

        for (var i = 0; i < len; i++) {
            (function(i) {
                item = oLi[i];
                item.addEventListener('click', function() {
                    console.log(i);
                }, false)
            })(i);
        }     
     
    </script>

绑定事件几种情况的联合封装

 function addEvent(el, type, fn) {
            if (el.addEventListener) {
                el.addEventListener(type, fn, false);
            } else if (el.attachEvent) {
                el.attachEvent('on' + type, function() {
                    fn.call(el);
                });
            } else {
                el['on' + type] = fn;
            }
        }
解除事件绑定的方法
1.   element.onclick = null/false;

2.  element.addEventListener('click',test,false); //跟绑定事件时的参数一一对应;
element.removeEventListener('ckick',test,false);

3. element.detachEvent('onclick',test);

取消默认事件

  1. return false;兼容性好,但只能用在句柄方式;
  2. w3c的标准方法是e.preventDefault()
  3. IE9及以下用的专用方式:e.returnValue = false;

阻止a标签跳转的几种方式:
1 . <a href="javascript:void(0);"></a>
2. <a href="javascript:;"></a>
3. <a href="#"></a> //# 是,锚点,当页面滚动的情况,点击会回到最顶端
4. 还可以利用JS element.onclick =function(e){e.preventDefault();}来阻止它跳转(这里的element是获取到的DOM元素a)

事件冒泡、事件捕获

  • 事件流:当某个事件发生的时候,哪些元素会监听到该事件发生,这些元素发生该事件的顺序。
  • 当一个元素发生了某个事件时,那该元素的所有祖先元素都发生了该事件
  • 事件冒泡:先触发最里层的元素,然后再依次触发外层元素。
  • 事件捕获:先触发最外层的元素,然后再依次触发里层的元素。
  • 目前,标准规定,默认情况下,事件是冒泡方式触发。

事件捕获流(Event Capturing) 事件冒泡流(Event Bubbling)

element.addEventListener的第三个参数为false则是冒泡事件流,为true则是捕获事件流。

focus、 blur、change、submit 、reset 、select 这些事件不产生冒泡和捕获事件流,IE浏览器所有事件没有捕获事件流
取消冒泡事件的方法

  1. w3c的标准 e.stopPropagation();
  2. IE8 window.event event在window上,
  3. 所以兼容的写法是function(e){var e = e||window.event;}
 function cancelbubble(e) {
            var e = e || window.event;
            if (e.stopPropagation) {
                e.stopPropagation();
            } else {
                e.cancelbubble = true;
            }
        }

捕获与冒泡同时存在的情况,先执行捕获,再执行冒泡即:
事件流:事件捕获阶段、处于目标阶段、事件冒泡阶段

下面的代码和打印出的结果,对其做了论证,先打印了捕获事件,在处于目标阶段,在前的绑定事件先打印。

<style>
        .wrapper {
            width: 200px;
            height: 200px;
            background-color: #008c8c;
        }
        
        .outer {
            width: 100px;
            height: 100px;
            background-color: green;
        }
        
        .inner {
            width: 50px;
            height: 50px;
            background-color: #f80;
        }
    </style>
</head>

<body>
    <div class="wrapper">
        <div class="outer">
            <div class="inner"></div>
        </div>
    </div>
    <script>
        var wrapper = document.getElementsByClassName('wrapper')[0],
            outer = document.getElementsByClassName('outer')[0],
            inner = document.getElementsByClassName('inner')[0];
        wrapper.addEventListener('click', function() {
            console.log('bubbleWrapper');
        }, false);
        outer.addEventListener('click', function() {
            console.log('bubbleOuter');
        }, false);
        inner.addEventListener('click', function() {
            console.log('bubbleInner');
        }, false);
        wrapper.addEventListener('click', function() {
            console.log('wrapper');
        }, true);
        outer.addEventListener('click', function() {
            console.log('outer');
        }, true);
        inner.addEventListener('click', function() {
            console.log('inner');
        }, true);
    </script>



在这里插入图片描述
在这里插入图片描述

target srcElement——>事件源对象;火狐只有target,IE只有srcElement,谷歌两个都有。

事件代理如何拿到点击元素的索引值:将所有元素遍历匹配事件源和item的值,相等的那一个,的索引值,即是要找的。

//很容易想到的方法,缺点当数量比较多时性能不太好
for(var i = 0;i<len;i++;){
item = oLi[i];
if(tar ===item){console.log(i);}}
//比较靠谱的解决办法
 //Array.prototype.indexOf
        var index = Array.prototype.indexOf.call(oLi, tar);
        console.log(index);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值