DOM事件总结

627 阅读4分钟

DOM事件模型

dom事件模型分为脚本模型,内联模型和动态绑定三种。

    <html>
        <body>
            <!--行内绑定:脚本模型-->
            <div onclick="javascrpt:alert('eventType1')" id="eventType1">脚本模型</div>
            <!--内联模型: 多次声明,只能绑定一次,后声明,后生效-->
            <div onclick="sayType" id="eventType2">内联模型</div>
            <!--动态绑定:可以绑定多次,都可生效-->
            <div id="eventType3">动态绑定</div>
        </body>
        
        <script>
            function sayType(){
                alert('eventType2-1')
            }
            
            // 会覆盖之前的事件声明
            document.getElementById('eventType2').onclick = function(){
                 alert('eventType2-2')
            }
            
            var eventType3Dom = document.getElementById('eventType3')
            eventType3Dom.onclick = function(){
                alert('eventType3-1')
            }
            
            // 生效
            eventType3Dom.addEventListener('click',function(){
                alert('eventType3-2')
            })
            
            // 生效
            eventType3Dom.addEventListener('click',function(){
                alert('eventType3-2')
            })
            
        </script>
    </html>

DOM事件流

DOM结构是一个树型结构,当一个HTML元素产生一个事件时,该事件会在元素结点与根结点之间的路径传播,路径所经过的结点都会收到该事件,这个传播过程可称为DOM事件流。DOM同时支持两种事件模型:捕获型事件和冒泡型事件。

"DOM2事件流"规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段,事件冒泡阶段。

DOM事件流图示

事件捕获阶段

document对象首先接收到事件,然后事件沿着DOM树依次向下,一直传播到事件的实际目标。

事件捕获的用意在于在事件到达预定目标之前就捕获它。给addEventListener第三个参数传入true即可监听捕获事件。

    <html>
        <body>
           <div id="div">div</div>
        </body>
        
        <script>
            document.getElementById('div').addEventListener('click',function(){
                alert('div')
            },true)  
            document.addEventListener('click',function(){
                alert('html')
            },true)
            document.body.addEventListener('click',function(){
                alert('body')
            },true)
        </script>
    </html>

依次打印html body div

事件捕获阶段

事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点。

    <html>
        <body>
           <div id="div">div</div>
        </body>
        
        <script>
            document.getElementById('div').addEventListener('click',function(){
                alert('div')
            },true)  
            document.addEventListener('click',function(){
                alert('html')
            },true)
            document.body.addEventListener('click',function(){
                alert('body')
            },true)
        </script>
    </html>

依次打印div body html

阻止冒泡

    function stopBubble(e) { 
        //如果提供了事件对象,则这是一个非IE浏览器 
        if ( e && e.stopPropagation ) {
            //因此它支持W3C的stopPropagation()方法 
            e.stopPropagation(); 
        } else { 
            //否则,我们需要使用IE的方式来取消事件冒泡 
            window.event.cancelBubble = true;
        } 
}

阻止默认行为

    function stopDefault( e ) { 
        //阻止默认浏览器动作(W3C) 
        if ( e && e.preventDefault ) {
            e.preventDefault(); 
        //IE中阻止函数器默认动作的方式 
        } else { 
            window.event.returnValue = false; 
        }
        return false; 
    }

DOM事件的一些优化方法

事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

<ul id="parent">
  <li class="child">one</li>
  <li class="child">two</li>
  <li class="child">three</li>
</ul>

<script>
  //父元素
  var dom= document.getElementById('parent');

  //父元素绑定事件,代理子元素的点击事件
  dom.onclick= function(event) {
    var event= event || window.event;
    var curTarget= event.target || event.srcElement;

    if (curTarget.tagName.toLowerCase() == 'li') {
      //事件处理
    }
  }
</script>

局限性:

  • focus、blur 之类的事件本身没有事件冒泡机制,所以无法委托
  • mousemove、mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,不适合事件委托

节流函数解决频繁回调的事件

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

function throttle(fn,delay){
    var flag = true;
    var _this = this;
    
    return function(){
        if (flag === false) {
            return; 
        }
        
        flag = false;
        var args = arguments;
        
        setTimeout(function(){
            fn.apply(_this, args);
            flag = true;
        },delay || 500);
    }
}

// onsize事件
window.onresize = throttle(function (e) 
    console.log(123);
}, 500)

防抖函数解决连续触发事件

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

<button id="button">连续点击</button>

<script>
    function debounce (fn, delay){
        var timer = null;
        var _this = this;
        
        return function(){
            var args = arguments;
            clearTimeout(timer);
            
            timer = setTimeout(function(){
                fn.apply(_this, args);
            },delay || 500);
        }
        
    }
    
    // 500ms只执行一次
    document.getElementById('button').addEventListener('click',debounce(function(){
        console.log(123)
    }))
    
</script>