JavaScript 事件绑定及深入


Posted in Javascript onApril 13, 2015

事件绑定分为两种:

一种是传统事件绑定(内联模型/脚本模型);上一章内容;
一种是现代事件绑定(DOM2级模型);现代事件绑定在传统事件绑定基础上提供了更强大的功能;
一 传统事件绑定的问题

// 脚本模型将一个函数赋值给一个事件处理函数;
  var box = document.getElementById('box');  // 获取元素;
  box.onclick = function(){          // 元素点击触发事件;
    alert('Lee');
  }

// 问题一:一个事件处理函数触发两次事件;
  window.onload = function(){         // 第一组程序;
    alert('Lee');
  }
  window.onload = function(){         // 第二组程序;
    alert('Mr.Lee');
  }
  // PS:当两组程序同时执行的时候,后面一个会把前面一个完全覆盖;
  // 导致前面的window.onload完全失效了;
// 解决方案:
  window.onload = function(){         // 第一组事件处理程序,会被覆盖;
    alert('Lee');
  }
  if(typeof window.onload == 'function'){   // 判断之前是否有window.onload;
    var saved = null;            // 创建一个保存器;
    saved = window.onload;          // 把之前的window.onload保存起来;
  }
  window.onload = function(){         // 下一个要执行的事件;
    // saved()=window.onload = function
    if(saved)saved();            // 判断之前是否有事件,如果有则先执行之前保存的事件;
    alert('Mr.Lee');             // 执行本事件的代码;
  }
// 问题二:事件切换器
  box.onclick = boBlue;             // 第一次执行toBlue();
  function toRed(){
    this.className = 'red';
    this.onclick = toBlue;          // 第三次执行roBlue(),然后来回切换;
  }
  function toBlue(){
    this.className = 'blue';
    this.onclick = toRed;          // 第二次执行toRed();
  }
  // 这个切换器在扩展的时候,会出现一些问题:
  1.如果增加一个执行函数,那么会被覆盖;
  box.onclick = toAlert;            // 被增加的函数;
  box.onclick = toBlue;            // toAlert被覆盖了;

  2.如果解决覆盖问题,就必须包含同时执行;
  box.onclick = function(){          // 包含进去,但可读性降低;
    toAlert();                // 第一次不会被覆盖,但第二次又被覆盖;
    toBlue.call(this);            // 还必须把this传递到切换器里;
  }
// 综上三个问题:覆盖问题/可读性问题/this传递为题;
// 我们创建一个自定义事件处理函数;
  function addEvent(obj,type,fn){       // 取代传统事件处理函数;
    var saved = null;            // 保存每次触发的事件处理函数;
    if(typeof obj['on'+type] == 'function'){// 判断是不是存在事件;
      saved = obj['on'+type];       // 如果有,保存起来;
    }
    obj['on'+type] = function(){      // 然后执行;
      if(saved)saved();          // 执行上一个;
      fn.call(this);           // 执行函数,把this传递进去;
    }
  }
  addEvent(window,'load',function(){
    alert('Lee');              // 可以执行;
  });
  addEvent(window.'load',function(){
    alert('Mr.Lee');            // 可以执行;
  })

