在我之前关于DOM的文章里,其实已经有提到过事件的概念。在讲事件之前,首先要知道的就是javascript是由事件驱动的。什么叫事件驱动呢?打个比方,比如我们在页面中点击一个按钮,才会跳出一个窗口或者一句话,又或者说,我们鼠标滚轮移动的时候,出现了一些动画之类的,这就是事件驱动。那什么是事件呢?事件本质上来说就是个触发和响应的过程。事件有三部分组成:事件源,事件名和事件处理函数。所谓事件源,就是触发事件的对象,事件名则是触发了什么事件,事件处理函数则是事件触发后索要执行的函数。比如下述代码

btn.onclick= function () {
alert("啦啦啦");
}

  在这里,事件源是btn,事件名称click,事件处理函数是onclick也可以说是后面那个function匿名函数,他们两个是等价的。在这里,我们用的是on加事件名的方式来注册事件的。除了这种方式之外,我们还有另外的注册时间的方式。就是addEventListener()这个方法有三个参数。第一个参数是事件名称,第二个参数是事件监听者,也就是事件处理函数。第三个参数则是一个boolean类型的值,不过他涉及到了事件捕获的概念,这里就先不多提了,一般情况下,我们默认这个值为false。具体使用方法可以看下述代码

btn.addEventListener("click", function () {
alert("啦啦啦");
}, false);

  这段代码跟上述代码执行的效果是一样的。可能会有人觉得,上面那种方法这么简单,为什么还要有下面的呢?一开始我也是这么觉得的,下面的繁琐又难记,而且觉得也没什么用,所一直用的是前一种方法。不过,都是在做小项目的时候,直到后来,如果给同一个事件源添加了两个相同事件的时候,前一个就会被覆盖掉。也就是说,用on的方式只能注册一个相同的事件。而addEventListener则可以给对象注册多个同名事件,不会造成覆盖。不过,这个方法也有他的局限性,那就是低版本浏览器不支持...在ie8及以下的版本中,他只支持attachEvent()这个方法,这个方法有两个参数,第一个参数是事件名称,第二个参数是事件监听者。这种方法在在新版本浏览器中已经不支持了,包括ie11。但是没办法ie8到现在为止还是有很大的市场份额,所以作为程序猿的我们只能是封装兼容性代码了。

 var  EventTools = {
addEventListener : function (element, type, listener) {
if(element.addEventListener) {
element.addEventListener(type, listener, false);
}else if(element.attachEvent) {
element.attachEvent("on" + type, listener);
}else{
element["on" + type] = listener;
}
}
};

  这里,我把兼容性代码放在了一个EventTools对象中,这样做的好处就是减少变量污染。之前的文章中我有提到过变量污染,不过没有细讲。所谓变量污染,对于平时练习的小项目中,这个问题是很少的,比如我在全局作用域中定义了两次相同名字的变量。这时候,后面定义的变量就会把前面的变量覆盖掉,如果是小项目中,这个问题出现只要改其中一个变量名就好了,可是在大的开发中,如果遇到这样的问题,要找出问题的错误之处是很麻烦的。所以说,我们要尽量去减少变量污染,比如上述方法,我们把有相似一类功能的方法封装在一个对象中,方法名是对象的一个属性,并不具有全局性,这时候就减少了变量的污染。

  好了,说完了变量的污染,就该来说说这段代码了,这段代码首先先判断了addEventListener这个方法浏览器是否支持。如果支持,则给元素添加事件监听,否则,判断是否支持attachEvent这个方法,如果支持,则用这种方法给元素添加事件监听。最后一种方法则是为了兼容一些很老版本的浏览器了,就是浏览器这两种方法都不支持的时候,就用on的方式给元素添加事件,不过现在在用的浏览其中基本上都是支持前两种方法的。所以最后一种情况不用怎么考虑。

  说完了两种给元素添加事件的方法,就该来说说事件的参数了,在事件触发的时候,系统会自动给事件处理函数传入一个参数,这个我们可以通过这个参数来获取一些事件相关的数据。如果我们不需要这些数据的话,这个参数可以省略。不过现在我们考虑的是有参的情况。

