第二十六课:jQuery对事件对象的修复

因为原生的event对象,在不同浏览器下,有不同的属性和方法,因此需要用jQuery进行兼容。

jQuery在这里分两步走,首先创建一个伪事件类jQuery.Event(jQuery里面自定义的事件类),这个事件类会统一处理事件对象的兼容性问题,比如:stopPropagation,preventDefault方法。然后通过jQuery.event.fix方法,针对不同的事件类型修复特定的属性。比如:mousewheel,keydown等事件类型。

jQuery.Event = function(src, props){

  if(!(this instanceof jQuery.Event)){  //如果不是new jQuery.Event(),就返回一个new出来的jQuery.Event对象

    return new jQuery.Event(src,props);

  }

  if(src && src.type){   //如果传进来的src有type属性,也就是event对象

    this.originalEvent = src; //存储原始事件,也就是event事件对象

    this.type = src.type;

    this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||src.getPregentDefault && src.getPreventDefault()) ? returntrue : return false;    //如果传进来的event对象是阻止默认事件的,那么自定义事件(jQuery.Event)的isDefaultPrevented 属性为returntrue。

  }

  else{     //如果传进来的src没有type属性,也就是不是event对象而是事件类型,比如click,就把src当做此事件的type属性值,也就是这个事件类的事件类型就是src

    this.type = src;

  }

  if(props){

    jQuery.extend(this,props);   //如果传入了一个对象,就复制它的属性到自定义事件对象中。

  }

  this.timeStamp = src && src.timeStamp || jQuery.now(); //有些浏览器下,事件对象有timeStamp属性,没有的,就用now方法,得到当前日期的毫秒数

  this[jQuery.expando] = true;    //标识这个自定义事件对象已经修正过

}

jQuery.Event.prototype = {

  preventDefault : function(){

    this.isDefaultPrevented = returnTrue;

    var e = this.originalEvent;

    if(!e){    //如果传入的不是event对象,就没有阻止事件默认行为。

      return;

    }

    if(e.preventDefault){

      e.preventDefault();

    }else{

      e.returnValue = false;  //   IE下阻止事件默认行为的代码

    }

  }, 

  stopPropagation: function(){

    this.isPropagationStopped = returnTrue;

    var e = this.originalEvent;

    if(!e){    //如果传入的不是event对象,就不用阻止冒泡行为。

      return;

    }

    if(e.stopPropagation){

      e.stopPropagation();

    }

    e.cancelBubble= true;  //   IE下阻止事件冒泡的代码

  },

  stopImmediatePropagation: function(){

    this.isImmediatePropagationStopped = returnTrue;

    this.stopPropagation();

  },

  isDefaultPrevented : returnFalse,

  isPropagationStopped  : returnFalse,

  isImmediatePropagationStopped : returnFalse

}

jQuery.event.fix也很简单,它把大部分逻辑分担给其他钩子对象了,比如:mouseHooks处理mouse事件对象的不同属性,keyHooks处理键盘事件对象的不同属性。

fix = function(event){

  if(event[jQuery.expando]){  //如果事件对象已经修正了,那么就直接返回。

    return event;

  }

  var i,prop,originalEvent = event,fixHook = jQuery.event.fixHooks[event.type] || {},  //查找此事件类型是否有相对应的钩子对象。

    copy = fixHook.props ? this.props.concat(fixHooks.props) : this.props;  //如果此钩子对象有相应的属性集合,就把这些属性集合合并到当前this.props属性集合中

  event = jQuery.Event(originalEvent);   //得到jQuery自定义事件对象

  for(i=copy.length;i;){

    prop = copy[--i];  //得到此事件对象的属性名

    event[prop] = originalEvent[prop]; //把原始对象的属性值赋给此jQuery事件对象的属性名

  }

   if(!event.target){   //IE6-8下是srcElement

    event.target = originalEvent.srcElement || document;

  }

  if(event.target.nodeType === 3){    //文本节点不能作为事件目标元素,在火狐和safari下

    event.target = event.target.parentNode;

  }

  event.metaKey = !!event.metaKey;//metaKey事件属性可返回一个布尔值,指示当事件发生时,"meta" 键是否被按下并保持住。大多数键盘上并不存在Meta键,该键存在于MIT计算机、Mac计算机或Sun公司的一些计算机键盘上。IE6-8下不支持。

  return fixHook.filter ? fixHook.filter(event,originalEvent) : event; //此事件相对应的钩子对象是否有filter方法,如果有就证明此事件对象需要进行修复处理。因此执行钩子对象的filter方法修复事件对象,然后返回这个修复后的event。

}

