DOM简介:

通过 HTML DOM,JavaScript 能够访问和改变 HTML 文档的所有元素。


HTML DOM(文档对象模型)
当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。

HTML DOM 模型被结构化为对象树

对象的 HTML DOM 树
在这里插入图片描述

通过这个对象模型,JavaScript 获得创建动态 HTML 的所有力量:

  • JavaScript 能改变页面中的所有 HTML 元素
  • JavaScript 能改变页面中的所有 HTML 属性
  • JavaScript 能改变页面中的所有 CSS 样式
  • JavaScript 能删除已有的 HTML 元素和属性
  • JavaScript 能添加新的 HTML 元素和属性
  • JavaScript 能对页面中所有已有的 HTML 事件作出反应
  • JavaScript 能在页面中创建新的 HTML 事件

什么是 DOM?
DOM 是一项 W3C (World Wide Web Consortium) 标准。

DOM 定义了访问文档的标准:

“W3C 文档对象模型(DOM)是中立于平台和语言的接口,它允许程序和脚本动态地访问、更新文档的内容、结构和样式。”
W3C DOM 标准被分为 3 个不同的部分:

  • Core DOM - 所有文档类型的标准模型
  • XML DOM - XML 文档的标准模型
  • HTML DOM - HTML 文档的标准模型

什么是 HTML DOM?
HTML DOM 是 HTML 的标准对象模型和编程接口。它定义了:

作为对象的 HTML 元素

  • 所有 HTML 元素的属性
  • 访问所有 HTML 元素的方法
  • 所有 HTML 元素的事件

换言之:HTML DOM 是关于如何获取、更改、添加或删除 HTML 元素的标准。


HTML DOM 是关于如何获取、更改、添加或删除 HTML 元素的标准。
Document对象是我们可以从脚本中对HTML页面中的所有元素进行访问

节点:Node——构成HTML文档最基本的单元。
常用节点分为4类:

  • 文档节点:整个HTML文档
  • 元素节点:HTML文档中的HTML标签
  • 属性节点:元素的属性
  • 文本节点:HTML标签中的文本内容

在这里插入图片描述
文档的加载:
    浏览器在加载一个页面的时候,是按照自上向下的顺序加载的
    读取到一行就运行一行,如果将script标签写到页面的上边,
    在代码执行时,页面还没有加载,页面没有加载DOM对象也没有加载
    会导致无法获取到DOM对象

Onload事件在整个 页面加载完成之后才触发
    为window绑定一个onload事件
    该事件对应的响应函数,将会在页面加载完成之后执行,
    这样可以确保我们的代码执行时所有的DOM对象已经加载完成。


获取元素节点

通过document对象调用

1.getElementById()
  通过id属性来获取一个元素节点的对象

2.getElementsByTagName()
  可以根据标签名来获取一组元素节点对象
  这个方法会给我们返回一个类数组对象,所有查询到的元素都会在封装到对象
  即使查询到的元素只有一个,也会封装到数组中返回。

3.getElementsByName()
  通过name属性来获取一组元素节点对象。
  这个方法会给我们返回一个类数组对象,所有查询到的元素都会在封装到对象。
  即使查询到的元素只有一个,也会封装到数组中返回。

InnerHTML 通过这个属性可以获取到元素内部的html代码
  Bj.innerHTML
  对于自结束标签没有意义,会返回为空。
  如果需要读取元素节点属性,
  直接使用 元素.属性名
  例子: 元素.id 元素.name 元素.value
  注意:class属性不能采用这种方式,(主要用于表单中)
  读取class属性需要使用 元素. className

innerText
  该属性可以获取元素内部中的文本内容。
  他和innerHTML类似,不同的是他会自动将html标签去除。


获取元素节点的子节点
通过具体的元素节点调用
1.getElementByTagName()
  是一个方法,方法返回当前的指定标签名后代节点

2.childNodes
  是一个属性 表示当前节点的所有子节点
  childNodes属性会获取包括文本节点在内的所有节点
  根据DOM标签与标签间的空白也会当成文本节点
注意:IE8及以下的浏览器中不会将空白文本当成子节点,
children属性可以获取当前元素的所有子元素。标签与标签间的空白不会当成文本节点。

3.firstChild
   属性,表示当前节点的第一个子节点。
  可以获取当前元素的第一个子节点(包含空白文本节点和注释)。

  firstElementChild:指向第一个元素;(不包括文本节点和注释)

