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 相关文章推荐
js程序中美元符号$是什么
Jun 05 Javascript
javascript parseInt 大改造
Sep 27 Javascript
JavaScript中的apply()方法和call()方法使用介绍
Jul 25 Javascript
解决js数据包含加号+通过ajax传到后台时出现连接错误
Aug 01 Javascript
JS实现alert中显示换行的方法
Dec 17 Javascript
基于javascript编写简单日历
May 02 Javascript
AngularJS轻松实现双击排序的功能
Aug 30 Javascript
js实现固定宽高滑动轮播图效果
Jan 13 Javascript
详解Vue前端对axios的封装和使用
Apr 01 Javascript
JQuery中DOM节点的操作与访问方法实例分析
Dec 23 jQuery
ES6 Iterator遍历器原理,应用场景及相关常用知识拓展详解
Feb 15 Javascript
JavaScript实现横版菜单栏
Mar 17 Javascript
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&mysql 日期操作小记
2012/02/27 PHP
PHP下载文件函数与用法示例
2019/09/27 PHP
showModelessDialog()使用详解
2006/09/21 Javascript
动态的改变IFrame的高度实现IFrame自动伸展适应高度
2012/12/28 Javascript
JavaScript实现自动生成网页元素功能(按钮、文本等)
2015/11/21 Javascript
Javascript实现图片轮播效果(二)图片序列节点的控制实现
2016/02/17 Javascript
AngularJS ng-mousedown 指令
2016/08/02 Javascript
利用原生js和jQuery实现单选框的勾选和取消操作的方法
2016/09/04 Javascript
javascript iframe跨域详解
2016/10/26 Javascript
TypeScript学习之强制类型的转换
2016/12/27 Javascript
jQuery实现滚动条滚动到子元素位置(方便定位)
2017/01/08 Javascript
微信小程序 简单教程实例详解
2017/01/13 Javascript
基于bootstrap页面渲染的问题解决方法
2018/08/09 Javascript
解决vue-cli项目打包出现空白页和路径错误的问题
2018/09/04 Javascript
JavaScript实现的滚动公告特效【基于jQuery】
2019/07/10 jQuery
python根据出生年份简单计算生肖的方法
2015/03/27 Python
Python实现的计数排序算法示例
2017/11/29 Python
mac PyCharm添加Python解释器及添加package路径的方法
2018/10/29 Python
Flask框架模板渲染操作简单示例
2019/07/31 Python
PyTorch 对应点相乘、矩阵相乘实例
2019/12/27 Python
使用sklearn的cross_val_score进行交叉验证实例
2020/02/28 Python
解决keras加入lambda层时shape的问题
2020/06/11 Python
南非最受欢迎的时尚品牌:MRP
2016/09/18 全球购物
荷兰度假屋租赁网站:Aan Zee
2020/02/28 全球购物
大学校庆策划书
2014/01/31 职场文书
《乞巧》教学反思
2014/02/27 职场文书
成龙洗发水广告词
2014/03/14 职场文书
法制宣传日活动总结
2014/04/29 职场文书
会计电算化专业求职信
2014/06/10 职场文书
县政府班子个人对照检查材料
2014/10/05 职场文书
出差报告格式模板
2014/11/06 职场文书
董事长岗位职责
2015/02/13 职场文书
农业项目投资意向书
2015/05/09 职场文书
python 实现体质指数BMI计算
2021/05/26 Python
Python函数中的不定长参数相关知识总结
2021/06/24 Python
Python基本知识点总结
2022/04/07 Python