jQuery只修复了键盘事件和鼠标事件。

键盘事件的重点在于修复keyCode,不过W3C已经规定which才是标准属性,因此兼容性写法是:

if(event.which == null){  //如果不支持W3C的which属性。就进行修复。

  event.which = event.charCode !=null ? event.charCode : event.keyCode;   //先取事件对象的charCode属性值,如果这属性值不存在,就取event的keyCode属性值

}

鼠标事件的重点是修复pageX/pageY,relatedTarget与用于左中右键的which属性。

clientX clientY 为事件触发时鼠标在当前浏览器可视区的坐标

screenX screenY 为事件触发时鼠标在屏幕的坐标

pageX pageY 为事件触发时鼠标在整个document的坐标,IE6-8不支持。

offsetX offsetY 为事件触发时鼠标在事件源元素的坐标,不过元素的区域有很多计算方式,按规范来说,参考点是padding围成的区域(不包括滚动条和边框)的左上角。

layerX layerY 为事件发生时鼠标相当于事件源元素的offsetParent的坐标,IE6-8不支持。

x y 是IE的layerX,layerY。但是IE6-8的offsetParent存在bug,比如td元素的offsetParent总是table,不推荐使用这两个属性。

我们修复IE6-8下的pageX和pageY:

e.pageX = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft);

e.pageY = e.clientY + (document.documentElement.scrollTop || document.body.scrollTop);

IE兼容模式下,窗口滚动条是出现在body中的,标准模式下,窗口滚动条出现在html。webkit下,窗口滚动条出现在body中,火狐和Opera的滚动条在html中。当窗口滚动条出现在body中时,document.body.scrollLeft是我们想要的值,而document.documentElement.scrollLeft为0,反之亦然。

在IE6-7标准模式以及IE全系列的怪异模式下,页面左上角存在2px的偏移值(表现在document.body.clientLeft,document.body.clientTop),这会导致我们在IE下取到的pageX和pageY比其他的浏览器大2px。

在IE下触发标准模式很简单,使用<!DOCTYPE HTML>在页面最前面就行了,它的前面不能有任何字符。切换成怪异模式有以下几种方法:

(1)在最前面加上非法的标签,如<ddd></ddd>

(2)在最前面加上一段文本,如ddddddd

(3)在IE6下,在最前面加上XML声明,如:<?xml version="1.0" encoding="utf-8"?>。

一般使用第一个方案。

IE在怪异模式下,页面显示用的顶级容器是body,2px 的偏移在body中表现,我们可以通过在body{ border:0},消除这个2px的偏移量。而且也可以使用html{border:0}消除。意思就是在怪异模式下,IE可以通过在body或html中设置boder:0消除2px的偏移。

那么,标准模式下,IE6-7呢?标准模式下,IE6可以通过以上方式消除2px的偏移,但是IE7不行,所以要兼容所有浏览器,就不能使用修改样式的方式。因此完美方案:

if(event.pageX ==null && event.clientX !=null){   //处理鼠标事件的

  var doc = event.target.ownerDocument || document;

  var box = document.compatMode == "BackCompat" ? doc.body : doc.documentElement; //文档模式是怪异模式,就使用body

  event.pageX = event.clientX + (box && box.scrollLeft || 0) - (box && box.clientLeft || 0);

  event.pageY = event.clientY + (box && box.scrollTop || 0) - (box && box.clientTop || 0);

}

如果不是html文档,而是xml或svg文档,就没有scrollLeft和clientLeft属性。

加油!

时间: 2024-07-30 23:46:59

第二十六课:jQuery对事件对象的修复的相关文章

第二十六篇 jQuery 学习8 遍历-父亲兄弟子孙元素