4.lastChile
   属性,表示当前节点的最后一个子节点
  可以获取当前元素的最后一个子节点(包含空白文本节点和注释)。

  lastElementChild:指向最后一个子元素;(不包括文本节点和注释)


遍历元素
  childElementCount:返回子元素的个数(不包括文本节点和注释)


获取父节点和兄弟节点

通过具体的节点调用
1.parentNode
   属性,表示当前节点的父节点

2.previousSibling
   属性,表示当前节点的前一个兄弟节点

  previousSibling 属性返回元素节点之前的兄弟节点(包括文本节点、注释节点);
  previousElementSibling 属性只返回元素节点之前的兄弟元素节点(不包括文本节点、注释节点);

3.nextSibling
   属性,表示当前节点的后一个兄弟节点

  nextSibling 属性返回元素节点之后的兄弟节点(包括文本节点、注释节点);
  nextElementSibling 属性只返回元素节点之后的兄弟元素节点(不包括文本节点、注释节点)。


1、在document中有一个属性body,它保存的是body的引用
  Var body = document.body;

2、Doucument.doucumentElement保存的是html根标签
  Var html = document.documentElenmet;

3、Document.all 代表页面中的所有元素
  var all = document.all;
  (var all = document.getElementsByTagName(“*”))效果一样。
  它返回一个数组

4、根据元素的class属性查询一组元素节点对象
  GetElementsByClassNmae() 可以根据class属性值获取一组元素节点对象(类数组)
  但是该方法 不支持IE8及以下浏览器。

5、documen.querySelector()
   需要一个选择器的字符串作为参数,可以根据一个CSS选择器来查询一个元素节点对象
   虽然IE8中没有getElementByTagName() 但是可以使用querySelector,他可以支持IE8.
   使用该方法总会返回一个唯一的一个元素,如果满足条件的元素有多个,那么它会返回第一个。

6、documen.querySelectorAll()
  该方法和querySelector()用法类似,不同的是他会将符合条件的元素封装到一个数组中
  即使符合条件的元素只有一个,他也会返回数组。


DOM增删改查:

1.Document.createElement()
  可以用于创建一个元素节点对象,
  它需要一个标签名作为参数,将会根据标签名创建元素节点对象
  并将创建好的对象作为返回值返回。
  它可以个innerHTML结合使用。
在这里插入图片描述

2.document.createTextNode()
  可以用来创建一个文本节点对象
  需要一个文本内容作为参数,将会根据该内容创建文本节点,并将新的节点返回。

3.appendChild()
  向一个父节点中添加一个新的子节点
  用法:父节点.appendChild(子节点);

4.insertBefore()
  可以在指定的子节点前插入新的子节点
  语法:
  父节点.insertBefore(新节点,旧节点);

5.replaceChild()
  可以使用指定的子节点替换已有的子节点
  语法:
  父节点.replaceChile(新节点,旧节点)

6.removeChild()
  可以删除一个子节点
  语法:父节点.removeChild(子节点);
  但是:
  常用:子节点.parentNode.removeChild(子节点)

Document 对象

每个载入浏览器的 HTML 文档都会成为 Document 对象。

Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问。

提示:Document 对象是 Window 对象的一部分,可通过 window.document 属性对其进行访问。


一、文档子节点

1、在document中有一个属性body,它保存的是body的引用(比通过childNodes列表访问的更快,更直接)
  Var body = document.body;

2、Doucument.doucumentElement保存的是html根标签
  Var html = document.documentElenmet;

3、Document.all 代表页面中的所有元素
  var all = document.all;
  (var all = document.getElementsByTagName(“*”))效果一样。
  它返回一个数组

4、根据元素的class属性查询一组元素节点对象
  GetElementsByClassNmae() 可以根据class属性值获取一组元素节点对象(类数组)
  但是该方法 不支持IE8及以下浏览器。

5、documen.querySelector()
   需要一个选择器的字符串作为参数,可以根据一个CSS选择器来查询一个元素节点对象
   虽然IE8中没有getElementByTagName() 但是可以使用querySelector,他可以支持IE8.
   使用该方法总会返回一个唯一的一个元素,如果满足条件的元素有多个,那么它会返回第一个。

6、documen.querySelectorAll()
  该方法和querySelector()用法类似,不同的是他会将符合条件的元素封装到一个数组中
  即使符合条件的元素只有一个,他也会返回数组。


二、文档信息

1.document.title
  可以取得当前界面的标题,也可以修改当前界面的标题并反映给浏览器的标题栏中。

1
2
console.log(document.title)  //查看标题
document.title = "aqing" //修改标题

