Js高级程序设计第三版学习(十三章)

                             Js高级程序设计第三版学习(十三章)

 

 第十三章 事件

事件就是文档或者浏览器窗口中发生的特定的交互的瞬间

  1.事件流:  

事件流描述的是页面中接收事件的顺序

  • 事件冒泡  

事件开始由最具体的元素(目标元素) 直至不具体的节点(文档), 事件会依次传播,例如点击一个div,click事件会传递到他的父级直到document或window,所有浏览器都支持事件冒泡

    
    
  • 事件捕获

事件捕获与冒泡相反,不具体节点先接收事件,具体节点在最后接收事件,它的寓意在于事件在到达预定目标之前捕获他

Js高级程序设计第三版学习(十三章)_第1张图片

  • DOM事件流

DOM2级规定了事件流包括三个阶段 事件捕获, 处于目标阶段, 事件冒泡阶段,

Js高级程序设计第三版学习(十三章)_第2张图片

   2.事件处理程序:  

事件就是用户或者浏览器自身执行的动作, 而事件处理程序就是响应某个事件的函数,

  • HTML事件处理程序

 元素支持的每种事件,都可以使用相应的事件处理程序同名的HTML特性绑定,这个值应该是可以执行的js代码,

      
  • adhlasdhl
  • adhlasdhl
  • HTML事件处理程序可能存在几个缺点, 1: 用户可能在页面刚加载就触发事件, 而此时事件处理程序没有条件执行 2: 代码与HTML紧密耦合, 试想一下有100个li 每个li都需要一个click事件,突然想改变事件改怎么办?

    • DOM0级事件处理程序 

    通过js指定事件处理程序, 将一个函数赋值给一个事件处理程序的属性,删除事件可以给事件处理程序设置为null

     hehe.onclick = function() {
            console.log('hehe');
            hehe.onclick = null;
          };
    • DOM2级事件处理程序

    DOM2级定义了两个方法用来 添加或删除事件处理程序,addEventListener 添加 和removeEventListener 移除, 他们都接收3个参数, 参数1: 事件名, 参数2: 作为事件处理程序的函数, 参数3: 捕获阶段(true) or 冒泡阶段(false or 空)

         hehe.addEventListener('click',function(){
           console.log('hehe')
         })

    addEventListener 添加事件的主要好处是可以添加多个事件,但只能通过removeEventListener函数移除, 并且被移除的函数只能是外部函数, 不能是匿名函数, 另外说明下 addEventListener 的函数无法传参 hehe.addEventListener('click',fn('xxx')),像这种形式 fn会立即执行, 在不许要移除事件处理程序的情况下可以通过bind方式, 但我们需要移除的情况下,可以使用函数表达式的形式结合bind的方式

          var hehe = document.getElementById('hehe');
          var divClick = document.getElementById('divClick');
    
          function fn(name) {
            console.log(name);
          }
    
          var fff = fn.bind(hehe, 'lmxxxxx');
          console.log(fff == fn);// false
          console.log(typeof fff);// function
    
          /* 
            如果仅仅需要传递参数在并不需要移除的情况下 那么可以使用bind的形式
           */
          hehe.addEventListener('click', fn.bind(hehe,'lmx'));//lmx
          divClick.onclick = function(){
            hehe.removeEventListener('click',fn.bind(hehe,'lmx')); // 无效
          }
    
    
          /* 
            如果需要移除 可以使用函数表达式的形式 将绑定fff 并移除fff
           */
           hehe.addEventListener('click', fff);//lmx lmxxxxx
          divClick.onclick = function(){
            hehe.removeEventListener('click',fff); // 移除成功
          }
    • IE事件处理程序

    由于ie8及一下,不支持事件捕获, 但有类似于addEventListener的两个方法, attachEvent(增加), detachEvent(删除), 但需要注意的是 attachEvent 参数1 是事件处理程序而不是事件 (onclick 而不是 click) ,而且 参数2 使用匿名函数时 this指向的时window而addEventListener 匿名函数this指向触发事件的元素,另外后添加的事件处理程序先执行(addEventListener 也是)

          var hehe = document.getElementById('hehe');
          var divClick = document.getElementById('divClick');
    
          function fn(name) {
            console.log('name');
          }
    
          hehe.attachEvent('onclick',fn)
          divClick.onclick = function(){
            hehe.detachEvent('onclick',fn)
          }
    
          hehe.attachEvent('onclick',function(){
            console.log(this === window) //true
          })

       3.事件对象:  

    在触发DOM上的某个事件的时候,会产生event对象,这个对象包含着所有与事件有关的信息,虽然所有浏览器都支持,但方式不同, 如火狐的事件方法的参数必须传入event ,而chrome 直接在函数体中使用event就可以了

    • DOM的事件对象

    无论指定事件的是什么方法, DOM浏览器都会把event对象传入事件处理器中,在事件结束后event对象被销毁,event对象会根据触发事件类型的不同,属性和方法也不同,不过所有事件都会有一些固定属性,和方法

          var divClick = document.getElementById('divClick');
          var uu = document.getElementById('uu');
    
          divClick.onclick = function(event) {
            //是否冒泡
            console.log(event.bubbles);
            //是否可以取消默认行为
            console.log(event.cancelable);
            //表示是否调用了event.preventDefault()
            console.log(event.defaultPrevented);
            //事件阶段 1捕获 2 目标阶段 3冒泡阶段
            console.log(event.eventPhase);
            //事件的类型
            console.log(event.type); //click
            //正在执行事件的元素
            console.log(event.currentTarget); //divClick
            //触发事件的目标元素
            console.log(event.target); //divClick
            //阻止当前元素的默认事件 如a标签的跳转
            event.preventDefault();
            console.log(event.defaultPrevented); //true
            //阻止事件冒泡 点击divClick后 uu的click不会执行
            event.stopPropagation();
          };
    
          uu.onclick = function(event) {
            /* 
              当点击uu元素本身的时候触发click时 currentTarget = target 为uu
              但当点击uu内部元素divClick 触发click时 click方法冒泡到了uu
              此时uu执行click事件,event的currentTarget为 uu 而target 为divClick
             */
            console.log(event.currentTarget); //uu
            console.log(event.target); //divClick
          };
    
          /* 
             event.stopImmediatePropagation();
             不单单会阻止事件冒泡,还会阻止事件分发(在绑定多个click处理器的情况下)
             当执行 event.stopPropagation() 会打印 12 13 14
             当执行 event.stopImmediatePropagation() 会打印 12
           */
    
          divClick.addEventListener('click', function(event) {
            // event.stopPropagation()
             event.stopImmediatePropagation();
            console.log('12');
          });
          divClick.addEventListener('click', function(event) {
           
            console.log('13');
          });
          divClick.addEventListener('click', function(event) {
            console.log('14');
          });
    •  IE中的事件对象

    ie8及一下不支持通过传参的形式,只能通过window.event,event函数稍有不用

          divClick.onclick = function(event){
            var e = event || window.event;
            console.log(e);
            //srcElement 类似target
            console.log(e.srcElement === this);
            // 类似于 preventDefault()
            e.returnValue = false;
            //类似于 stopPropagation()
            e.cancelBubble = true
          }

       4.事件类型:  

    浏览器中可能发生的类型有很多种,不同类型具有不同的信息

    • UI事件

    UI事件指不一定与用户操作有关,如 load (当页面加载完毕,或者img加载完毕触发) unload(页面卸载,关闭或刷新) abort(页面停止下载) .... 等等

          var divClick = document.getElementById('divClick');
          var uu = document.getElementById('uu');
          //页面加载完成后执行的方法
          window.onload = function(){
            var img = document.createElement('img')
            //图片加载完成
            img.onload = function(){
              console.log(1);
            }
            img.src = './loading.png';
            divClick.appendChild(img);
          }
    
          //调整浏览器大小后触发
          window.onresize = function(event){
            console.log('resize')
          }
    
          /* 
            滚动滚动条时触发, 如果存在 用documentElement
            如果不存在用body
           */
          window.onscroll = function(event){
            console.log(event.target.body.scrollTop);//0
            console.log(event.target.documentElement.scrollTop);//滚动值
          }
    • 焦点事件

    在页面获得焦点或者失去焦点时触发, 也可以通过手动调用 element.focus()触发

          var input = document.getElementById('fouces');
    
          /* 
           focus与blur不冒泡
           */
          
          input.onfocus = function(){
            console.log('获得焦点')
          }
    
          input.onblur = function(){
            console.log('失去焦点')
          }
    
          /* 
            这种方式 浏览器无效
           */
          // input.onfocusin = function(){
          //   console.log('获得焦点')
          // }
    
          // input.onfocusout = function(){
          //   console.log('失去焦点')
          // }
    
          /* 
            这种方式可以, 但ie8 不支持需要用兼容写法
           */
          input.addEventListener('focusout',function(){
            console.log('失去焦点')
          })
    • 鼠标与滚轮事件

    DOM3级定义了9个鼠标事件,在鼠标事件的event对象中保存了一些关于鼠标的信息

          var hehe = document.getElementById('hehe');
          var divClick = document.getElementById('divClick');
          var uu = document.getElementById('uu');
          var input = document.getElementById('fouces');
    
          /* 
            鼠标按下
           */
          window.onmousedown = function(event){
            /* 
              clientX 鼠标在视口中位于X轴的坐标 clientY鼠标在视口中位于Y轴的坐标
             */
            console.log(event.clientX);
            console.log(event.clientY);
            /* 
               pageX 鼠标在页面中位于X轴的坐标 pageY鼠标在页面中位于Y轴的坐标
             */
             console.log(event.pageX);
             console.log(event.pageY);
    
             /* 
              screenX 鼠标在屏幕中位于X轴的坐标 screenY鼠标在屏幕中位于Y轴的坐标
             */
             console.log(event.screenX);
             console.log(event.screenY);
             /* 
              offsetX鼠标在触发元素内部的X轴坐标 offsetY鼠标在触发元素内部的Y轴坐标
              新版火狐已经支持
             */
             console.log(event.offsetX);
             console.log(event.offsetY);
    
            console.log('鼠标按下')
          }
    
          /* 
            鼠标抬起
           */
          window.onmouseup = function(){
            console.log('鼠标抬起')
          }
    
          /* 
            单击事件,
             一次click事件 先触发onmousedown然后onmouseup
             最后onclick
           */
          window.onclick = function(){
            console.log('单击')
          }
    
          /* 
            双击事件
            一次dbclick 触发两次onmousedown,onmouseup,onclick
            顺序onmousedown,onmouseup,onclick,onmousedown,onmouseup,onclick,ondbclick
           */
          window.ondblclick = function(){
            console.log('双击')
          }
    
          /* 
            鼠标进入元素触发 不冒泡
           */
           hehe.onmouseenter = function(){
            console.log('鼠标进入元素')
          }
          /* 
            鼠标离开元素触发 不冒泡
           */
           hehe.onmouseleave = function(){
             console.log('鼠标离开一个元素')
           }
    
           /* 
            鼠标在一个元素上, 在进入另一个元素时触发(包括包含元素),此方法冒泡 
            在鼠标经过后代元素上会频繁触发
           */
           hehe.onmouseover = function(){
             console.log('鼠标在一个元素上')
           }
           /* 
            鼠标在一个元素上, 在离开这个元素时触发(包括包含元素),此方法冒泡 
            在鼠标经过后代元素上会频繁触发
           */
           hehe.onmouseout = function(){
            console.log('鼠标离开一个元素')
           }
    
           hehe.onmousemove = function(){
             console.log('鼠标在元素上移动')
           }

    DOM规定了4个属性, 当某个鼠标事件发生时,用来检测确定用户同时按下了其中某几个键

          hehe.onclick = function(event){
            //是否按下了shift键
            console.log(event.shiftKey)
            //是否按下了ctrl键
            console.log(event.ctrlKey)
            //是否按下了alt
            console.log(event.altKey)
            //是否按下了meta键 window下是windows键
            console.log(event.metaKey)
          }

     触发click事件时event.button 记录了点击的鼠标按钮 0 代表左键 1中间键 2代表又键(最新版都相同), event.detail记录鼠标单击的次数 但各个浏览器都不相同 chrome pc端 会累计相加 模拟phone 恒等于1  ff pc与手机端相同 数字不超过3  ie11 恒等于0

    针对onmouseout 与 onmouseover 的event对象中的 relatedTarget属性 , 这个属性 在onmouseout表示 移动到的元素(包括子元素), 在onmouseover表示被离开的元素

          hehe.onmouseover = function(){
             /* 
              onmouseover的relateTarget 表示被离开的元素
             */
            console.log('onmouseover')
            console.log(event.relatedTarget);
          }
          hehe.onmouseout = function(){
            /* 
              onmouseout的relateTarget 表示移动到的元素
             */
            console.log('onmouseout')
            console.log(event.relatedTarget)
          }
    •  键盘与文本事件

    用户使用键盘时会触发键盘事件,所有元素都支持三个事件,keydown,keyup,keypress和一个文本事件textInput(ie11,ff不支持),keydown与keypress都是在文本框变化之前触发,keyup在文本框变化之后触发, keydown,keyup,在用户按下键时就触发, 而keypress在用户按下字符键触发包括退格按钮,textInput仅仅在字符键按下触发

    keydown,keyup 键码keyCode键码相同 但要是汉字的化 ff, chrome都显示299,ie 按下299 抬起时英文编码, keypress的keyCode, 与keydown,keyup 键码不同, ff中文和英文都恒等于0, ie,chrome中文不显示,英文相同, 而keypress的charCode 与keyCode字符编码相同.

    keydown,keyup,keypress的 key属性 否返回按键的字符串如 按下shift 返回''shift.

    DOM3级事件中添加了 location属性, 返回1-5数字 0:默认键盘 1:左侧 2右侧 3小键盘 4移动设备 5手柄

    •  变动事件 

    在改变DOM结构时 我们可以为元素添加事件 但是兼容性太好 只列出绝大部分浏览器兼容的事件,DOMSubtreeModified(DOM结构改变就触发),DOMNodeInserted(插入节点触发),DOMNodeRemoved(移除或替换触发),可冒泡

          var hehe = document.getElementById('hehe');
          var divClick = document.getElementById('divClick');
          var uu = document.getElementById('uu');
          var input = document.getElementById('fouces');
    
          uu.addEventListener('DOMSubtreeModified', function() {
            console.log('DOM结构改变了');
          });
    
          uu.removeChild(uu.children[0])
    
          uu.addEventListener('DOMNodeInserted',function(){
            console.log('节点插入了')
          })
    
          hehe.appendChild(input);//未触发
    
          uu.addEventListener('DOMNodeRemoved',function(){
            console.log('节点移除了')
          })
    
          hehe.removeChild(input);//未触发
    • H5事件

    H5列出了浏览器应该支持的所有事件

    contextmenu事件(右键菜单)

          window.oncontextmenu = function(event){
            console.log(1);
            //可以阻止右键弹出菜单
            event.preventDefault()
          }

    beforeunload 页面刷新或者关闭页面触发, 此事件内alert confrim等无效,

        /* 
            离开页面或刷新触发 alert等弹窗无效
            返回值 为弹窗显示的值 询问客户的字符串
           */
          window.onbeforeunload = function(event){
            var msg = "确定要离开么";
            return msg;
          }

    DOMContentLoaded 事件 不必等到页面完全加载就可以执行,他在DOM树完成后就可以触发,这跟jq $(document).ready()效果是一样的, js标签之所以放在body的结束标签之前 是因为浏览器解析js时会阻断代码 浏览器执行完js后再接着构建html, 所以书中建议css放在头部, 而js代码放在body结束标签之前

          window,addEventListener('DOMContentLoaded',function(){
            console.log('1')
          })

    onreadystatechange事件, 早readyState状态改变时 触发事件,readyState有5个状态, uninitialized(未初始化),loading(正在加载) loaded(加载完毕) interactive(可以操作,但是未加载完全) complete(完成)

    如果往返缓存中取得页面, 页面时不会再次执行onload事件的, 所以我们可以使用 pageshow(ie11已支持) 和pagehide, pageshow在onload执行之后执行, 只要页面重新恢复或者页面重新加载时就会触发,.pagehide同理但是在unload之前触发,两个事件都有persisted属性, 在pageshow中 如果时从缓存触发的persisted为true 否则false, pagehide 中, 如果页面进入缓存为true 否则false, (pc端测试ff chrome 都是重新加载, 手机端 测试 是从往返缓存)

    haschange事件, 只要URL的参数列表变化 就会触发

    • 触摸与手势事件

    触摸事件会在手机放在屏幕上面,移动,等触发, touchstart 手指触摸屏幕时触发, touchmove手指在频幕上滑动时触发,会频繁触发, touchend 手指离开屏幕触发, 在移动端touch与click区别, 先触发touchstart -->touchend-->click,click只有在短时间内切并未滑动时触发.

    触摸event对象 有额外三个属性 touches(当前屏幕触摸点集合)  targetTouches(绑定事件元素上的触摸点集合) changedTouches(触摸事件改变的元素集合)

    手势事件电脑不太方便测 先略

     5.内存和性能:   

    在js中事件处理程序的数量, 影响着 页面的整体性能, 每个函数都是对象, 内存中对象越多,性能就越差.

    •  事件委托

    针对事件处理程序过多, 可以采用事件委托, 实质就是利用事件冒泡. 用一个事件处理程序,管理某一类所有的事件

          /* 
            可以给父元素绑定事件
            当子元素触发相同事件时 冒泡到父元素
            父元素根据子元素类型 来判断如何执行
           */
          uu.onclick =function(event){
            console.log(event.target)
          }
    • 移除事件处理程序

    在删除dom元素后, 事件处理程序极有可能不会被释放 , 可以在删除元素之前, 删除方法

      6.模拟事件:   

    • DOM中的事件模拟

     document.createEvent(),用来创建event对象并返回 ,   这个方法接收一个参数MouseEvent鼠标事件,UIEvent一般ui事件,MutationEvent DOM变动事件 单数为DOM3级事件 复数为DOM2级事件, 模拟事件一般需要三个步骤 创建 --> 注册-->触发,

          document.addEventListener("click",function(){
            console.log(11);
          })
    
    
          // 创建事件 参数 为要注册的事件类型
          var msTest = document.createEvent("MutationEvent")
          // 初始化事件  不同类型的事件需要的具体参数也不同 不过前4个参数 基本都需要
          // 事件类型, 是否冒泡, 事件是否可以取消, 视图
          msTest.initMouseEvent('click',true,true,document.defaultView)
          // 触发事件
          document.dispatchEvent(msTest) // 11

     

    你可能感兴趣的:(js)