事件
事件:就是文档或浏览器窗口中发生的一些特定的交互瞬间。对于 Web 应用来说,有下面这些代表性的事件:点击某个元素、将鼠标移动至某个元素上方、关闭弹窗等等。
JavaScript 是以事件驱动为核心的一门语言。JavaScript 与 HTML 之间的交互是通过事件实现的。
事件的三要素
事件的三要素:事件源、事件、事件驱动程序。
比如,我用手去按开关,灯亮了。这件事情里,事件源是:手。事件是:按开关。事件驱动程序是:灯开了或者关了。
再比如,网页上弹出一个广告,我点击右上角的X,广告就关闭了。这件事情里,事件源是:X。事件是:onclick。事件驱动程序是:广告关闭了。
于是我们可以总结出:谁引发的后续事件,谁就是事件源。
总结如下:
- 事件源:引发后续事件的html标签。
- 事件:js已经定义好了(见下图)。
- 事件驱动程序:对样式和html的操作。也就是DOM。
也就是说,我们可以在时间对应的属性中写一些js代码,当事件被触发时,这些代码将会执行。
代码书写步骤如下:(重要)
- (1)获取事件源:document.getElementById(“box”); // 类似于Android里面的findViewById
- (2)绑定事件: 事件源box.事件onclick = function(){ 事件驱动程序 };
- (3)书写事件驱动程序:关于DOM的操作。
最简单的代码举例:(点击box1,然后弹框)
<body><div id="box1"></div><script type="text/javascript">// 1、获取事件源var div = document.getElementById("box1");// 2、绑定事件div.onclick = function () {// 3、书写事件驱动程序alert("我是弹出的内容");}</script></body>
常见的事件如下:

下面针对这事件的三要素,进行分别介绍。
1、获取事件源的方式(DOM节点的获取)
获取事件源的常见方式如下:
var div1 = document.getElementById("box1"); //方式一:通过id获取单个标签var arr1 = document.getElementsByTagName("div"); //方式二:通过 标签名 获得 标签数组,所以有svar arr2 = document.getElementsByClassName("hehe"); //方式三:通过 类名 获得 标签数组,所以有s
2、绑定事件的方式
方式一:直接绑定匿名函数
<div id="box1" ></div><script type="text/javascript">var div1 = document.getElementById("box1");//绑定事件的第一种方式div1.onclick = function () {alert("我是弹出的内容");}</script>
方式二:先单独定义函数,再绑定
<div id="box1" ></div><script type="text/javascript">var div1 = document.getElementById("box1");//绑定事件的第二种方式div1.onclick = fn; //注意,这里是fn,不是fn()。fn()指的是返回值。//单独定义函数function fn() {alert("我是弹出的内容");}</script>
注意上方代码的注释。绑定的时候,是写fn,不是写fn()。fn代表的是整个函数,而fn()代表的是返回值。
方式三:行内绑定
<!--行内绑定--><div id="box1" onclick="fn()"></div><script type="text/javascript">function fn() {alert("我是弹出的内容");}</script>
注意第一行代码,绑定时,是写的"fn()",不是写的"fn"。因为绑定的这段代码不是写在js代码里的,而是被识别成了字符串。
3、事件驱动程序
我们在上面是拿alert举例,不仅如此,我们还可以操作标签的属性和样式。举例如下:
点击鼠标时,原本粉色的div变大了,背景变红:
<style>#box1 {width: 100px;height: 100px;background-color: pink;cursor: pointer;}</style></head><body><div id="box1" ></div><script type="text/javascript">var div1 = document.getElementById("box1");//点击鼠标时,原本粉色的div变大了,背景变红了div1.onclick = function () {div1.style.width = "200px"; //属性值要写引号div1.style.height = "200px";div1.style.backgroundColor = "red"; //属性名是backgroundColor,不是background-color}</script>
上方代码的注意事项:
- 在js里写属性值时,要用引号
- 在js里写属性名时,是
backgroundColor,不是CSS里面的background-color。
实现效果如下:

onload事件
onload事件比较特殊,这里单独讲一下。
当页面加载(文本和图片)完毕的时候,触发onload事件。
举例:
<script type="text/javascript">window.onload = function () {console.log("smyhvae"); //等页面加载完毕时,打印字符串}</script>
有一点我们要知道:js的加载是和html同步加载的。因此,如果使用元素在定义元素之前,容易报错。这个时候,onload事件就能派上用场了,我们可以把使用元素的代码放在onload里,就能保证这段代码是最后执行。
建议是:整个页面上所有元素加载完毕再执行js内容。所以,window.onload可以预防使用标签在定义标签之前。
备注:关于 onLoad事件,在下一篇文章《DOM简介和DOM操作》中有更详细的讲解和示例。
事件举例:京东顶部广告栏

比如上面这张图,当鼠标点击右上角的X时,关掉整个广告栏,这就要用到事件。
代码实现如下:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>* {padding: 0;margin: 0;}.top-banner {background-color: pink;height: 80px;}.w {width: 1210px;margin: 10px auto;position: relative;}img {display: block;width: 1210px;height: 80px;background-color: blue;}a {position: absolute;top: 5px;right: 5px;color: #fff;background-color: #000;text-decoration: none;width: 20px;height: 20px;font: 700 14px/20px "simsum";text-align: center;}.hide {display: none!important;}</style></head><body><div class="top-banner" id="topBanner"><div class="w"><img src="" alt=""/><a href="#" id="closeBanner">×</a></div></div><script>//需求:点击案例,隐藏盒子。//思路:点击a链接,让top-banner这个盒子隐藏起来(加隐藏类名)。//1.获取事件源和相关元素var closeBanner = document.getElementById("closeBanner");var topBanner = document.getElementById("topBanner");//2.绑定事件closeBanner.onclick = function () {//3.书写事件驱动程序//类控制// topBanner.className += " hide"; //保留原类名,添加新类名topBanner.className = "hide";//替换旧类名(方式一)// topBanner.style.display = "none"; //方式二:与上一行代码的效果相同}</script></body></html>
注意最后一行代码,这种方式会替换旧类名,意思是,不管之前的类名叫什么,都会被修改。
事件举例:
要求实现效果:当鼠标悬停在img上时,更换为另外一张图片;鼠标离开时,还原为本来的图片。
代码实现:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><script>//window.onload页面加载完毕以后再执行此代码window.onload = function () {//需求:鼠标放到img上,更换为另一张图片,也就是修改路径(src的值)。//步骤://1.获取事件源//2.绑定事件//3.书写事件驱动程序//1.获取事件源var img = document.getElementById("box");//2.绑定事件(悬停事件:鼠标进入到事件源中,立即触发事件)img.onmouseover = function () {//3.书写事件驱动程序(修改src)img.src = "image/jd2.png";// this.src = "image/jd2.png";}//2.绑定事件(悬停事件:鼠标进入到事件源中,立即触发事件)img.onmouseout = function () {//3.书写事件驱动程序(修改src)img.src = "image/jd1.png";}}</script></head><body><img id="box" src="image/jd1.png" style="cursor: pointer;border: 1px solid #ccc;"/></html>
DOM简介和DOM操作
常见概念
JavaScript的组成
JavaScript基础分为三个部分:
- ECMAScript:JavaScript的语法标准。包括变量、表达式、运算符、函数、if语句、for语句等。
- DOM:文档对象模型(Document object Model),操作网页上的元素的API。比如让盒子移动、变色、轮播图等。
- BOM:浏览器对象模型(Browser Object Model),操作浏览器部分功能的API。比如让浏览器自动滚动。
节点
节点(Node):构成 HTML 网页的最基本单元。网页中的每一个部分都可以称为是一个节点,比如:html标签、属性、文本、注释、整个文档等都是一个节点。
虽然都是节点,但是实际上他们的具体类型是不同的。常见节点分为四类:
- 文档节点(文档):整个 HTML 文档。整个 HTML 文档就是一个文档节点。
- 元素节点(标签):HTML标签。
- 属性节点(属性):元素的属性。
- 文本节点(文本):HTML标签中的文本内容(包括标签之间的空格、换行)。
节点的类型不同,属性和方法也都不尽相同。所有的节点都是Object。
什么是DOM
DOM:Document Object Model,文档对象模型。DOM 为文档提供了结构化表示,并定义了如何通过脚本来访问文档结构。目的其实就是为了能让js操作html元素而制定的一个规范。
DOM就是由节点组成的。
解析过程:
HTML加载完毕,渲染引擎会在内存中把HTML文档,生成一个DOM树,getElementById是获取内中DOM上的元素节点。然后操作的时候修改的是该元素的属性。
DOM树:(一切都是节点)
DOM的数据结构如下:

上图可知,在HTML当中,一切都是节点(非常重要)。节点的分类,在上一段中,已经讲了。
整个html文档就是一个文档节点。所有的节点都是Object。
DOM可以做什么
- 找对象(元素节点)
- 设置元素的属性值
- 设置元素的样式
- 动态创建和删除元素
- 事件的触发响应:事件源、事件、事件的驱动程序
元素节点的获取
DOM节点的获取方式其实就是获取事件源的方式。关于事件,上一篇文章中已经讲到了。
想要操作元素节点,必须首先要找到该节点。有三种方式可以获取DOM节点:
var div1 = document.getElementById("box1"); //方式一:通过 id 获取 一个 元素节点(为什么是一个呢?因为 id 是唯一的)var arr1 = document.getElementsByTagName("div"); //方式二:通过 标签名 获取 元素节点数组,所以有svar arr2 = document.getElementsByClassName("hehe"); //方式三:通过 类名 获取 元素节点数组,所以有s
既然方式二、方式三获取的是标签数组,那么习惯性是先遍历之后再使用。
特殊情况:数组中的值只有1个。即便如此,这一个值也是包在数组里的。这个值的获取方式如下:
document.getElementsByTagName("div1")[0]; //取数组中的第一个元素document.getElementsByClassName("hehe")[0]; //取数组中的第一个元素
DOM访问关系的获取
DOM的节点并不是孤立的,因此可以通过DOM节点之间的相对关系对它们进行访问。如下:

节点的访问关系,是以属性的方式存在的。
JS中的父子兄访问关系:

这里我们要重点知道parentNode和children这两个属性的用法。下面分别介绍。
获取父节点
调用者就是节点。一个节点只有一个父节点,调用方式就是
节点.parentNode
获取兄弟节点
1、下一个节点 | 下一个元素节点:
Sibling的中文是兄弟。
(1)nextSibling:
- 火狐、谷歌、IE9+版本:都指的是下一个节点(包括标签、空文档和换行节点)。
- IE678版本:指下一个元素节点(标签)。
(2)nextElementSibling:
- 火狐、谷歌、IE9+版本:都指的是下一个元素节点(标签)。
总结:为了获取下一个元素节点,我们可以这样做:在IE678中用nextSibling,在火狐谷歌IE9+以后用nextElementSibling,于是,综合这两个属性,可以这样写:
下一个兄弟节点 = 节点.nextElementSibling || 节点.nextSibling
2、前一个节点 | 前一个元素节点:
previous的中文是:前一个。
(1)previousSibling:
- 火狐、谷歌、IE9+版本:都指的是前一个节点(包括标签、空文档和换行节点)。
- IE678版本:指前一个元素节点(标签)。
(2)previousElementSibling:
- 火狐、谷歌、IE9+版本:都指的是前一个元素节点(标签)。
总结:为了获取前一个元素节点,我们可以这样做:在IE678中用previousSibling,在火狐谷歌IE9+以后用previousElementSibling,于是,综合这两个属性,可以这样写:
前一个兄弟节点 = 节点.previousElementSibling || 节点.previousSibling
3、补充:获得任意一个兄弟节点:
节点自己.parentNode.children[index]; //随意得到兄弟节点
获取单个的子节点
1、第一个子节点 | 第一个子元素节点:
(1)firstChild:
- 火狐、谷歌、IE9+版本:都指的是第一个子节点(包括标签、空文档和换行节点)。
- IE678版本:指第一个子元素节点(标签)。
(2)firstElementChild:
- 火狐、谷歌、IE9+版本:都指的是第一个子元素节点(标签)。
总结:为了获取第一个子元素节点,我们可以这样做:在IE678中用firstChild,在火狐谷歌IE9+以后用firstElementChild,于是,综合这两个属性,可以这样写:
第一个子元素节点 = 节点.firstElementChild || 节点.firstChild
2、最后一个子节点 | 最后一个子元素节点:
(1)lastChild:
- 火狐、谷歌、IE9+版本:都指的是最后一个子节点(包括标签、空文档和换行节点)。
- IE678版本:指最后一个子元素节点(标签)。
(2)lastElementChild:
- 火狐、谷歌、IE9+版本:都指的是最后一个子元素节点(标签)。
总结:为了获取最后一个子元素节点,我们可以这样做:在IE678中用lastChild,在火狐谷歌IE9+以后用lastElementChild,于是,综合这两个属性,可以这样写:
最后一个子元素节点 = 节点.lastElementChild || 节点.lastChild
获取所有的子节点
(1)childNodes:标准属性。返回的是指定元素的子节点的集合(包括元素节点、所有属性、文本节点)。是W3C的亲儿子。
- 火狐 谷歌等高本版会把换行也看做是子节点。
用法:
子节点数组 = 父节点.childNodes; //获取所有节点。
(2)children:非标准属性。返回的是指定元素的子元素节点的集合。【重要】
- 它只返回HTML节点,甚至不返回文本节点。
- 在IE6/7/8中包含注释节点(在IE678中,注释节点不要写在里面)。
虽然不是标准的DOM属性,但它和innerHTML方法一样,得到了几乎所有浏览器的支持。
用法:(用的最多)
子节点数组 = 父节点.children; //获取所有节点。用的最多。
DOM节点的操作(重要)
上一段的内容:节点的访问关系都是属性。
本段的内容:节点的操作都是函数(方法)。
创建节点
格式如下:
新的标签(元素节点) = document.createElement("标签名");
比如,如果我们想创建一个li标签,或者是创建一个不存在的adbc标签,可以这样做:
<script type="text/javascript">var a1 = document.createElement("li"); //创建一个li标签var a2 = document.createElement("adbc"); //创建一个不存在的标签console.log(a1);console.log(a2);console.log(typeof a1);console.log(typeof a2);</script>
打印结果:

插入节点
插入节点有两种方式,它们的含义是不同的。
方式1:
父节点.appendChild(新的子节点);
解释:父节点的最后插入一个新的子节点。
方式2:
父节点.insertBefore(新的子节点,作为参考的子节点)
解释:
- 在参考节点前插入一个新的节点。
- 如果参考节点为null,那么他将在父节点里面的最后插入一个子节点。

我们可以看到,li标签确实被插入到了box1标签的里面,和box2并列了。
方式2的举例:

我们可以看到,b1标签被插入到了box1标签的里面,和a1标签并列,在a1标签的前面。
特别强调:
关于方式1的appendChild方法,这里要强调一下。比如,现在有下面这样一个div结构:
<div class="box11"><div class="box12">生命壹号</div></div><div class="box21"><div class="box22">永不止步</div></div>
上方结构中,子盒子box12是在父亲box11里的,子盒子box22是在父亲box21里面的。现在,如果我调用方法box11.appendChild(box22),最后产生的结果是:box22会跑到box11中(也就是说,box22不在box21里面了)。这是一个很神奇的事情:

删除节点
格式如下:
父节点.removeChild(子节点);
解释:用父节点删除子节点。必须要指定是删除哪个子节点。
如果我想删除自己这个节点,可以这么做:
node1.parentNode.removeChild(node1);
复制节点(克隆节点)
格式如下:
要复制的节点.cloneNode(); //括号里不带参数和带参数false,效果是一样的。要复制的节点.cloneNode(true);
括号里带不带参数,效果是不同的。解释如下:
- 不带参数/带参数false:只复制节点本身,不复制子节点。
- 带参数true:既复制节点本身,也复制其所有的子节点。
设置节点的属性
我们可以获取节点的属性值、设置节点的属性值、删除节点的属性。
我们就统一拿下面这个标签来举例:
<img src="images/1.jpg" class="image-box" title="美女图片" alt="地铁一瞥" id="a1">
下面分别介绍。
1、获取节点的属性值
方式1:
元素节点.属性名;元素节点[属性名];
举例:(获取节点的属性值)
<body><img src="images/1.jpg" class="image-box" title="美女图片" alt="地铁一瞥" id="a1"><script type="text/javascript">var myNode = document.getElementsByTagName("img")[0];console.log(myNode.src);console.log(myNode.className); //注意,是className,不是classconsole.log(myNode.title);console.log("------------");console.log(myNode["src"]);console.log(myNode["className"]); //注意,是className,不是classconsole.log(myNode["title"]);</script></body>
上方代码中的img标签,有各种属性,我们可以逐一获取,打印结果如下:

方式2:
元素节点.getAttribute("属性名称");
举例:
console.log(myNode.getAttribute("src"));console.log(myNode.getAttribute("class")); //注意是class,不是classNameconsole.log(myNode.getAttribute("title"));
打印结果:

方式1和方式2的区别在于:前者是直接操作标签,后者是把标签作为DOM节点。推荐方式2。
2、设置节点的属性值
方式1举例:(设置节点的属性值)
myNode.src = "images/2.jpg" //修改src的属性值myNode.className = "image2-box"; //修改class的name
方式2:
元素节点.setAttribute("属性名", "新的属性值");
方式2举例:(设置节点的属性值)
myNode.setAttribute("src","images/3.jpg");myNode.setAttribute("class","image3-box");myNode.setAttribute("id","你好");
3、删除节点的属性
格式:
元素节点.removeAttribute(属性名);
举例:(删除节点的属性)
myNode.removeAttribute("class");myNode.removeAttribute("id");
总结
获取节点的属性值和设置节点的属性值,都有两种方式。
如果是节点的“原始属性”(比如 普通标签的class/className属性、普通标签的style属性、普通标签的 title属性、img 标签的src属性、超链接的href属性等),方式1和方式2是等价的,可以混用。怎么理解混用呢?比如说:用 div.title = '我是标题'设置属性,用 div.getAttribute('title')获取属性,就是混用。
但如果是节点的“非原始属性”,比如:
div.aaa = 'qianguyihao';div.setAttribute('bbb', 'qianguyihao');
上面的这个“非原始属性”,在使用这两种方式时,是有区别的。区别如下:
- 方式1 的
元素节点.属性和元素节点[属性]:绑定的属性值不会出现在标签上。 - 方式2 的
get/set/removeAttribut:绑定的属性值会出现在标签上。 - 这两种方式不能交换使用,get值和set值必须使用同一种方法。
举例:
<body><div id="box" title="主体" class="asdfasdfadsfd">我爱你中国</div><script>var div = document.getElementById("box");//采用方式一进行setdiv.aaaa = "1111";console.log(div.aaaa); //打印结果:1111。可以打印出来,但是不会出现在标签上//采用方式二进行setdiv.setAttribute("bbbb","2222"); //bbbb作为新增的属性,会出现在标签上console.log(div.getAttribute("aaaa")); //打印结果:null。因为方式一的set,无法采用方式二进行get。console.log(div.bbbb); //打印结果:undefined。因为方式二的set,无法采用方式一进行get。</script></body>
DOM对象的属性-补充
innerHTML和innerText的区别
- value:标签的value属性。
- innerHTML:双闭合标签里面的内容(包含标签)。
- innerText:双闭合标签里面的内容(不包含标签)。(老版本的火狐用textContent)
获取内容举例:
如果我们想获取innerHTML和innerText里的内容,看看会如何:(innerHTML会获取到标签本身,而innerText则不会)

修改内容举例:(innerHTML会修改标签本身,而innerText则不会)

nodeType属性
这里讲一下nodeType属性。
- nodeType == 1 表示的是元素节点(标签) 。记住:在这里,元素就是标签。
- nodeType == 2 表示是属性节点。
- nodeType == 3 是文本节点。
nodeType、nodeName、nodeValue
我们那下面这个标签来举例:
<div id="box" value="111">生命壹号</div>
上面这个标签就包含了三种节点:
- 元素节点(标签)
- 属性节点
- 文本节点
获取这三个节点的方式如下:
var element = document.getElementById("box1"); //获取元素节点(标签)var attribute = element.getAttributeNode("id"); //获取box1的属性节点var txt = element.firstChild; //获取box1的文本节点var value = element.getAttribute("id"); //获取id的属性值console.log(element);console.log("--------------");console.log(attribute);console.log("--------------");console.log(txt);console.log("--------------");console.log(value);
打印结果如下:

既然这三个都是节点,如果我想获取它们的nodeType、nodeName、nodeValue,代码如下:
var element = document.getElementById("box1"); //获取元素节点(标签)var attribute = element.getAttributeNode("id"); //获取box1的属性节点var txt = element.firstChild; //获取box1的文本节点//获取nodeTypeconsole.log(element.nodeType); //1console.log(attribute.nodeType); //2console.log(txt.nodeType); //3console.log("--------------");//获取nodeNameconsole.log(element.nodeName); //DIVconsole.log(attribute.nodeName); //idconsole.log(txt.nodeName); //#textconsole.log("--------------");//获取nodeValueconsole.log(element.nodeValue); //nullconsole.log(attribute.nodeValue); //box1console.log(txt.nodeValue); //生命壹号
打印结果如下:

文档的加载
浏览器在加载一个页面时,是按照自上向下的顺序加载的,读取到一行就运行一行。如果将script标签写到页面的上边,在代码执行时,页面还没有加载,页面没有加载DOM对象也没有加载,会导致无法获取到DOM对象。
onload 事件:
onload 事件会在整个页面加载完成之后才触发。为 window 绑定一个onload事件,该事件对应的响应函数将会在页面加载完成之后执行,这样可以确保我们的代码执行时所有的DOM对象已经加载完毕了。
代码举例:
<!DOCTYPE html><html><head><meta charset="UTF-8" /><title></title><script type="text/javascript">// 【方式一:先加载,后执行】这段 js 代码是写在 <head> 标签里的,所以建议放在 window.onload 里面。window.onload = function() {// 获取id为btn的按钮var btn = document.getElementById("btn");// 为按钮绑定点击事件btn.onclick = function() {alert("hello");};};</script></head><body><button id="btn">点我一下</button><script type="text/javascript">// 【方式二:后加载,后执行】这段 js 代码是写在 <body> 标签里的,代码的位置是处在页面的下方。这么做,也可以确保:在页面加载完毕后,再执行 js 代码。// 获取id为btn的按钮var btn = document.getElementById("btn");// 为按钮绑定点击事件btn.onclick = function() {alert("hello");};</script></body></html>
通过style对象获取和设置行内样式
style属性的获取和修改
在DOM当中,如果想设置样式,有两种形式:
- className(针对内嵌样式)
- style(针对行内样式)
这篇文章,我们就来讲一下style。
需要注意的是:style是一个对象,只能获取行内样式,不能获取内嵌的样式和外链的样式。例如:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>div {border: 6px solid red;}</style></head><body><div class="box1" style="width: 200px;height: 100px;background-color: pink;"></div><script>var box1 = document.getElementsByTagName("div")[0];console.log(box1.style.backgroundColor);console.log(box1.style.border); //没有打印结果,因为这个属性不是行内样式console.log(typeof box1.style); //因为是对象,所以打印结果是Objectconsole.log(box1.style); //打印结果是对象</script></body></html>
打印结果:

上图显示,因为border属性不是行内样式,所以无法通过style对象获取。
通过 js 读取元素的样式
语法:(方式一)
元素.style.样式名
备注:我们通过style属性读取的样式都是行内样式。
语法:(方式二)
元素.style["属性"]; //格式box.style["width"]; //举例
方式二最大的优点是:可以给属性传递参数。
通过 js 设置元素的样式
语法:
元素.style.样式名 = 样式值;
举例:
box1.style.width = "300px";box1.style.backgroundColor = "red"; // 驼峰命名法
备注:我们通过style属性设置的样式都是行内样式,而行内样式有较高的优先级。但是如果在样式中的其他地方写了!important,则此时!important会有更高的优先级。
style属性的注意事项
style属性需要注意以下几点:
(1)样式少的时候使用。
(2)style是对象。我们在上方已经打印出来,typeof的结果是Object。
(3)值是字符串,没有设置值是“”。
(4)命名规则,驼峰命名。
(5)只能获取行内样式,和内嵌和外链无关。
(6)box.style.cssText = “字符串形式的样式”。
cssText这个属性,其实就是把行内样式里面的值当做字符串来对待。在上方代码的基础之上,举例:
<script>var box1 = document.getElementsByTagName("div")[0];//通过cssText一次性设置行内样式box1.style.cssText = "width: 300px;height: 300px;background-color: green;";console.log(box1.style.cssText); //这一行更加可以理解,style是对象</script>
打印结果:

style的常用属性
style的常用属性包括:
- backgroundColor
- backgroundImage
- color
- width
- height
- border
- opacity 设置透明度 (IE8以前是filter: alpha(opacity=xx))
注意DOM对象style的属性和标签中style内的值不一样,因为在JS中,-不能作为标识符。比如:
- DOM中:backgroundColor
- CSS中:background-color
style属性的举例
我们针对上面列举的几个style的样式,来举几个例子:
- 举例1、改变div的大小和透明度
- 举例2、当前输入的文本框高亮显示
- 举例3、高级隔行变色、高亮显示
下面来逐一实现。
举例1:改变div的大小和透明度
代码举例:
<body><div style="width: 100px;height: 100px;background-color: pink;"></div><script>var div = document.getElementsByTagName("div")[0];div.onmouseover = function () {div.style.width = "200px";div.style.height = "200px";div.style.backgroundColor = "black";div.style.opacity = "0.2"; //设置背景色的透明度。单位是0.1div.style.filter = "alpha(opacity=20)"; //上一行代码的兼容性写法。注意单位是百进制}</script></body>
举例2:当前输入的文本框高亮显示
代码实现:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>input {display: block;}</style></head><body><ul><input type="text"/><input type="text"/><input type="text"/><input type="text"/><input type="text"/></ul><script>//需求:让所有的input标签获取焦点后高亮显示//1.获取事件源var inpArr = document.getElementsByTagName("input");//2.绑定事件//3.书写事件驱动程序for (var i = 0; i < inpArr.length; i++) {//获取焦点后,所有的input标签被绑定onfocus事件inpArr[i].onfocus = function () {this.style.border = "2px solid red";this.style.backgroundColor = "#ccc";}//绑定onblur事件,取消样式inpArr[i].onblur = function () {this.style.border = "";this.style.backgroundColor = "";}}</script></body></html>
举例3:高级隔行变色、高亮显示
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>* {padding: 0;margin: 0;text-align: center;}.wrap {width: 500px;margin: 100px auto 0;}table {border-collapse: collapse;border-spacing: 0;border: 1px solid #c0c0c0;width: 500px;}th,td {border: 1px solid #d0d0d0;color: #404060;padding: 10px;}th {background-color: #09c;font: bold 16px "微软雅黑";color: #fff;}td {font: 14px "微软雅黑";}tbody tr {background-color: #f0f0f0;cursor: pointer;}.current {background-color: red !important;}</style></head><body><div class="wrap"><table><thead><tr><th>序号</th><th>姓名</th><th>课程</th><th>成绩</th></tr></thead><tbody id="target"><tr><td>1</td><td>生命壹号</td><td>语文</td><td>100</td></tr><tr><td>2</td><td>生命贰号</td><td>日语</td><td>99</td></tr><tr><td>3</td><td>生命叁号</td><td>营销学</td><td>98</td></tr><tr><td>4</td><td>生命伍号</td><td>数学</td><td>90</td></tr><tr><td>5</td><td>许嵩</td><td>英语</td><td>96</td></tr><tr><td>6</td><td>vae</td><td>体育</td><td>90</td></tr></tbody></table></div><script>//需求:让tr各行变色,鼠标放入tr中,高亮显示。//1.隔行变色。var tbody = document.getElementById("target");var trArr = tbody.children;//循环判断并各行赋值属性(背景色)for (var i = 0; i < trArr.length; i++) {if (i % 2 !== 0) {trArr[i].style.backgroundColor = "#a3a3a3";} else {trArr[i].style.backgroundColor = "#ccc";}//鼠标进入高亮显示//难点:鼠标移开的时候要回复原始颜色。//计数器(进入tr之后,立刻记录颜色,然后移开的时候使用记录好的颜色)var myColor = "";trArr[i].onmouseover = function () {//赋值颜色之前,先记录颜色myColor = this.style.backgroundColor;this.style.backgroundColor = "#fff";}trArr[i].onmouseout = function () {this.style.backgroundColor = myColor;}}</script></body></html>
实现的效果如下:

代码解释:
上方代码中,我们用到了计数器myColor来记录每一行最原始的颜色(赋值白色之前)。如果不用计数器,可能很多人以为代码是写的:(错误的代码)
<script>//需求:让tr各行变色,鼠标放入tr中,高亮显示。//1.隔行变色。var tbody = document.getElementById("target");var trArr = tbody.children;//循环判断并各行赋值属性(背景色)for (var i = 0; i < trArr.length; i++) {if (i % 2 !== 0) {trArr[i].style.backgroundColor = "#a3a3a3";} else {trArr[i].style.backgroundColor = "#ccc";}//鼠标进入高亮显示//难点:鼠标移开的时候要回复原始颜色。//计数器(进入tr之后,立刻记录颜色,然后移开的时候使用记录好的颜色)trArr[i].onmouseover = function () {this.style.backgroundColor = "#fff";}trArr[i].onmouseout = function () {this.style.backgroundColor = "#a3a3a3";}}</script>
这种错误的代码,实现的效果却是:(未达到效果)

通过 js 获取元素当前显示的样式
我们在上面的内容中,通过元素.style.className的方式只能获取行内样式。但是,有些元素,也写了内嵌样式或外链样式。
既然样式有这么多种,那么,如何获取元素当前显示的样式(包括行内样式、内嵌样式、外链样式)呢?我们接下来看一看。
获取元素当前正在显示的样式
(1)w3c的做法:
window.getComputedStyle("要获取样式的元素", "伪元素");
两个参数都是必须要有的。参数二中,如果没有伪元素就用 null 代替(一般都传null)。
(2)IE和opera的做法:
obj.currentStyle;
注意:
- 如果当前元素没有设置该样式,则获取它的默认值。
- 该方法会返回一个对象,对象中封装了当前元素对应的样式,可以通过
对象.样式名来读取具体的某一个样式。 - 通过currentStyle和getComputedStyle()读取到的样式都是只读的,不能修改,如果要修改必须通过style属性。
综合上面两种写法,就有了一种兼容性的写法,同时将其封装。代码举例如下:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>div {background-color: pink;/*border: 1px solid #000;*/padding: 10px;}</style></head><body><div style="width: 100px;height: 100px;"></div><script>var div1 = document.getElementsByTagName("div")[0];console.log(getStyle(div1, "width"));console.log(getStyle(div1, "padding"));console.log(getStyle(div1, "background-color"));/** 兼容方法,获取元素当前正在显示的样式。* 参数:* obj 要获取样式的元素*. name 要获取的样式名*/function getStyle(ele, attr) {if (window.getComputedStyle) {return window.getComputedStyle(ele, null)[attr];}return ele.currentStyle[attr];}</script></body></html>
打印结果:

offset相关属性和匀速动画
前言
JS动画的主要内容如下:
1、三大家族和一个事件对象:
- 三大家族:offset/scroll/client。也叫三大系列。
- 事件对象/event(事件被触动时,鼠标和键盘的状态)(通过属性控制)。
2、动画(闪现/匀速/缓动)
3、冒泡/兼容/封装
offset 家族的组成
我们知道,JS动画的三大家族包括:offset/scroll/client。今天来讲一下offset,以及与其相关的匀速动画。
offset的中文是:偏移,补偿,位移。
js中有一套方便的获取元素尺寸的办法就是offset家族。offset家族包括:
- offsetWidth
- offsetHight
- offsetLeft
- offsetTop
- offsetParent
下面分别介绍。
1、offsetWidth 和 offsetHight
offsetWidth 和 offsetHight:获取元素的宽高 + padding + border,不包括margin。如下:
- offsetWidth = width + padding + border
- offsetHeight = Height + padding + border
这两个属性,他们绑定在了所有的节点元素上。获取元素之后,只要调用这两个属性,我们就能够获取元素节点的宽和高。
举例如下:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;padding: 10px;border: 10px solid #000;margin: 100px;background-color: pink;}</style></head><body><div class="box"></div><script>var div1 = document.getElementsByTagName("div")[0];console.log(div1.offsetHeight); //打印结果:140(100+20+20)console.log(typeof div1.offsetHeight); //打印结果:number</script></body></html>
2、offsetParent
offsetParent:获取当前元素的定位父元素。
- 如果当前元素的父元素,有CSS定位(position为absolute、relative、fixed),那么
offsetParent获取的是最近的那个父元素。 - 如果当前元素的父元素,没有CSS定位(position为absolute、relative、fixed),那么
offsetParent获取的是body。
举例:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title></head><body><div class="box1" style="position: absolute;"><div class="box2" style="position: fixed;"><div class="box3"></div></div></div><script>var box3 = document.getElementsByClassName("box3")[0];console.log(box3.offsetParent);</script></body></html>
打印结果:

3、offsetLeft 和 offsetTop
offsetLeft:当前元素相对于其定位父元素的水平偏移量。
offsetTop:当前元素相对于其定位父元素的垂直偏移量。
备注:从父亲的 padding 开始算起,父亲的 border 不算在内。
举例:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>.box1 {width: 300px;height: 300px;padding: 100px;margin: 100px;position: relative;border: 100px solid #000;background-color: pink;}.box2 {width: 100px;height: 100px;background-color: red;/*position: absolute;*//*left: 10px;*//*top: 10px;*/}</style></head><body><div class="box1"><div class="box2" style="left: 10px"></div></div><script>var box2 = document.getElementsByClassName("box2")[0];//offsetTop和offsetLeftconsole.log(box2.offsetLeft); //100console.log(box2.style.left); //10px</script></body></html>
在父盒子有定位的情况下,offsetLeft == style.left(去掉px之后)。注意,后者只识别行内样式。但区别不仅仅于此,下面会讲。
offsetLeft 和 style.left 区别
(1)最大区别在于:
offsetLeft 可以返回无定位父元素的偏移量。如果父元素中都没有定位,则body为准。
style.left 只能获取行内样式,如果父元素中都没有设置定位,则返回””(意思是,返回空字符串);
(2)offsetTop 返回的是数字,而 style.top 返回的是字符串,而且还带有单位:px。
比如:
div.offsetLeft = 100;div.style.left = "100px";
(3)offsetLeft 和 offsetTop 只读,而 style.left 和 style.top 可读写(只读是获取值,可写是修改值)
总结:我们一般的做法是:用offsetLeft 和 offsetTop 获取值,用style.left 和 style.top 赋值(比较方便)。理由如下:
- style.left:只能获取行内式,获取的值可能为空,容易出现NaN。
- offsetLeft:获取值特别方便,而且是现成的number,方便计算。它是只读的,不能赋值。
动画的种类
- 闪现(基本不用)
- 匀速(本文重点)
- 缓动(后续重点)
简单举例如下:(每间隔500ms,向右移动盒子100px)
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;background-color: pink;position: absolute;}</style></head><body><button>动画</button><div class="box" style="left: 0px"></div><script>var btn = document.getElementsByTagName("button")[0];var div = document.getElementsByTagName("div")[0];//1、闪动// btn.onclick = function () {// div.style.left = "500px";// }//2、匀速运动btn.onclick = function () {//定时器,每隔一定的时间向右走一些setInterval(function () {console.log(parseInt(div.style.left));//动画原理: 盒子未来的位置 = 盒子现在的位置 + 步长;//方法1:用offsetLeft获取值,用style.left赋值。div.style.left = div.offsetLeft + 100 + 'px';// 方法2:必须一开始就在DOM节点上添加 style="left: 0px;"属性,才能用方法2。否则, div.style.left 的值为 NaN// div.style.left = parseInt(div.style.left)+100+"px"; //方法2:}, 500);};</script></body></html>
效果如下:

匀速动画的封装:每间隔30ms,移动盒子10px【重要】
代码如下:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>.box1 {margin: 0;padding: 5px;height: 300px;background-color: #ddd;position: relative;}button {margin: 5px;}.box2 {width: 100px;height: 100px;background-color: red;position: absolute;left: 195px;top: 40px;}.box3 {width: 100px;height: 100px;background-color: yellow;position: absolute;left: 0;top: 150px;}</style></head><body><div class="box1"><button>运动到 left = 200px</button><button>运动到 left = 400px</button><div class="box2"></div><div class="box3"></div></div><script>var btnArr = document.getElementsByTagName("button");var box2 = document.getElementsByClassName("box2")[0];var box3 = document.getElementsByClassName("box3")[0];//绑定事件btnArr[0].onclick = function () {//如果有一天我们要传递另外一个盒子,那么我们的方法就不好用了//所以我们要增加第二个参数,被移动的盒子本身。animate(box2, 200);animate(box3, 200);}btnArr[1].onclick = function () {animate(box2, 400);animate(box3, 400);}//【重要】方法的封装:每间隔30ms,将盒子向右移动10pxfunction animate(ele, target) {//要用定时器,先清除定时器//一个盒子只能有一个定时器,这样的话,不会和其他盒子出现定时器冲突//我们可以把定时器本身,当成为盒子的一个属性clearInterval(ele.timer);//我们要求盒子既能向前又能向后,那么我们的步长就得有正有负//目标值如果大于当前值取正,目标值如果小于当前值取负var speed = target > ele.offsetLeft ? 10 : -10; //speed指的是步长ele.timer = setInterval(function () {//在执行之前就获取当前值和目标值之差var val = target - ele.offsetLeft;ele.style.left = ele.offsetLeft + speed + "px";//移动的过程中,如果目标值和当前值之差如果小于步长,那么就不能在前进了//因为步长有正有负,所有转换成绝对值来比较if (Math.abs(val) < Math.abs(speed)) {ele.style.left = target + "px";clearInterval(ele.timer);}}, 30)}</script></body></html>
实现的效果:

上方代码中的方法封装,可以作为一个模板步骤,要记住。其实,这个封装的方法,写成下面这样,会更严谨,更容易理解:(将if语句进行了改进)
//【重要】方法的封装:每间隔30ms,将盒子向右移动10pxfunction animate(ele, target) {//要用定时器,先清除定时器//一个盒子只能有一个定时器,这样的话,不会和其他盒子出现定时器冲突//我们可以把定时器本身,当成为盒子的一个属性clearInterval(ele.timer);//我们要求盒子既能向前又能向后,那么我们的步长就得有正有负//目标值如果大于当前值取正,目标值如果小于当前值取负var speed = target > ele.offsetLeft ? 10 : -10; //speed指的是步长ele.timer = setInterval(function () {//在执行之前就获取当前值和目标值之差var val = target - ele.offsetLeft;//移动的过程中,如果目标值和当前值之差如果小于步长,那么就不能在前进了//因为步长有正有负,所有转换成绝对值来比较if (Math.abs(val) < Math.abs(speed)) { //如果val小于步长,则直接到达目的地;否则,每次移动一个步长ele.style.left = target + "px";clearInterval(ele.timer);} else {ele.style.left = ele.offsetLeft + speed + "px";}}, 30)}
代码举例:轮播图的实现
完整版代码如下:(注释已经比较详细)
<!doctype html><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>无标题文档</title><style type="text/css">* {padding: 0;margin: 0;list-style: none;border: 0;}.all {width: 500px;height: 200px;padding: 7px;border: 1px solid #ccc;margin: 100px auto;position: relative;}.screen {width: 500px;height: 200px;overflow: hidden;position: relative;}.screen li {width: 500px;height: 200px;overflow: hidden;float: left;}.screen ul {position: absolute;left: 0;top: 0px;width: 3000px;}.all ol {position: absolute;right: 10px;bottom: 10px;line-height: 20px;text-align: center;}.all ol li {float: left;width: 20px;height: 20px;background: #fff;border: 1px solid #ccc;margin-left: 10px;cursor: pointer;}.all ol li.current {background: yellow;}#arr {display: none;}#arr span {width: 40px;height: 40px;position: absolute;left: 5px;top: 50%;margin-top: -20px;background: #000;cursor: pointer;line-height: 40px;text-align: center;font-weight: bold;font-family: '黑体';font-size: 30px;color: #fff;opacity: 0.3;border: 1px solid #fff;}#arr #right {right: 5px;left: auto;}</style><script>window.onload = function () {//需求:无缝滚动。//思路:赋值第一张图片放到ul的最后,然后当图片切换到第五张的时候// 直接切换第六章,再次从第一张切换到第二张的时候先瞬间切换到// 第一张图片,然后滑动到第二张//步骤://1.获取事件源及相关元素。(老三步)//2.复制第一张图片所在的li,添加到ul的最后面。//3.给ol中添加li,ul中的个数-1个,并点亮第一个按钮。//4.鼠标放到ol的li上切换图片//5.添加定时器//6.左右切换图片(鼠标放上去隐藏,移开显示)//1.获取事件源及相关元素。(老三步)var all = document.getElementById("all");var screen = all.firstElementChild || all.firstChild;var imgWidth = screen.offsetWidth;var ul = screen.firstElementChild || screen.firstChild;var ol = screen.children[1];var div = screen.lastElementChild || screen.lastChild;var spanArr = div.children;//2.复制第一张图片所在的li,添加到ul的最后面。var ulNewLi = ul.children[0].cloneNode(true);ul.appendChild(ulNewLi);//3.给ol中添加li,ul中的个数-1个,并点亮第一个按钮。for (var i = 0; i < ul.children.length - 1; i++) {var olNewLi = document.createElement("li");olNewLi.innerHTML = i + 1;ol.appendChild(olNewLi)}var olLiArr = ol.children;olLiArr[0].className = "current";//4.鼠标放到ol的li上切换图片for (var i = 0; i < olLiArr.length; i++) {//自定义属性,把索引值绑定到元素的index属性上olLiArr[i].index = i;olLiArr[i].onmouseover = function () {//排他思想for (var j = 0; j < olLiArr.length; j++) {olLiArr[j].className = "";}this.className = "current";//鼠标放到小的方块上的时候索引值和key以及square同步// key = this.index;// square = this.index;key = square = this.index;//移动盒子animate(ul, -this.index * imgWidth);}}//5.添加定时器var timer = setInterval(autoPlay, 1000);//固定向右切换图片//两个定时器(一个记录图片,一个记录小方块)var key = 0;var square = 0;function autoPlay() {//通过控制key的自增来模拟图片的索引值,然后移动ulkey++;if (key > olLiArr.length) {//图片已经滑动到最后一张,接下来,跳转到第一张,然后在滑动到第二张ul.style.left = 0;key = 1;}animate(ul, -key * imgWidth);//通过控制square的自增来模拟小方块的索引值,然后点亮盒子//排他思想做小方块square++;if (square > olLiArr.length - 1) {//索引值不能大于等于5,如果等于5,立刻变为0;square = 0;}for (var i = 0; i < olLiArr.length; i++) {olLiArr[i].className = "";}olLiArr[square].className = "current";}//鼠标放上去清除定时器,移开后在开启定时器all.onmouseover = function () {div.style.display = "block";clearInterval(timer);}all.onmouseout = function () {div.style.display = "none";timer = setInterval(autoPlay, 1000);}//6.左右切换图片(鼠标放上去显示,移开隐藏)spanArr[0].onclick = function () {//通过控制key的自增来模拟图片的索引值,然后移动ulkey--;if (key < 0) {//先移动到最后一张,然后key的值取之前一张的索引值,然后在向前移动ul.style.left = -imgWidth * (olLiArr.length) + "px";key = olLiArr.length - 1;}animate(ul, -key * imgWidth);//通过控制square的自增来模拟小方块的索引值,然后点亮盒子//排他思想做小方块square--;if (square < 0) {//索引值不能大于等于5,如果等于5,立刻变为0;square = olLiArr.length - 1;}for (var i = 0; i < olLiArr.length; i++) {olLiArr[i].className = "";}olLiArr[square].className = "current";}spanArr[1].onclick = function () {//右侧的和定时器一模一样autoPlay();}function animate(ele, target) {clearInterval(ele.timer);var speed = target > ele.offsetLeft ? 10 : -10;ele.timer = setInterval(function () {var val = target - ele.offsetLeft;ele.style.left = ele.offsetLeft + speed + "px";if (Math.abs(val) < Math.abs(speed)) {ele.style.left = target + "px";clearInterval(ele.timer);}}, 10)}}</script></head><body><div class="all" id='all'><div class="screen" id="screen"><ul id="ul"><li><img src="images/1.jpg" width="500" height="200"/></li><li><img src="images/2.jpg" width="500" height="200"/></li><li><img src="images/3.jpg" width="500" height="200"/></li><li><img src="images/4.jpg" width="500" height="200"/></li><li><img src="images/5.jpg" width="500" height="200"/></li></ul><ol></ol><div id="arr"><span id="left"><</span><span id="right">></span></div></div></div></body></html>
实现效果:

温馨提示:动图太大,可以把http://img.smyhvae.com/20180202_2020.gif单独在浏览器中打开。
scroll相关属性和缓动动画
scroll 相关属性
window.onscroll() 方法
当我们用鼠标滚轮,滚动网页的时候,会触发 window.onscroll() 方法。效果如下:(注意看控制台的打印结果)

如果你需要做滚动监听,可以使用这个方法。
我们来看看和 scroll 相关的有哪些属性。
1、ScrollWidth 和 scrollHeight
ScrollWidth 和 scrollHeight:获取元素整个滚动区域的宽、高。包括 width 和 padding,不包括 border和margin。
注意:
scrollHeight 的特点是:如果内容超出了盒子,scrollHeight为内容的高(包括超出的内容);如果不超出,scrollHeight为盒子本身的高度。ScrollWidth同理。
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;padding: 10px;margin: 3px;border: 8px solid red;}</style></head><body><div class="box">静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。</div><script>var div = document.getElementsByTagName("div")[0];// `scrollHeight` 的特点是:如果内容超出了盒子,`scrollHeight`为内容的高(包括超出的内容);如果不超出,`scrollHeight`为盒子本身的高度。//IE8以下(不包括IE8),为盒子本身内容的高度。console.log(div.scrollWidth);console.log(div.scrollHeight);</script></body></html>
打印结果:

2、scrollTop 和 scrollLeft
scrollLeft:获取水平滚动条滚动的距离。scrollTop:获取垂直滚动条滚动的距离。
实战经验:
当某个元素满足scrollHeight - scrollTop == clientHeight时,说明垂直滚动条滚动到底了。
当某个元素满足scrollWidth - scrollLeft == clientWidth时,说明水平滚动条滚动到底了。
这个实战经验非常有用,可以用来判断用户是否已经将内容滑动到底了。比如说,有些场景下,希望用户能够看完“长长的活动规则”,才允许触发接下来的表单操作。
scrollTop 的兼容性
如果要获取页面滚动的距离,scrollTop 这个属性的写法要注意兼容性,如下。
(1)如果文档没有 DTD 声明,写法为:
document.body.scrollTop
在没有 DTD 声明的情况下,要求是这种写法,chrome浏览器才能认出来。
(2)如果文档有 DTD 声明,写法为:
document.documentElement.scrollTop
在有 DTD 声明的情况下,要求是这种写法,IE6、7、8才能认出来。
综合上面这两个,就诞生了一种兼容性的写法:
document.body.scrollTop || document.documentElement.scrollTop //方式一document.body.scrollTop + document.documentElement.scrollTop //方式二
另外还有一种兼容性的写法:window.pageYOffset 和 window.pageXOffset。这种写法无视DTD的声明。这种写法支持的浏览器版本是:火狐/谷歌/ie9+。
综合上面的几种写法,为了兼容,不管有没有DTD,最终版的兼容性写法:
window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
判断是否已经 DTD 声明
方法如下:
document.compatMode === "CSS1Compat" // 已声明document.compatMode === "BackCompat" // 未声明
将 scrollTop 和 scrollLeft 进行封装
这里,我们将 scrollTop 和 scrollLeft 封装为一个方法,名叫scroll(),返回值为 一个对象。以后就直接调用scroll().top 和 scroll().left就好。
代码实现:
<html><head lang="en"><meta charset="UTF-8"><title></title><style>body {height: 6000px;width: 5000px;}</style></head><body><script>//需求:封装一个兼容的scroll().返回的是对象,用scroll().top获取scrollTop,用scroll().left获取scrollLeftwindow.onscroll = function () {// var myScroll = scroll();// myScroll.top;console.log(scroll().top);console.log(scroll().left);}//函数封装(简单封装,实际工作使用)function scroll() {return { //此函数的返回值是对象left: window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,right: window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft}}</script></body></html>
上方代码中,函数定义的那部分就是要封装的代码。
另外还有一种比较麻烦的封装方式:(仅供参考)
function scroll() { // 开始封装自己的scrollTopif(window.pageYOffset !== undefined) { // ie9+ 高版本浏览器// 因为 window.pageYOffset 默认的是 0 所以这里需要判断return {left: window.pageXOffset,top: window.pageYOffset}}else if(document.compatMode === "CSS1Compat") { // 标准浏览器 来判断有没有声明DTDreturn {left: document.documentElement.scrollLeft,top: document.documentElement.scrollTop}}return { // 未声明 DTDleft: document.body.scrollLeft,top: document.body.scrollTop}}
获取 html 文档的方法
获取title、body、head、html标签的方法如下:
document.title文档标题;document.head文档的头标签document.body文档的body标签;document.documentElement(这个很重要)。
document.documentElement表示文档的html标签。也就是说,基本结构当中的 html 标签而是通过document.documentElement访问的,并不是通过 document.html 去访问的。
scrollTop 举例:固定导航栏
完整版代码实现:
(1)index.html:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>* {margin: 0;padding: 0}img {vertical-align: top;}.main {margin: 0 auto;width: 1000px;margin-top: 10px;}#Q-nav1 {overflow: hidden;}.fixed {position: fixed;top: 0;left: 0;}</style><!--引入工具js--><script src="tools.js"></script><script>window.onload = function () {//需求1:当我们滚动界面的时候,被卷曲的头部如果超过第二个盒子距离顶部的位置,那么直接给第二个盒子加类名.fixed//需求2:当我们滚动界面的时候,被卷曲的头部如果小于第二个盒子距离顶部的位置,那么直接给第二个盒子取消类名.fixed//1.老三步。var topDiv = document.getElementById("top");var height = topDiv.offsetHeight;var middle = document.getElementById("Q-nav1");var main = document.getElementById("main");window.onscroll = function () {//2.判断 ,被卷曲的头部的大小if (scroll().top > height) {//3.满足条件添加类,否则删除类middle.className += " fixed";//第二个盒子也要占位置,为了避免重叠,我们给第三个盒子一个上padding的空间,把这个空间留给第二个盒子main.style.paddingTop = middle.offsetHeight + "px";} else {middle.className = "";//清零main.style.paddingTop = 0;}}}</script></head><body><div class="top" id="top"><img src="images/top.png" alt=""/></div><div id="Q-nav1"><img src="images/nav.png" alt=""/></div><div class="main" id="main"><img src="images/main.png" alt=""/></div></body></html>
上方代码中,有一个技巧:
main.style.paddingTop = middle.offsetHeight + "px";
仔细看注释就好。
(2)tools.js:
/*** Created by smyhvae on 2018/02/03.*/function scroll() { // 开始封装自己的scrollTopif (window.pageYOffset !== undefined) { // ie9+ 高版本浏览器// 因为 window.pageYOffset 默认的是 0 所以这里需要判断return {left: window.pageXOffset,top: window.pageYOffset}}else if (document.compatMode === "CSS1Compat") { // 标准浏览器 来判断有没有声明DTDreturn {left: document.documentElement.scrollLeft,top: document.documentElement.scrollTop}}return { // 未声明 DTDleft: document.body.scrollLeft,top: document.body.scrollTop}}
实现效果:

工程文件:
- 2018-02-03-scrollTop固定导航栏.rar
- 下载链接暂无。
缓动动画
三个函数
缓慢动画里,我们要用到三个函数,这里先列出来:
- Math.ceil() 向上取整
- Math.floor() 向下取整
- Math.round(); 四舍五入
缓动动画的原理
缓动动画的原理就是:在移动的过程中,步长越来越小。
设置步长为:目标位置和盒子当前位置的十分之一。用公式表达,即:
盒子位置 = 盒子本身位置 + (目标位置 - 盒子本身位置)/ 10;
代码举例:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;background-color: pink;position: absolute;}</style></head><body><button>运动到left = 400px</button><div></div><script>var btn = document.getElementsByTagName("button")[0];var div = document.getElementsByTagName("div")[0];btn.onclick = function () {setInterval(function () {//动画原理:盒子未来的位置 = 盒子当前的位置+步长div.style.left = div.offsetLeft + (400 - div.offsetLeft) / 10 + "px";}, 30);}</script></body></html>
效果:

缓慢动画的封装(解决四舍五入的问题)
我们发现一个问题,上图中的盒子最终并没有到达400px的位置,而是只到了396.04px就停住了:

原因是:JS在取整的运算时,进行了四舍五入。
我们把打印396.04px这个left值打印出来看看:

我么发现,通过div.style.left获取的值是精确的,通过div.offsetLeft获取的left值会进行四舍五入。
此时,我们就要用到取整的函数了。
通过对缓动动画进行封装,完整版的代码实现如下:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;background-color: pink;position: absolute;left: 0;}</style></head><body><button>运动到200</button><button>运动到400</button><div></div><script>var btn = document.getElementsByTagName("button");var div = document.getElementsByTagName("div")[0];btn[0].onclick = function () {animate(div, 200);}btn[1].onclick = function () {animate(div, 400);}//缓动动画封装function animate(ele, target) {//要用定时器,先清定时器//一个萝卜一个坑儿,一个元素对应一个定时器clearInterval(ele.timer);//定义定时器ele.timer = setInterval(function () {//获取步长//步长应该是越来越小的,缓动的算法。var step = (target - ele.offsetLeft) / 10;//对步长进行二次加工(大于0向上取整,小于0向下取整)//达到的效果是:最后10像素的时候都是1像素1像素的向目标位置移动,就能够到达指定位置。step = step > 0 ? Math.ceil(step) : Math.floor(step);//动画原理: 目标位置 = 当前位置 + 步长ele.style.left = ele.offsetLeft + step + "px";console.log(step);//检测缓动动画有没有停止console.log("smyhvae");if (Math.abs(target - ele.offsetLeft) <= Math.abs(step)) {//处理小数赋值ele.style.left = target + "px";clearInterval(ele.timer);}}, 30);}</script></body></html>
实现效果:

window.scrollTo()方法举例:返回到顶部小火箭
(1)index.html:
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title><style>img {position: fixed;bottom: 100px;right: 50px;cursor: pointer;display: none;border: 1px solid #000;}div {width: 1210px;margin: 100px auto;text-align: center;font: 500 26px/35px "simsun";color: red;}</style><script src="tools.js"></script><script>window.onload = function () {//需求:被卷去的头部超过100显示小火箭,然后点击小火箭屏幕缓慢移动到最顶端。//难点:我们以前是移动盒子,现在是移动屏幕,我们没有学过如何移动屏幕。// 技术点:window.scrollTo(x,y);浏览器显示区域跳转到指定的坐标//步骤://1.老三步var img = document.getElementsByTagName("img")[0];window.onscroll = function () {//被卷去的距离大于200显示小火箭,否则隐藏//2.显示隐藏小火箭if (scroll().top > 1000) {img.style.display = "block";} else {img.style.display = "none";}//每次移动滚动条的时候都给leader赋值,模拟leader获取距离顶部的距离leader = scroll().top;}//3.缓动跳转到页面最顶端(利用我们的缓动动画)var timer = null;var target = 0; //目标位置var leader = 0; //显示区域自身的位置img.onclick = function () {//技术点:window.scrollTo(0,0);//要用定时器,先清定时器clearInterval(timer);timer = setInterval(function () {//获取步长var step = (target - leader) / 10;//二次处理步长step = step > 0 ? Math.ceil(step) : Math.floor(step);leader = leader + step; //往上移动的过程中,step是负数。当前位置减去步长,就等于下一步的位置。//显示区域移动window.scrollTo(0, leader);//清除定时器if (leader === 0) {clearInterval(timer);}}, 25);}}</script></head><body><img src="images/Top.jpg"/><div>我是最顶端.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br></div></body></html>
(2)tools.js:
/*** Created by smyhvae on 2015/12/8.*///函数:获取scrollTop和scrollLeft的值function scroll() { // 开始封装自己的scrollTopif (window.pageYOffset != null) { // ie9+ 高版本浏览器// 因为 window.pageYOffset 默认的是 0 所以这里需要判断return {left: window.pageXOffset,top: window.pageYOffset}}else if (document.compatMode === "CSS1Compat") { // 标准浏览器 来判断有没有声明DTDreturn {left: document.documentElement.scrollLeft,top: document.documentElement.scrollTop}}return { // 未声明 DTDleft: document.body.scrollLeft,top: document.body.scrollTop}}
实现效果:

小火箭的图片资源:

client(可视区)相关属性
client 家族的组成
clientWidth 和 clientHeight
元素调用时:
- clientWidth:获取元素的可见宽度(width + padding)。
- clientHeight:获取元素的可见高度(height + padding)。
body/html 调用时:
- clientWidth:获取网页可视区域宽度。
- clientHeight:获取网页可视区域高度。
声明:
clientWidth和clientHeight属性是只读的,不可修改。clientWidth和clientHeight的值都是不带 px 的,返回的都是一个数字,可以直接进行计算。
clientX 和 clientY
event调用:
- clientX:鼠标距离可视区域左侧距离。
- clientY:鼠标距离可视区域上侧距离。
clientTop 和 clientLeft
- clientTop:盒子的上border。
- clientLeft:盒子的左border。
三大家族 offset/scroll/client 的区别
区别1:宽高
- offsetWidth = width + padding + border
- offsetHeight = height + padding + border
- scrollWidth = 内容宽度(不包含border)
- scrollHeight = 内容高度(不包含border)
- clientWidth = width + padding
- clientHeight = height + padding
区别2:上左
offsetTop/offsetLeft:
- 调用者:任意元素。(盒子为主)
- 作用:距离父系盒子中带有定位的距离。
scrollTop/scrollLeft:
- 调用者:document.body.scrollTop(window调用)(盒子也可以调用,但必须有滚动条)
- 作用:浏览器无法显示的部分(被卷去的部分)。
clientY/clientX:
- 调用者:event
- 作用:鼠标距离浏览器可视区域的距离(左、上)。
函数封装:获取浏览器的宽高(可视区域)
函数封装如下:
//函数封装:获取屏幕可视区域的宽高function client() {if (window.innerHeight !== undefined) {//ie9及其以上的版本的写法return {"width": window.innerWidth,"height": window.innerHeight}} else if (document.compatMode === "CSS1Compat") {//标准模式的写法(有DTD时)return {"width": document.documentElement.clientWidth,"height": document.documentElement.clientHeight}} else {//没有DTD时的写法return {"width": document.body.clientWidth,"height": document.body.clientHeight}}}
案例:根据浏览器的可视宽度,给定不同的背景的色。
PS:这个可以用来做响应式。
代码如下:(需要用到上面的封装好的方法)
<!DOCTYPE html><html><head lang="en"><meta charset="UTF-8"><title></title></head><body><script src="tools.js"></script><script>//需求:浏览器每次更改大小,判断是否符合某一标准然后给背景上色。// // >960红色,大于640小于960蓝色,小于640绿色。window.onresize = fn; //页面大小发生变化时,执行该函数。//页面加载的时候直接执行一次函数,确定浏览器可视区域的宽,给背景上色fn();//封装成函数,然后指定的时候去调用和绑定函数名function fn() {if (client().width > 960) {document.body.style.backgroundColor = "red";} else if (client().width > 640) {document.body.style.backgroundColor = "blue";} else {document.body.style.backgroundColor = "green";}}</script></body></html>
上当代码中,window.onresize事件指的是:在窗口或框架被调整大小时发生。各个事件的解释如下:
- window.onscroll 屏幕滑动
- window.onresize 浏览器大小变化
- window.onload 页面加载完毕
- div.onmousemove 鼠标在盒子上移动(注意:不是盒子移动)
获取显示器的分辨率
比如,我的电脑的显示器分辨率是:1920*1080。
获取显示器的分辨率:
window.onresize = function () {document.title = window.screen.width + " " + window.screen.height;}
显示效果:

上图中,不管我如何改变浏览器的窗口大小,title栏显示的值永远都是我的显示器分辨率:1920*1080。