2.属性 URL、domain、referrer 都和网页请求有关。
  URL属性包含完整的URL
  domain属性中只包含了页面的域名。
  referrer属性中保存着连接到当前页面的哪个页面的URL,在没有来源页面的情况下,referrer属性中可能会包含空字符串


三、查找元素

1.getElementById()
  通过id属性来获取一个元素节点的对象

2.getElementsByTagName()
  可以根据标签名来获取一组元素节点对象
  这个方法会给我们返回一个类数组对象,所有查询到的元素都会在封装到对象
  即使查询到的元素只有一个,也会封装到数组中返回。

3.getElementsByName()
  通过name属性来获取一组元素节点对象。
  这个方法会给我们返回一个类数组对象,所有查询到的元素都会在封装到对象。
  即使查询到的元素只有一个,也会封装到数组中返回。


四、特殊集合

1、document.anchors
  包含文档中所有带name特性的 a 元素

2、document.forms
  包含文档中所有的

元素,与documen.getElementByTagName(“form”)结果相同。

3、document.images
  包含文档中所有的元素,与documen.getElementByTagName(“img”)结果相同。

4、document.links
  包含文档中所带href特性的 a 元素

集合中的项也会随着当前文档内容的更新而更新


五、DOM一致性检测

hasFeature()
  这个方法有两个参数:要检测的DOM功能的名称及版本号。如果浏览器支持给定的名称和版本的功能,则该方法会返回 true 。

1
console.log(document.implementation.hasFeature("XML", "1.0"));

六、文档写入

1、write()方法
定义和用法
  write() 方法可向文档写入 HTML 表达式或 JavaScript 代码。

可列出多个参数(exp1,exp2,exp3,…) ,它们将按顺序被追加到文档中。

语法
  document.write(exp1,exp2,exp3,….)

说明
  虽然根据 DOM 标准,该方法只接受单个字符串作为参数。不过根据经验,write() 可接受任何多个参数。

我们通常按照两种的方式使用 write() 方法:一是在使用该方在文档中输出 HTML,另一种是在调用该方法的的窗口之外的窗口、框架中产生新文档。在第二种情况中,请务必使用 close() 方法来关闭文档。

2、writeln() 方法

定义和用法
  writeln() 方法与 write() 方法作用相同,外加可在每个表达式后写一个换行符。

3、open()方法

定义和用法
  open() 方法可打开一个新文档,并擦除当前文档的内容。

语法
  document.open(mimetype,replace)
|参数| 描述 |
|–|–|
| mimetype |可选。规定正在写的文档的类型。默认值是 “text/html”。 |
|replace|可选。当此参数设置后,可引起新文档从父文档继承历史条目。|

说明
  该方法将擦除当前 HTML 文档的内容,开始一个新的文档,新文档用 write() 方法或 writeln() 方法编写。

提示和注释
  重要事项:调用 open() 方法打开一个新文档并且用 write() 方法设置文档内容后,必须记住用 close 方法关闭文档,并迫使其内容显示出来。

注释:属于被覆盖的文档的一部分的脚本或事件句柄不能调用该方法,因为脚本或事件句柄自身也会被覆盖。

4、close() 方法

定义和用法
  close() 方法可关闭一个由 document.open 方法打开的输出流,并显示选定的数据。

语法
  document.close()
说明
  该方法将关闭 open() 方法打开的文档流,并强制地显示出所有缓存的输出内容。如果您使用 write() 方法动态地输出一个文档,必须记住当你这么做的时候要调用 close() 方法,以确保所有文档内容都能显示。

  一旦调用了 close(),就不应该再次调用 write(),因为这会隐式地调用 open() 来擦除当前文档并开始一个新的文档。

动态脚本

创建动态脚本有两种方式。

1、引入外部文件

1
<script type="text/javascript" src="aqing.js" ></script>

2、直接插入JavaScript代码

1
2
3
4
5
6
7
8
function loadScript(url) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}

loadScript("aqing.js");

动态样式

能够把CSS样式包含到HTML页面中的元素有两个。其中,<link>元素用于包含来自外部的文件,而<style>元素用于指定嵌入样式。与动态脚本类似。所谓动态样式是指在页面刚加载时不存在的样式;动态样式是页面加载完成后动态添加到页面中的。

1、插入外部文件

1
<link rel="stylesheet" type="text/css" href="style.css">

2、直接插入JavaScript代码

1
2
3
4
5
6
7
8
9
10
11
12
13
function loadStyleString(css) {
var style = document.createElement("style")
style.type = "text/css";
try {
style.appendChild(document.createTextNode(css));
} catch (ex) {
style.styleSheet.cssText = css;
}
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}