document.onmousemove = function (e) {
console.log(e.clientX + "===" + e.clientY);
}

  上述代码中,我们给document注册了一个onmousemove的事件,当鼠标在页面中移动的时候,打印出当前鼠标距离document左侧和顶部的距离。随着鼠标在页面中不断地移动,我们可以在控制台中看到他不断地在输出鼠标距离浏览器顶部和底部的距离。这里的事件参数就是e,这个参数也可以用其他名字,不过通常情况下,我们都会使用e这个参数。不过这种传参的方式也有他的局限性,就是ie8及以下的版本不支持。不过他的兼容性代码比较简单,我们只需要使用短路运算就能搞定了

e = e || window.event;

  解决了兼容性问题,我们就可以用这种方法做很多是事了,比如我们鼠标移动时,把他的e.clientX值和e.clientY值赋给一图片,这样,我们就能实现图片跟随鼠标移动的效果了,比如下述代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title> <style>
#i1 {
position: absolute;
}
</style>
<script>
window.onload = function () {
var i1 = document.getElementById("i1"); document.onmousemove = function (e) {
i1.style.left = e.clientX - 20 + "px";
i1.style.top = e.clientY -20 + "px";
}
}
box.onclick= function () {
alert("啦啦啦");
}
</script>
</head>
<body>
<img id="i1" src="data:images/tianshi.gif" alt="">
</body>
</html>

  在这个页面中,我们就能实现图片跟随鼠标移动,不过他还是会有一个小问题,就是如果我们的页面出现了滚动条的话,图片跟鼠标就会错位。这时候,我们就要用到了另一种方法就是,就是用pageX和pageY来代替e.clientX和e.clientY。pageX和pageY获取的是相对于浏览器的距离。就不会出现之前的情况了,不过,好的东西总是有他的局限性...ie8还是不支持这个属性。这时候,我们只能再次封装兼容性代码了。

function getPage(e) {
return {
pageX: e.pageX || (e.clientX + scroll().scrollLeft),
pageY: e.pageY || (e.clientY + scroll().scrollTop)
}
}
function scroll() {
return {
scrollLeft:document.body.scrollLeft || document.documentElement.scrollLeft,
scrollTop:document.body.scrollTop || document.documentElement.scrollTop
};
}

  上述代码中,我封装了两个兼容性代码,我们一个个来分析,首先先判断浏览器是否支持pageX和pageY这两个方法,如果支持,则返回e.pageX或e.pageY否则就使用e.clientX和e.clientY加上被卷去的距离。这时候,又涉及到了另一个兼容性问题,就是相卷的距离的判断,有些浏览器是根据body来判断的,而有些浏览器则是 根据html来判断的。这时候,我们就又要来封装兼容性代码了,如果是根据body来判断,就返回相对于body被卷去的距离,如果是相对于html来判断,那么前一个值是undefined这时候就返回后面的值。这样,浏览器的兼容问题就解决了。

  上面说了很多关于clientX和clientY,其实,跟他一个系列的还有两个属性,就是clientWidth和clientHeight。他获取的也是对象内容加padding的值,如果有滚动条的话,这个值也包括滚动条。我们可以用它来获取页面可视区域的大小,不过我们用的多是innerWidth和innerHeight的方式。只不过,他们也是有兼容性问题的。所以,我们只能再封装兼容性代码。

function client() {
return {
clientWidth: window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth || 0,
clientHeight : window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight || 0
};
}

  如果浏览器支持用inner的方式获取宽高,就是用inner的方式,否则就是后面两种,后面两种,分别判断的时候如果浏览器是根据body来获取宽度的,或者是html来获取宽度的。如果三种都不支持的话,则返回0。

  到这里,BOM中的三大家族已经都说完了。分别是offset系列,scroll系列和client系列。可能有些人有些搞混,不过下面三张图片可以帮我们更容易的区别这三者。

  要注意区分的是,client和offset系列的话是相对于offsetParent而言的,也就是往上数第一个有定位的父元素。而client相对于offsetParent的值用的是X和Y。