// 用自定义事件函数注册到切换器上查看效果:
  addEvent(window,'load',function(){
    var box = document.getElementById('box');
    addEvent(box,'click',toBlue);
  });
  function toRed(){
    this.className = 'red';
    addEvent(this,'click',toBlue);
  }
  function toBlue(){
    this.className = 'blue';
    addEvent(this,'click',toRed);

二 W3C事件处理函数
// "DOM2级事件"定义了两个方法,用于添加事件和删除事件的处理程序:addEventListener()和removeEventListener();

// 所有DOM节点中都包含这两个方法,并且它们都接收3个参数:事件名/函数/冒泡或捕获的布尔值(true表示捕获,false表示冒泡);
  window.addEventListener('load',function(){
    alert('Lee');
  },false);
  window.addEventListener('load',function(){
    alert('Mr.Lee');
  },false);
  // PS:W3C的事件绑定好处:1.不需要自定义了;2.可以屏蔽相同的函数;3.可以设置冒泡和捕获;
  window.addEventListener('load',init,false);    // 第一次执行了;
  window.addEventListener('load',init,false);    // 第二次被屏蔽了;
  function init(){
    alert('Lee');
  }

// 事件切换器
  window.addEventListener('load',function(){
    var box = document.getElementById('box');
    box.addEventListener('click',function(){    // 不会被覆盖/误删;
      alert('Lee');
    },false);
    box.addEventListener('click',toBlue,false);  // 引入切换;
  },false);

  function toRed(){
    this.className = 'red';
    this.removeEventListener('click',toRed,false); // 移除事件处理函数;
    this.addEventListener('click',toBlue,false);  // 添加需要切换的事件处理函数; 
  }

  function toBlue(){
    this.className = 'blue';
    this.removeEventListener('click',toBlue,false);
    this.addEventListener('click',toRed,false);
  }

// 设置冒泡和捕获阶段
  document.addEventListener('click',function(){
    alert('document');
  },true);                    // 设置为捕获;

  document.addEventListener('click',function(){
    alert('Lee');
  },false);                    // 设置为冒泡;

三 IE事件处理函数
// IE中实现了与DOM中类似的两个方法:attachEvent()和detachEvent();

// 这两个方法接收相同的参数:事件名和函数;

// 在使用这两组函数的时候,区别:
// 1.IE不支持捕获,只支持冒泡;
// 2.IE添加事件不能屏蔽重复的函数;
// 3.IE中的this指向的是window而不是DOM对象;
// 4.在传统事件上,IE是无法接受到event对象的;但使用了attachEvent()却可以;
  window.attachEvent('onload',function(){
    var box = document.getElementById('box');
    box.attachEvent('onclick',toBlue);
  });

  function toRed(){
    var that = window.event.srcElement;
    that.className = 'red';
    that.detachEvent('onclick',toRed);
    that.attachEvent('onclick',toBlue);
  }

  function toBlue(){
    var that = window.event.srcElement;
    that.className = 'blue';
    that.detachEvent('onclick',toBlue);
    that.attachEvent('onclick',toRed);
  }
  // PS:IE不支持捕获;
  // IE不能屏蔽;
  // IE不能传递this,可以call过去;

// 在传统绑定上,IE是无法像W3C那样通过传参接受event对象;但如果使用了attachEvent()却可以;
  box.onclick = function(evt){
    alert(evt);                // undefined;
  }

  box.attachEvent('onclick',function(evt){
    alert(evt);                // object;
    alert(evt.type);              // click;
  });

// 兼容IE和W3C的事件切换器函数;
  function addEvent(obj,type,fn){        // 添加事件处理程序兼容;
    if(obj.addEventListener){
      obj.addEventListener(type,fn);
    }else if(obj.attachEvent){
      obj.attachEvent('on'+type,fn);
    }
  }

  function removeEvent(obj,type,fn){      // 移除事件处理程序兼容;
    if(obj.removeEventListener){
      obj.removeEventListener(type,fn);
    }esle if(obj.detachEvent){
      obj.detachEvent('on'+type,fn);
    }
  }

  function getTarget(evt){           // 得到事件目标;
    if(evt.target){
      return evt.target;
    }else if(window.event.srcEleemnt){
      return window.event.srcElement;
    }
  }

四 事件对象补充

1.relatedTarget
// 这个属性可以在mouseover和mouseout事件中获取从哪里移入和从哪里移出的DOM对象;
  box.onmouseover = function(evt){      // 鼠标移入box;
    alert(evt.relatedTarget);        // 获取移入box之前的那个元素;
  }
  box.onmouseout = function(evt){       // 鼠标移出box;
    alert(evt.relatedTarget);        // 获取移出box之后到的那个元素;
  }

// IE提供了两组与之对应的属性:fromElement和toElement;
// 兼容函数
  function getEarget(evt){
    var e = evt || window.event;      // 得到事件对象;
    if(e.srcElement){            // 如果支持srcElement,表示IE;
      if(e.type == 'mouseover'){     // 如果是over事件;
        return e.fromeElement;     // 就使用from;
      }else if(e.type == 'mouseout'){   // 如果是out;
        return e.toElement;       // 就使用to;
      }
    }else if(e.relatedTarget){       // 如果支持relatedTarget,表示W3C;
      return e.relatedTarget;
    }
  }

2.阻止事件的默认行为

// 一个超链接的默认行为就点击然后跳转到指定的页面;
// 那么阻止默认行为就可以屏蔽跳转的这种操作,而实现自定义操作;
// 取消事件默认行为还有一种不规范的做法,就是返回false;
  link.onclick = function(){
    alert('Lee');            
    return false;              // 直接返回false,就不会跳转了;
  }
  // PS:虽然return false;可以实现这个功能,但有漏洞;
  // 第一:代码必须写到最后,这样导致中间的代码执行后,有可能执行不到return false;
  // 第二:return false写到最前那么之后的自定义操作就失败了;
  // 解决方案:在最前面就阻止默认行为,并且后面还能执行代码;
  function preDef(evt){            // 跨浏览器兼容阻止默认行为;
    var e = evt || window.event;
    if(e.preventDefault){
      e.preventDefault();         // W3C,阻止默认行为;
    }else{
      e.returnValue = false;        // IE,阻止默认行为;
    }
  }
3.上下文菜单事件contextmenu
// 当我们右击网页的时候,会自动出现windows自带的菜单;
// 那么我们可以使用contextmenu事件来修改我们指定的菜单;但前提是把右击的默认行为取消;
  addEvent(window,'load',function(){
    var text = docuemnt.getElementById('text');
    addEvent(text,'contextmenu',function(evt){    // 添加右键菜单事件处理程序;
      var e = evt || window.event;
      preDef(e);                  // 阻止默认行为函数;
      var menu = document.getElementById('menu');  // 找到自定义的menu对象;
      menu.style.left = e.clientX+'px';       // 确定自定义menu在屏幕上的位置;
      menu.style.top = e.clientX+'px';
      menu.style.visibility = 'visible';      // 设置自定义menu的属性为可见;
      addEvent(document,'click',function(){     // 给document添加单击事件处理程序;
        docuemnt.getElementById('myMenu').style.visibility = 'hidden';  //将自定义的menu隐藏;
      });
    });
  });

4.卸载前事件beforeunload

// 这个事件可以帮助在离开本页的时候给出相应的提示;"离开"或"返回"操作;
  addEvent(window.'beforeunload',function(evt){
    var evt = event || window.event;
    var message = '是否离开此页?';
    evt.returnValue = message;
    return message;
  });

5.鼠标滚轮(mousewheel)和DOMMouseScroll

// 用于获取鼠标上下滚轮的距离;
  addEvent(docuemnt,'mousewheel',function(evt){    // 非Firefox;
    alert(getWD(evt));
  });
  addEvent(docuemnt,'DOMMouseScroll',function(evt){  // Firefox;
    alert(getWD(evt));
  });
 
  function getWD(evt){
    var e = evt || window.event;
    if(e.wheelDelta){                // mousewheel事件的滚动值保存在wheelDelta里;
      return e.wheelDelta;
    }else if(e.detail){               // DOMMouseScroll事件的滚动值保存在detail里;
      return -evt.detail*30;            // 保持计算的统一;
    }
  }
Javascript 相关文章推荐
JQuery toggle使用分析
Nov 16 Javascript
浅析Prototype的模板类 Template
Dec 07 Javascript
引用其它js时如何同时处理多个window.onload事件
Sep 02 Javascript
利用a标签自动解析URL分析网址实例
Oct 20 Javascript
JS动画效果打开、关闭层的实现方法
May 09 Javascript
JS获取下拉框显示值和判断单选按钮的方法
Jul 09 Javascript
微信小程序  生命周期详解
Oct 27 Javascript
详解React native全局变量的使用(跨组件的通信)
Sep 07 Javascript
浅析node应用的timing-attack安全漏洞
Feb 28 Javascript
vue 集成jTopo 处理方法
Aug 07 Javascript
js实现全选和全不选功能
Jul 28 Javascript
vue3.0 项目搭建和使用流程
Mar 04 Vue.js
JavaScript 事件对象介绍
Apr 13 #Javascript
JavaScript 事件入门知识
Apr 13 #Javascript
JavaScript 动态加载脚本和样式的方法
Apr 13 #Javascript
JavaScript DOM元素尺寸和位置
Apr 13 #Javascript
JavaScript DOM操作表格及样式
Apr 13 #Javascript
JavaScript DOM进阶方法
Apr 13 #Javascript
JavaScript DOM基础
Apr 13 #Javascript
You might like
php上传文件的增强函数
2010/07/21 PHP
一个简单至极的PHP缓存类代码
2015/10/23 PHP
php使用ffmpeg获取视频信息并截图的实现方法
2016/05/03 PHP
启用OPCache提高PHP程序性能的方法
2019/03/21 PHP
laravel5.5安装jwt-auth 生成token令牌的示例
2019/10/24 PHP
PHP+fiddler抓包采集微信文章阅读数点赞数的思路详解
2019/12/20 PHP
Centos7安装swoole扩展操作示例
2020/03/26 PHP
各种常用浏览器getBoundingClientRect的解析
2009/05/21 Javascript
js本身的局限性 别让javascript做太多事
2010/03/23 Javascript
js 代码优化点滴记录
2012/02/19 Javascript
jquery之超简单的div显示和隐藏特效demo(分享)
2013/07/09 Javascript
javascript的动态加载、缓存、更新以及复用(一)
2014/06/09 Javascript
node.js中的http.createServer方法使用说明
2014/12/14 Javascript
js实现tab切换效果实例
2015/09/16 Javascript
微信小程序 animation API详解及实例代码
2016/10/08 Javascript
详解使用Visual Studio Code对Node.js进行断点调试
2017/09/14 Javascript
深入剖析Node.js cluster模块
2018/05/23 Javascript
vue动态改变背景图片demo分享
2018/09/13 Javascript
在vue中获取token,并将token写进header的方法
2018/09/26 Javascript
在JS循环中使用async/await的方法
2018/10/12 Javascript
js判断鼠标移入移出方向的方法
2020/06/24 Javascript
[01:10]为家乡而战!完美世界城市挑战赛全国总决赛花絮
2019/07/25 DOTA
全球知名提供各类营养保健品的零售商:Vitamin Shoppe
2016/10/09 全球购物
TIME时代杂志台湾总代理:台时亚洲
2018/10/22 全球购物
卡拉威高尔夫官方网站:Callaway Golf
2020/09/16 全球购物
财务会计实习报告体会
2013/12/20 职场文书
汽车运用工程专业毕业生推荐信
2013/12/25 职场文书
人力资源部副职的竞聘演讲稿
2014/01/07 职场文书
高中体育教学反思
2014/01/29 职场文书
村党支部书记承诺书
2014/05/29 职场文书
健康状况证明模板
2014/10/23 职场文书
2015年妇委会工作总结
2015/05/22 职场文书
一年之计:2019年下半年的计划
2019/05/07 职场文书
利用Python读取微信朋友圈的多种方法总结
2021/08/23 Python
德生BCL3000抢先使用感受和评价
2022/04/07 无线电
PostgreSQL出现死锁该如何解决
2022/05/30 PostgreSQL