loadStyleString("body{background-color:red}");

选择符API

1、querySlector()方法

querySlector()方法接受一个CSS选择器,返回与该模式匹配的第一个元素。通过Document类型调用querySlector()方法时,会在文档元素的范围内查找匹配的元素。而通过Element类型调用querySlector()方法时,只会在该元素的范围内查找匹配的元素。

1
2
3
4
5
6
7
8
9
10
11
//取得body元素
var body = document.querySelector("body");

//取得ID为“mydiv”的元素
var myDiv = document.querySelector("#mydiv");

//取得类为 "selected"的第一个元素
var selected = document.querySelector(".selected");

//取得button元素的第一个图像
var img = document.body.querySelector("img.button");

2、querySelectorAll()方法

querySelectAll()方法接受的参数与querySelect()方法一样都是一个CSS选择符,但querySelectAll()方法返回的是一个NodeList的实例,不仅仅是一个元素,而是所有匹配到的都会在NodeList中。只要querySelectAll()方法的CSS选择符有效,该方法都会返回一个NodeList对象,不管匹配到多少元素,就算是空的,也会返回一个Nodelist。

1
2
//取得所有类为 "selected"的元素
var selected = document.querySelectorAll(".selected");

3、matchesSelector()方法

Element类型新增的一个方法matchesSelector(),接受一个CSS选择符为参数,如果调用元素与该选择符匹配,返回true;否则返回false。

1
2
3
if(document.body.matchesSelctor("body.page1")){
//true
}

与类相关的扩充

classList属性
为所有元素添加classList属性。它自己有一个表示自己包含多少元素的属性,取得每个元素可以使用item()方法,也可以使用方括号语法。此外还有如下方法:

add(value):将给定的字符串值添加到列表中,如果值已经存在,就不添加了。
contains(value):表示列表中是否存在给定的值,如果存在就返回true,否则返回flase。
remove(value):从列表中删除给定的字符串。
toggle(value):如果列表中已经存在给定的值,则删除它;如果列表中没有给定的值,则添加它。

支持chrome和Firfox 3.6和IE10以上。

1
2
3
4
5
6
7
8
9
10
11
12
13
//向div元素中添加div类
div.classList.add("div")
//检查div类
div.classList.contains("div")
//删除div类
div.classList.remove("div")
// 切换div类
div.classList.toggle("div")

// 迭代类名
for (var i = 0; i < div.classList.length; i++) {
console.log(div.classList[i]);
}

焦点管理

document.activeElement属性,这个属性始终会引用DOM中当前获得焦点的元素。元素获取焦点的方式有页面加载,用户输入(通常是按Tab键),和代码汇总调用focus()方法。

默认情况下,文档刚刚加载完成时,document.activeElement中保存的是document.body元素的引用。文档加载期间,document.activeElement的值为null。

另外就是新增了document.hasFocus()方法,这个方法用于确定文档是否获得了焦点。


HTMLDocument的变化

1、readyState属性

它有两个可能的值

  • loading,正在加载文档
  • complete,已经加载完文档

使用document.readyState的最恰当方式,就是通过它来实现一个指示文档已经加载完成的指示器。用法如下:

1
2
3
if(document.readyState =="complete"){
// 执行操作
}

2、兼容模式

这个属性是为了告诉开发人员浏览器采用的哪种渲染模式。

1
2
3
4
5
if (document.compatMode == "CSS1Compat") {
alert("Standards mode"); //标准模式
} else {
alert("Quirks mode") //混杂模式
}

3、head属性
引用文档的<head>元素,可以结合使用这个属性和另一种后备方法。

1
2
var head = document.head || document.getElementsByTagName("head")[0];
//如果有head属性就使用,否则仍然使用document.getElementsByTatName("head")[0];

字符集属性

charset属性表示文档中实际使用的字符集,也可以直接设置。

1
document.charset

插入标记

1、innerHTML属性

在读模式下,innerHTML属性返回与调用元素的所有子节点(包括元素,注释和文本节点)和对应的HTML标记。在写模式下,innerHTML会根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素原先的所有子节点。

不是所有的元素都支持innerHTML属性。不支持innerHTML的元素有:<col>、<colgroup>、<frameset>、<head>、<html>、<style>、<tbody>、<thead>、<tfoot>、<tr>,在IE8及更早的版本中,<title>元素也没有innerHTML属性。

2、outerHTML属性