BOM基础(三)的更多相关文章

  1. BOM基础(四)

    最近写的文章感觉内容不像之前那么充实,内容可能也有点杂.对于DOM,和BOM来说,要理解是不难的,难的是做的时候.要自己想的到,而且,对于目前阶段来说,BOM还存在着很大的兼容性问题,最主要就是要兼容 ...

  2. BOM基础 计时器 定时器 DOM 基础

    -------------------------------------------滴水穿石,我心永恒. day48 CSSJS 1 ECMA script 2 BOM browser object ...

  3. Python全栈开发【基础三】

    Python全栈开发[基础三]  本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...

  4. Bootstrap <基础三十二>模态框(Modal)插件

    模态框(Modal)是覆盖在父窗体上的子窗体.通常,目的是显示来自一个单独的源的内容,可以在不离开父窗体的情况下有一些互动.子窗体可提供信息.交互等. 如果您想要单独引用该插件的功能,那么您需要引用  ...

  5. Bootstrap <基础三十一>插件概览

    在前面布局组件中所讨论到的组件仅仅是个开始.Bootstrap 自带 12 种 jQuery 插件,扩展了功能,可以给站点添加更多的互动.即使不是一名高级的 JavaScript 开发人员,也可以着手 ...

  6. Bootstrap <基础三十>Well

    Well 是一种会引起内容凹陷显示或插图效果的容器 <div>.为了创建 Well,只需要简单地把内容放在带有 class .well 的 <div> 中即可.下面的实例演示了 ...

  7. Bootstrap<基础三> 排版

    Bootstrap 使用 Helvetica Neue. Helvetica. Arial 和 sans-serif 作为其默认的字体栈. 使用 Bootstrap 的排版特性,您可以创建标题.段落. ...

  8. jdbc基础 (三) 大文本、二进制数据处理

    LOB (Large Objects)   分为:CLOB和BLOB,即大文本和大二进制数据 CLOB:用于存储大文本 BLOB:用于存储二进制数据,例如图像.声音.二进制文件 在mysql中,只有B ...

  9. Ruby语法基础(三)

    Ruby语法基础(三) ​ 在前面快速入之后,这次加深对基本概念的理解. 字符串 ​ Ruby字符串可以分为单引号字符串和双引号字符串,单引号字符串效率更高,但双引号的支持转义和运行 puts '单引 ...

  10. C#_02.13_基础三_.NET类基础

    C#_02.13_基础三_.NET类基础 一.类概述: 类是一个能存储数据和功能并执行代码的数据结构,包含数据成员和函数成员.(有什么和能够干什么) 运行中的程序是一组相互作用的对象的集合. 二.为类 ...

随机推荐

  1. Java 八大类型、String和 StringBuffer

    1. 八大类型 类型 封装类 占字节 int;       Integer;   4 short;         Short;            2 byte;          Byte;   ...

  2. Intent的属性及Intent-filter配置——实例Action、Data属性启动系统Activity

    一旦为Intent同时指定了Action.Data属性,那么Android将可根据指定的数据类型来启动特定的应用程序,并对指定数据类型执行相应的操作. 下面是几个Action属性.Data属性的组合. ...

  3. U盘安装系统

    http://www.ushendu.com/usdpzxt/1566.html http://www.ushendu.com/plus/view.php?aid=1571 http://www.ud ...

  4. Ajax.BeginForm()知多少

    在ASP.NET MVC中,Ajax.BeginForm扮演着异步提交的重要角色.其中就有五个重载方法,但是在实际应用中,你未必使用的得心应手,今天我们就从主要的参数来一探究竟. 一.actionNa ...

  5. Socket-IOS

    Socke Socket又称"套接字” 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. 应用程序通常通过"套接字"向网络发出请 ...

  6. Win下 MySQL数据库安装与配置详解

    第一步 从官网下载安装包 (本次只写安装版的32位的mysql) 1. https://www.mysql.com/downloads/ 下载的官网地址 一直滑到最下面 然后点第一个 然后选第一个 这 ...

  7. How To Ask Questions The Smart Way 转

    先查后问多思考莫做伸手党. 原文链接 译文链接

  8. 笑谈ArcToolbox (1) ArcToolbox 的发展方向

    笑谈ArcToolbox (1) ArcToolbox 的发展方向 by 李远祥 ArcGIS工具箱ArcToolbox具有非常多的工具,相信用过的人都非常惊叹,其功能完备并且强大,种类繁多,总能找到 ...

  9. linux 进程间通信 之fifo

    上一篇博客已经介绍了一种进程间通信的方式,但是那只是针对于有血缘关系的进程,即父子进程间的通信,那对于没有血缘关系的进程,那要怎么通信呢?  这就要创建一个有名管道,来解决无血缘关系的进程通信, fi ...

  10. iOS Plist 文件的 增 删 改

    一:Plist 文件的创建 Plist 文件作为我们IOS开发的一种数据存储文件,还是经常会用到的.在看<X-code江湖录>的时候,看到了这一点.自己就写了一下,把写的东西分享出来!先看 ...