jQuery 学习8 遍历-父亲兄弟子孙元素 jQuery遍历,可以理解为"移动",使用"移动"还获取其他的元素. 什么意思呢?老师举一个例子: 班上30位同学,我是新来负责教这个班学生的老师,但我不认识所有学生,只认识上学期教过的几位同学.比如小明.我们再用一小串代码来作解释: <body> <span id="ming">我是小明</span> <span>我坐在小明后面,我叫李四</sp

AGG第二十六课 裁剪功能

AGG有四种类型的裁剪,分别工作在不同的层次 1. 基础渲染器Base Render 除非直接调用基础渲染器的绘制线段的方法,否则在一般情况下,都是在render_scanline的时候被调用,进行裁剪,这个时候已经进行了大量无用的工作.比如顶点源超出屏幕范围,在调用render_scanline函数之前,比如调用rasterizer对象的add_path函数,已经浪费大量的资源 2 光栅器rasterizer rasterizer主要应用于根据顶点源,生成线段的详细信息,这个时候调用裁剪,可以

第二十六课

第一单元 语法部分重点使役态 使役态(使动态)变形规律:    1V:将词尾う段假名 -> あ段假名  + せる           書く 書かせる   読む 読ませる 2V:去掉词尾る   + させる              見る 見させる   食べる 食べさせる 3V:来(く)る 来(こ)させる            する させる 使役句(使动句) 意义:按照某人(使动者)的命令或者指示,另一个人(使动对象)去行动.一般表示强制命令.指示或者命令等. a.他动词使动句 主语は/が 使役对象

JAVA学习第二十六课(多线程(六))- 多生产者多消费者问题

多生产者多消费者问题 以生产馒头 消费馒头为例. class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name) { if (flag) { try { this.wait(); } catch (Exception e) { // TODO: handle exception } } this.

Spring入门第二十六课

Spring中的事务管理 事务简介 事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性. 事务就是一系列的动作,他们被当做一个单独的工作单元,这些动作要么全部完成,要么全部不起作用. 事务的四个关键属性(ACID) -原子性(atomicity):事务是一个原子操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用. -一致性(consistency):一旦所有事务动作完成,事务就被提交,数据和资源就处于一种满足业务规则的一致性状态中. -隔离性(is

JAVA学习第二十六课(多线程(五))- 多线程间的通信问题

一.线程间的通信 实例代码: 需求是:输入一个姓名和性别后,就输出一个姓名和性别 class Resource { String name; String sex ; } class Input implements Runnable { Resource r; Input(Resource r) { this.r = r; } public void run() { int x = 0; while(true) { synchronized (r) { if(x==0) { r.name =

linux学习笔记-第二十六课-Samba与squid

一.Samba Samba是SMB的一种实现方法,主要用来实现Linux系统的文件和打印服务.Linux用户通过配置使用Samba服务器可以实现与Windows 用户的资源共享.守护进程smbd和nmbd是Samba的核心,在全部时间内运行.nmbd程序使得通过企图计算机可以浏览Linux服务器. 1.Samba的安装 我们只通过yum安装 [[email protected] ~]# yum install -y samba 2.Samba配置 [[email protected] ~]# v

OpenGL教程翻译 第十六课 基本的纹理贴图

OpenGL教程翻译 第十六课 基本的纹理贴图 原文地址:http://ogldev.atspace.co.uk/(源码请从原文主页下载) Background 纹理贴图就是将任意一种类型的图片应用到3D模型的一个或多个面.图片(也可以称之为纹理)内容可以是任何东西,但是他们一般都是一些比如砖,叶子,地面等的图案,纹理贴图增加了场景的真实性.例如,对比下面的两幅图片. 为了进行纹理贴图,你需要进行三个步骤:将图片加载到OpenGl中,定义模型顶点的纹理坐标(以对其进行贴图),用纹理坐标对图片进行

2018-08-24 第三十六课

第三十六课 非关系统型数据库-mangodb 目录 二十四 mongodb介绍 二十五 mongodb安装 二十六 连接mongodb 二十七 mongodb用户管理 二十八 mongodb创建集合.数据管理 二十九 php的mongodb扩展 三十 php的mongo扩展 三十一 mongodb副本集介绍 三十二 mongodb副本集搭建 三十三 mongodb副本集测试 三十四 mongodb分片介绍 三十五 mongodb分片搭建 三十六 mongodb分片测试 三十七 mongodb备份