在读模式下,outerHTML属性返回调用它的元素所有子节点的HTML标签,在写模式下,outerHTML根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素。

1
2
3
4
5
6
7
8
9
div.outerHTML = "<p>aqing</p>";
//上面的效果和下面的一样



var p = document.createElement("p");
p.appendChild(document.createTextNode("<p>aqing</p>"));
div.parentNode.replaceChild(p, div);

3、insertAdjacentHTMl()方法

它接收两个参数:插入的位置要插入的HTML文本。第一个参数必须是下列值之一:

“beforebegin”,在当前元素之下插入一个紧邻的同辈元素;
“afterbegin”,在当前元素之下插入一个新的子元素或在第一个子元素之前在插入新的子元素;
“beforeend”,在当前元素之下插入一个新的子元素或在最后一个子元素之后在插入新的子元素;
“afterend”,在当前元素之后插入一个紧邻的同辈元素;

1
2
3
4
div.insertAdjacentHTML("beforeBegin", "<p>aqing</p>")
div.insertAdjacentHTML("afterEnd", "<p>aqing</p>")
div.insertAdjacentHTML("afterBegin", "<p>aqing</p>")
div.insertAdjacentHTML("beforeEnd", "<p>aqing</p>")

4、innerText属性

通过innerText 属性可以操作元素中包含所有文本内容,包括子文档树中的文本。通过innerText 读取值时,他会按照由浅入深的顺序,将子文档树中的所有文本拼接起来。再通过innerText 写入值时,结果会删除所有子节点,插入包含响应文本值的文本节点。

1
div.innerText = div.innerText;

执行这行代码后,就用原来的文本内容替换了容器中的所有内容(包含子节点,因而也就去掉了HTML标签)。

5、outerText 属性

除了作用范围变大到了包含调用它的节点之外,outerText与innerHTML 基本只上没有多大的区别。
在读取文本值时,结果完全一样。但在写模式下,outerText 就完全不同了:outerText 不只是替换调用它的元素子节点,而是会替换整个元素(包括子节点)。
由于这个属性会导致调用它的元素不存在,因此并不常用。


scrollIntoView()方法

滚动页面,可以在所有HTML元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。

1
div.scrollIntoView();

contains()方法

调用contains()方法的应该是祖先节点,也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测到的节点是后代节点,该方法返回true,否则返回false

1
2
console.log(document.documentElement.contains(document.body));
// true

测试<body>元素是不是<html>元素的后代。


访问元素的样式

通过JS修改元素的样式:
  语法:元素.style.样式名=样式值 (样式值必须是字符串样式)
  注意:如果css的样式汇总含有 - ,
  这种名称在JS中是不合法的,比如background-color
  需要将这种样式的命名修改为驼峰命名法去掉 - ,
  然后将 - 后的字母大写。

1
2
box.style.backgroundColor = "yellow"
box.style.width = "100px"

我们通过style属性设置的样式都是内联样式,
而内联样式有较高的优先级,所以通过JS修改的样式往往会立即显示。

但是如果在样式中写了!Important,则此时样式会有最高的优先级,
即使通过js也不能覆盖该样式,此时将会导致js修改样式失效
所以尽量不要为样式添加!Important

通过JS读取内联的样式:
  语法:元素.style.样式名
  通过style属性设置和读取的都是内联样式,如果没有内联样式则会返回空置。

通过style来设置元素的样式,记得要加上 单位 px


style对象的属性和方法

style对象定义了一些对象和方法,这些属性和方法在提供元素的style特性值的同时,也可以修改样式。

  • cssText:能够访问到style特性中的CSS代码。
  • length:应用给元素的CSS属性的数量。
  • getPropertyValue(propertyName):返回给定属性的字符串值
  • item(index):返回给定位置的CSS属性的名称。
  • removeProperty(propertyName):从样式表中删除给定的属性。
  • setProperty(propertyName,value,priority):将给定的属性设置为相应的值,并加上优先权标志(“important”或者一个空的字符串)。
1
2
3
4
5
6
7
8
9
10
11
12
//重写cssText
text.style.cssText = "width:200px;height:100px";
console.log(text.style.cssText)


//获取元素样式的属性名和属性值
var pop, value, i, len;
for (i = 0; i < text.style.length; i++) {
pop = text.style[i]; //或者pop = text.style.item(i);
value = text.style.getPropertyValue(pop);
console.log(pop + ":" + value);
}

获取计算的样式

js中如何获取元素的当前显示样式

无论在哪个浏览器中,所有计算的样式都是只读的


元素的大小

1、偏移量

offsetWidth
offsetHeight

  • 获取元素的整个的宽度和高度,包括内容区和内边距和边框。

offsetParent

  • 可以用来获取当前元素的定位父元素
  • 获取到里当前元素最近的开启了定位的祖先元素。
  • 如果所有的祖先元素都没有开启定位则会返回boby

offsetLeft

  • 当前元素相对于其定位父元素的水平偏移量。(外边框的距离也算)

offsetTop

  • 当前元素相对于其定位父元素的垂直偏移量。(外边框的距离也算)

这些属性都是不带px的,返回的都是一个数字,可以直接进行计算

在这里插入图片描述
要想知道某个元素在页面上的偏移量,将这个元素的offsetLeft和offsetTop与其offsetParent的相同属性相加,如此循环直至根元素,就可以得到一个基本准确的值。以下两个函数就可以用于分别取得元素的左和上偏移量。

获取元素的左偏移量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取元素的左偏移量
function getElementLeft(element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;

while (current !== null) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}

return actualLeft;
}

console.log(getElementLeft(text));

获取元素的上偏移量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取元素的上偏移量
function getElementTop(element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;

while (current !== null) {
actualTop += current.offsetTop;
current = current.offsetParent;
}

return actualTop;
}

console.log(getElementTop(text));

所有的偏移量都是只读的。而且每次访问都需要重新计算一次。因此应该尽量避免重复访问这些属性;如果需要重复使用其中某些属性的值,可以将他们设置为局部变量,以提高性能。


2、客户区大小

元素的客户区大小(clientdimension),指的是元素内容及其内边距所占据的空间大小。有关客户区大小的属性有两个: clientWidth和clientHeight。其中,clientWidth属性是元素内容区宽度加上左右内边距宽度; clientHeight属性是元素内容区高度加上,上下内边距高度。图12-2 形象地说明了这些属性表示的大小。

clientWidth
clientHeight

  • 这两个属性可以获取元素的可见高度
  • 这些属性都是不带px的,返回的都是一个数字,可以直接进行计算
  • 获取元素的高度和宽度,包括内容区和内边距区
  • 这些属性都是只读的,不能修改
  • 与偏移量相似,客户区大小也是只读的,也是每次访问都要重新计算。
    在这里插入图片描述

3、滚动大小

最后要介绍的是滚动大小( scroll dimension ),指的是包含滚动内容的元素的大小。有些元素(例如元素),即使没有执行任何代码也能自动地添加滚动条;但另外-些元素,则需要通过CSS的overflow属性进行设置才能滚动。以下是4个与滚动大小相关的属性。

  • scrollHeight:在没有滚动条的情况下,元素内容的总高度。
  • scrollwidth:在没有滚动条的情况下,元素内容的总宽度。
  • scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
  • scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。

在这里插入图片描述在确定文档的总高度时(包括基于视口的最小高度时),必须取得scrollwidth/clientwidth和scrollHeight/clientHeight中的最大值,才能保证在跨浏览器的环境下得到精确的结果。下面就是这样一个例子。

注意,对于运行在混杂模式下的IE,则需要用document . body代替document . documentElement。

1
2
3
4
5
var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);

console.log(docHeight);
console.log(docWidth);

通过scrollLeft和scrollTop属性既可以确定元素当前滚动的状态,也可以设置元素的滚动位置。在元素尚未被滚动时,这两个属性的值都等于0。如果元素被垂直滚动了,那么scrollTop的值会大于0,且表示元素上方不可见内容的像素高度。如果元素被水平滚动了,那么scrollLeft的值会大于0,且表示元素左侧不可见内容的像素宽度。这两个属性都是可以设置的,因此将元素的scrollLeft和scrollTop设置为0,就可以重置元素的滚动位置。下面这个函数会检测元素是否位于顶部,如果不是就将其回滚到顶部。
1
2
3
4
5
function scrollToTop(element) {
if (element.scrollTop != 0) {
element.scrollTop = 0;
}
}

关于滚动了一个练习连接地址


4、确定元素的大小

getBoundingClientRect() ,这个方法会返回一个矩形对象,包含4个属性:left、top、right、bottom。
这些属性给出了元素在页面中相对于视口的位置。但是每个浏览器实现都不一样。

写一个跨浏览器的函数:

注意:

  1. 由于使用了arguments.callee 所以这个方法不能再严格模式下使用。
  2. 但在某些情况下,这个函数返回的值可能会有所不同,例如使用表格布局或使用滚动元素情况下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function getBoundingClientRect(element) {
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;
if (element.getBoundingClientRect) {
if (typeof arguments.callee.offset != "number") {
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;left:0;top:0;";
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
};
} else {
var actualLeft = getElementLeft(element);
var actualTop = getElementTop(element);
return {
left: actualLeft - scrollLeft,
right: actualLeft + element.offsetWidth - scrollLeft,
top: actualTop - scrollTop,
bottom: actualTop + element.offsetHeight - scrollTop
}
}
}

遍历

“DOM2级遍历和范围”模块定义了两个用于辅助完成顺序遍历DOM结构的类型: NodeIterator和TreeWalker。这两个类型能够基于给定的起点对DOM结构执行深度优先( depth-first )的遍历操作。

DOM遍历是深度优先的DOM结构遍历,也就是说,移动的方向至少有两个(取决于使用的遍历类型)。遍历以给定节点为根,不可能向上超出DOM树的根节点。
在这里插入图片描述

任何节点都可以作为遍历的根节点。如果假设元素为根节点,那么遍历的第一步 就是访问<p>元素,然后再访问同为<body>元素后代的两个文本节点。不过,这次遍历永远不会到达<html><head>元素,也不会到达不属于<body>元素子树的任何节点。而以document为根节点的遍历则可以访问文档中的全部节点。

下图展示了对以document为根节点的DOM树进行深度优先遍历的先后顺序。NodeIterator 和TreeWalker都以这种方式遍历。
在这里插入图片描述

1、NodeIterator

使用document.createNodeIterator() 方法来创建它的实例。这个方法接收下列4个参数:

  • root:想要作为搜索起点的数中的节点。
  • whatToShow:表示想要访问哪些节点的数字代码。
  • filter:是一个NodeFilter对象,或者一个表示应该接受还是某种特定节点的函数。(类似节点过滤器的函数)
  • entityReferenceExpansion:布尔值表示是否扩展实体引用。这个参数在HTML页面中没有用,因为其中的实体引用不能扩展。

whatToShow参数是一个位掩码, 通过应用- -或多 个过滤器( filter )来确定要访问哪些节点。这个参数的值以常量形式在NodeFilter类型中定义,如下所示。

  • NodeFilter . SHOW_ ALL:显示所有类型的节点。
  • NodeFilter . SHOW_ ELEMENT:显示元素节点。
  • NodeFilter . SHOW_ ATTRIBUTE:显示特性节点。由于DOM结构原因,实际上不能使用这个值。
  • NodeFilter . SHOW_ TEXT:显示文本节点。
  • NodeFilter . SHOW_ CDATA_ SECTION: 显示CDATA节点。对HTML页面没有用。
  • NodeFilter . SHOw_ ENTITY_ REFERENCE:显示实体引用节点。对HTML页面没有用。
  • NodeFil ter . SHOW_ ENTITYE:显示实体节点。对HTML页面没有用。
  • NodeFil ter . SHOW_ PROCESSING_ INSTRUCTION:显示处理指令节点。对HTML页面没有用。
  • NodeFilter . SHOW_ COMMENT:显示注释节点。
  • NodeFilter . SHOW_ DOCUMENT: 显示文档节点。
  • NodeFilter . SHOW_ DOCUMENT_TYPE: 显示文档类型节点。
  • NodeFil ter . SHOW_ DOCUMENT_ FRAGMENT:显示文档片段节点。对HTML页面没有用。
  • NodeFilter . SHOW_ NOTATION:显示符号节点。对HTML页面没有用。

除了NodeFilter.SHOW_ ALL之外,可以使用按位或操作符来组合多个选项,

1
var whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;

可以通过createNodeIterator()方法的filter参数来指定自定义的NodeFilter对象,或者指定一个功能类似节点过滤器( node filter )的函数。
1
2
3
4
5
var filter = function(node) {
return node.tagName.toLowerCase() == "li"?
NodeFilter.FILTER_ACCEPT:
NodeFilter.FILTER_SKIP;
}

NodeIterator类型的两个主要方法是nextNode ()和previousNode()。顾名思义,在深度优先的DOM子树遍历中,nextNode ()方法用于向前前进一步,而previousNode ()用于向后后退一步。在刚刚创建的NodeIterator对象中,有一个内部指针指向根节点,因此第一次调用nextNode()会返回根节点。当遍历到DOM子树的最后一个节点时,nextNode() 返回null。previousNode()方法的工作机制类似。当遍历到DOM子树的最后一个节点,且previousNode()返回根节点之后,再次调用它就会返回null。


小例子:遍历指定节点中 <li>的元素,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var mylist = document.getElementById("mylist");  //获取我们遍历起点的节点。

var filter = function(node) {
return node.tagName.toLowerCase() == "li"?
NodeFilter.FILTER_ACCEPT:
NodeFilter.FILTER_SKIP;
}

var iterator = document.createNodeIterator(mylist, NodeFilter.SHOW_ELEMENT, filter, false);
var node = iterator.nextNode;
while (node !== null) {
console.log(node.tagName) //输出标签名
node = iterator.nextNode();
}

TrreWalker

TreeWalker是NodeIterator的一个更高级 的版本。除了包括 nextNode ()和previousNode ()在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历DOM结构的方法。

  • parentNode():遍历到当前节点的父节点;
  • firstChild():遍历到当前节点的第一个子节点;
  • lastChild():遍历到当前节点的最后一个子节点;
  • nextSibling():遍历到当前节点的下一个同辈节点;
  • previoussibling():遍历到当前节点的上一个同辈节点。

创建Treewalker对象要使用document. createTreewalker()方法,这个方法接受的4个参数与document . createNodeIterator ()方法相同:作为遍历起点的根节点、要显示的节点类型、过滤器和一个表示是否扩展实体引用的布尔值。由于这两个创建方法很相似,所以很容易用Treewalker 来代替NodeIterator,如下面的例子所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
var mylist = document.getElementById("mylist");
var filter = function(node) {
return node.tagName.toLowerCase() == "li" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
// NodeFilter.FILTER_REJECT;
}
var walker = document.createTreeWalker(mylist, NodeFilter.SHOW_ELEMENT, filter, false);
var node = walker.nextNode;
while (node !== null) {
console.log(node.tagName) //输出标签名
node = walker.nextNode();
}

在这里,filter可以返回的值有所不同。除了NodeFilter .FILTER_ACCEPT 和NodeFilter.FILTER_ SKIP之外,还可以使用NodeFilter . FILTER_REJECT。 在使用NodeIterator对象时,NodeFilter . FILTER_ SKIP与NodeFilter . FILTER_ REJECT 的作用相同:跳过指定的节点。但在使用TreeWalker对象时,NodeFilter . FILTER SKIP会跳过相应节点继续前进到子树中的下一个节点,而NodeFilter . FILTER REJECT 则会跳过相应节点及该节点的整个子树。例如,将前面例子中的NodeFilter . FILTER_ SKIP 修改成NodeFilter. FILTER_ REJECT, 结果就是不会访问任何节点。这是因为第一个返回的节点是<div>,它的标签名不是”li”,于是就会返回NodeFilter . FILTER REJECT ,这意味着遍历会跳过整个子树。在这个例子中,<div>元素是遍历的根节点,于是结果就会停止遍历。

当然,Treewalker真正强大的地方在于能够在DOM结构中沿任何方向移动。使用Treewalker遍历DOM树,即使不定义过滤器,也可以取得所有<li>元素,如下面的代码所示。(前提我们知道它的结构树)

1
2
3
4
5
6
7
8
9
10
11
var mylist = document.getElementById("mylist");
var walker = document.createTreeWalker(mylist, NodeFilter.SHOW_ELEMENT, null, false);

walker.firstChild(); //转到 <p>
walker.nextSibling() //转到<ul>

var node = walker.firstChild();
while (node !== null) {
console.log(node.tagName)
node = walker.nextSibling();
}

它的HTML结构

1
2
3
4
5
6
7
8
<div id="mylist">
<p>sdfsdf</p>
<ul>
<li id="li"></li>
<li></li>
<li></li>
</ul>
</div>

TreeWalker类型 还有一个属性,名叫 currentNode ,表示任何遍历方法在上一次遍历中返回的节点。通过设置这个属性也可以修改遍历继续进行起点,如下:

1
2
3
var node = walker.nextNode;
console.log(node === walker.currentNode); //ture
walker.currentNode = document.body; //修改起点

下面的例子会返回body中所有的 li 节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var mylist = document.getElementById("mylist");
var filter = function(node) {
return node.tagName.toLowerCase() == "li" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
// NodeFilter.FILTER_REJECT;
}
var walker = document.createTreeWalker(mylist, NodeFilter.SHOW_ELEMENT, filter, false);
var node = walker.nextNode;
console.log(node === walker.currentNode); //ture
walker.currentNode = document.body; //修改起点
while (node !== null) {
console.log(node.tagName)
node = walker.nextNode();
}


愿你的坚持终有收获。