DOM

我们知道,JavaScript是由ECMAScript + DOM + BOM组成的。ECMAScript是JS中的一些语法,而BOM主要是浏览器对象(window)对象的一些相关知识的集合。而DOM,则是文档对象相关的知识的集合。

我们知道,HTML和JS之间的交互是通过事件实现的。而DOM是针对HTML(XML)文档的一个API。因此,如果我们想实现与用户的交互,那么就需要使用DOM提供的API,获取HTML元素,然后在该元素上绑定相应的事件,实现与用户的交互。所以,对DOM的理解和掌握就显得相当重要。

本文章主要基于《JavaScript高级程序设计(三)》中的DOM相关章节,对DOM的主要知识作出一个梳理,并穿插我个人的一些理解。

节点层次

写过HTML代码的地球人应该都知道,我们需要给每一个元素添加缩进,然后在书写相关的HTMl标签和内容,最后显示在网页上。因此这种嵌套的HTML代码和内容就构成了节点层次。

对ECMAScript理解的地球人应该都知道,JS中的每一个对象都是基于一个引用类型创建的,而引用类型可以是JS原生提供的引用类型(Array、Function、RegExp、Object等),也可以是自定义的引用类型(通过new关键字调用引用类型(也可以叫构造函数))。而所有对象都是Object的实例对象,都可以继承Object.prototype上的属性和方法

而在DOM中,也同样有这样类似的机制。在DOM中,最顶层的类型是Node类型,其他所有节点都可以继承Node类型下的属性和方法。而Node类型实际上就相当于JS中的Object构造函数。

既然如此,那就线看看Node类型下有哪些属性和方法

Node类型

  • 属性(在某个特定的节点通过继承的方式调用以下属性)

    • nodeType
    • nodeName
    • nodeValue
    • ·············
    • childNodes(指针,指向NodeList对象)
    • parentNode
    • nextSibling
    • previousSibling
    • firstChild
    • lastChild
    • ownDocument(每个节点都只能属于一个Document节点)
  • 方法(在某个特定的节点通过继承的方式调用以下方法)

    • ··· 查找节点 ···
    • 查找元素的方法位于Document类型中
    • ························
    • ··· 插入节点 ···
    • appendChild(ele)
    • insertBefore(ele, target): 如果target为null,则规则同appendChild
    • ························
    • ··· 删除节点 ···
    • removeChild(ele)
    • ························
    • ··· 替换节点 ···
    • replaceChild(ele, target)
    • ························
    • ··· 复制节点 ···
    • cloneNode(boolean) true: 表示深复制, false: 表示浅复制
    • ························
    • ··· 处理文档节点 ··· 很少用~
    • normalize()

Node类型上的属性和方法也就那么多了,再啰嗦一次,所有的其他节点都可以继承Node类型上的属性和方法

Document类型

JS通过Document类型表示文档。document对象是HTMLDocument的一个实例,表示整个HTML页面。同时,document对象也是window对象下的一个属性,因此可以将其作为全局对象来访问。

  • 属性

    • document.documentElement (表示HTML元素),同时可以通过document.childNodes[1]获取HTML元素
    • document.body (表示body元素)
    • document.head (表示head元素) ---HTML5新增
    • document.compatMode (表示浏览器采用哪种渲染方式,'CSS1Compat'表示标准模式, 'BackCompat'表示混杂模式) ---HTML5新增
    • document.charset (表示文档中实际使用的字符集,也可用来指定新字符集) ---HTML5新增
    • document.dataset (表示通过dataset访问自定义属性,如document.dataset.myname) ---HTML5新增
    • document.docType (表示 <!DOCTYPE>元素), 存在浏览器兼容性问题 ---HTML5新增
    • document.title (表示 < title > 元素)
    • ··· 网页请求 ···
    • document.URL (获取URL地址)
    • document.domain (获取URL中的域名,pathname)
    • document.attributes (获取某个节点的属性,返回NamedNodeMap对象,与NodeList类似)
    • ··· 焦点管理,无障碍性访问 ···
    • document.activeElement: 获取页面上获得焦点的元素,通过tab键或者focus函数获得焦点 ---HTML5新增
    • ··· 判断文档是否加载完成 ···
    • document.readyState:存在两个值,一是'loading',二是'complete'。如果document.readyState === 'complete',表明文档已经加载完成。即在DOMContentLoaded事件之后。 ---HTML5新增
  • 方法
    • ··· 查找元素 ···
    • document.getElementById(id) 返回该元素
    • document.getElementsByTagName(classname) 返回包含零个或多个元素的HTMLCollection对象,与NodeList对象相似
    • document.getElementsByName(ele)返回带有给定name属性的元素,同样返回HTMLCollection对象
    • document.getElementsByClassName(className) 返回所有匹配的NodeList对象 (可在Document类型、Element类型上调用该方法)
    • document.querySelector(selector) selector表示CSS选择符 返回与该模式匹配的第一个元素,如果没有找到,返回null (Document类型, DocumentFragment类型, Element类型都可以调用此方法)
    • document.querySelectorAll(selector) selector表示CSS选择符 返回一个匹配成功的NodeList对象 (Document类型, DocumentFragment类型, Element类型都可以调用此方法)
    • ··· 创建元素 ···
    • document.createElement() (创建好的元素处于游离状态,需要通过appendChild插入)
    • ··· 创建文本节点 ···
    • document.createTextNode() (创建好的元素处于游离状态,需要通过appendChild插入)
    • ··· 确定元素大小 ···
    • document.getBoundingClientRect()
    • ··· 焦点管理,无障碍性访问 ···
    • document.hasFocus():用于判断页面上是否获得焦点,获得返回true;否则返回false

Element类型

  • 属性

    • id
    • title
    • lang
    • className
  • 方法
    • getAttribute(ele) 获取某个属性
    • setAttribute(name, value) 设置某个属性
    • removeAttribute(ele) 移除某个属性
    • getElementsByTagName(ele) 获取标签名为ele的元素
插入标记

动态插入DOM节点有以下方法:使用诸如 createElement()和 appendChild()之类的DOM方法,以及使用innerHTML。对于小的DOM更改而言,两种方法效率都差不多。然而,对于大的DOM更改,使用innerHTML要比使用标准DOM方法创建同样的DOM结构快得多。当把innerHTML设置为某个值时,后台会创建一个HTML解析器,然后使用内部的DOM调用来创建DOM结构,而非基于JavaScript的DOM调用。由于内部方法是编译好的而非解释执行的,所以执行快得多。

在使用innerHTML, outerHTML, inertAdjacentHTML方法时,需要注意一点的是,由于这三个方法都是直接替换指定元素下的DOM节点,因此当被替换的元素存在事件处理程序时,被替换的事件处理程序并未被一同替换,而是仍然存在内存中。所以,在替换前,需要手动接触被替换元素的事件处理程序。这属于DOM方面的前端性能优化问题。

div.removeEventListener('click', fn, false)
或者是
div.onclick = null
  • 属性

    • innerHTML属性:用于替换当前元素下的所有子元素。如document.body.innertHTML = '< h1 > 哈哈哈 < /h1 >',此时就用h1标签替换掉了body元素下的所有子元素。IE8+以上浏览器支持
    • outerHTML属性:用于替换自身及其所有子元素。IE9+以上浏览器支持
  • 方法
    • insertAdjacentHTML方法:这个方法可以配合innerHTML属性一起使用,代替appendChild, insertBefore方法。IE6+以上的浏览器都支持这方法。此方法需要两个参数,第一个参数为给定的四个字符串。第二个参数是需要插入的DOM节点。使用方法非常简单。

      • 'beforebegin': 在当前元素前插入一个兄弟元素
      • 'beforeend': 在当前元素元素之下插入一个新的子元素或者在第一个子元素之前插入一个新的子元素
      • 'afterbegin': 在当前元素之下插入一个新的子元素或在最后一个子元素之后插入一个新的子元素
      • 'afterend': 在当前元素之后插入一个紧邻的同辈元素
<div class="inner">
<div class="child">1</div>
</div> let inner = document.querySelector('.inner')
let element = '< div class="element" >new element< /div >'
inner.insertAdjacentHTML('beforebegin', element) // 作为.inner前一个兄弟元素存在
inner.insertAdjacentHTML('beforeend', element) // 作为.inner第一个子元素
inner.insertAdjacentHTML('beforebegin', element) // 作为.inner后一个兄弟元素存在
inner.insertAdjacentHTML('beforebegin', element) // 作为.inner最后一个子元素存在
DOM元素节点遍历

在写HTML时,我们会用tab或者回车键来对各个标签进行排版布局。当我们使用DOM中的childNodes或者firstChild、nextSibling方法时,会获取到换行之后的空文本节点(nodeType === 3)。如

    <p>previous</p>
<div class="inner">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
</div>
<p>next</p> let div = document.querySelector('.inner')
console.log(div.childNodes)
// 返回 (9) [div.child, text, div.child, text, div.child, text, div.child, text, div.child]
// 这种返回值是不符合我们的预期的。

因此,存在以下方法可以用来获取元素节点,排除不必要的文本节点。这几种属性和方法是扩展在Element.prototype原型对象上的。支持的浏览器有IE 9+、Firefox 3.5+、 Safari 4+、Chrome ֖、Opera 10+。

Element.prototype.childElementCount // 子元素节点数量
Element.prototype.firstElementChild // 第一个子元素节点
Element.prototype.lastElementChild // 最后一个子元素节点
Element.prototype.previousElementSibling // 上一个兄弟元素节点
Element.prototype.previousElementSibling // 下一个兄弟元素节点 div.childElementCount // 5
div.firstElementChild // <div class="child">1</div>
div.firstChild // <div class="child">1</div>
div.lastElementChild // ... 5 ...
div.lastElementChild // ... 5 ...
div.previousElementSibling // <p>previous</p>
div.previousSibling // '' 注意区别 previousSibling返回空文本节点
div.nextElementSibling // <p>next</p>
div.nextSibling // '' 注意区别 nextSibling返回空文本节点

当然,也可以使用children属性来代替childNodes。children属性会返回子元素同样是元素节点的NodeList集合,而忽略注释和空白节点。即div.children.length === div.childElementCount

classList属性

HTML5在Element.prototype上新增了classList属性,用于对某个元素的类型进行操作。以往通常是是用className属性对类型进行操作,由于className返回字符串,所以想删除某个类名时需要进行一些繁琐的操作。

<div class="outer happy fun">...</div>
// 删除happy
var classNames = div.className.split(/\s+/);
var pos = -1,
i,
len;
for (i=0, len=classNames.length; i < len; i++){
if (classNames[i] == "user"){
pos = i;
break;
}
}
classNames.splice(i,1);
div.className = classNames.join(" ");

使用classList属性可以避免以上的操作。classList返回类数组对象,存在以下方法

  1. add(value): 增加类名
  2. remove(value): 移除类名
  3. contains(value): 是否包含类名,包含则返回true;否则返回false
  4. toggle(value): 如果存在value类,则删除该类名;如果不存在value类,则添加该类名

因此,可以使用classList属性简化上面的操作

let div = document.querySelector('.outer')
div.classList.remove('happy')
contains方法

此方法用于确认某个节点下是否包含另一个DOM节点,如果存在则返回true;否则返回false;需要传入一个DOM节点作为参数。IE6+以上浏览器都支持。

document.documentElement.contains(document.body) // true
document.body.contains(document.head) // false

Text类型

  • 属性

    • nodeValue | data (访问Text节点中的文本)

DocumentFragment类型

用途:离线操作DOM元素,避免DOM节点大量的重排和重绘,造成性能问题

  • 方法

    • document.createDocumentFragment() (表示创建文档片段)

NodeList对象

理解 NodeList 及其“近亲”NamedNodeMap 和 HTMLCollection,是从整体上透彻理解 DOM 的关键所在。这三个集合都是“动态的”;换句话说,每当文档结构发生变化时,它们都会得到更新。因此,它们始终都会保存着最新、最准确的信息。从本质上说,所有NodeList 对象都是在访问 DOM 文档时实时运行的查询。

元素大小

偏移量(offset dimension)

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

function getElementLeft(element){
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current !== null){
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
} function getElementTop(element){
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current !== null){
actualTop += current. offsetTop;
current = current.offsetParent;
}
return actualTop;
}

客户区大小(client dimension)

要确定浏览器视口大小,可以使用 document.documentElement 或 document.body(在IE7 之前的版本中)的clientWidth 和 clientHeight。

function getViewport(){
if (document.compatMode == "BackCompat"){
return {
width: document.body.clientWidth,
height: document.body.clientHeight
};
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
};
}
}

滚动大小(scroll dimension)

·················

确定元素大小

document.getBoundingClientRect()方法, 返回一个矩形对象。包含4个属性:left、top、right和bottom。这些属性给出了元素在页面中相对于视口的位置。

DOM知识梳理的更多相关文章

  1. Jmeter 接口测试知识梳理——持续集成篇

    Jmeter 使用也有很长时间了,但是一直没有做一下知识梳理,近期会对公司同事做一下这方面的培训,借此机会,把使用过程中应用到的知识,或是遇到的问题,整理出来,方便大家学习! Jmeter + Ant ...

  2. jQuery知识梳理20190817

    目录 jQuery知识梳理20190817 1. jQuery的特征 2. jQuery的两把利器 2.1 jQuery核心函数 2.2 jQuery核心对象 3. jQuery核心函数详解 4. j ...

  3. Vue.js 2.x API 知识梳理(一) 全局配置

    Vue.js 2.x API 知识梳理(一) 全局配置 Vue.config是一个对象,包含Vue的全局配置.可以在启动应用之前修改指定属性. 这里不是指的@vue/cli的vue.config.js ...

  4. Vue基础开发入门之简单语法知识梳理(思维导图详解)

    基于个人写的以下关于Vue框架基础学习的三篇随笔,在此基础上,做一个阶段性的知识总结,以此来检验自己对Vue这一段时间学习的成果,内容不多,但很值得一看.(思维导图详解)

  5. [SQL] SQL 基础知识梳理(一)- 数据库与 SQL

    SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 目录 What's 数据库 ...

  6. [SQL] SQL 基础知识梳理(二) - 查询基础

    SQL 基础知识梳理(二) - 查询基础 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5904824.html 序 这是<SQL 基础知识梳理( ...

  7. [SQL] SQL 基础知识梳理(三) - 聚合和排序

    SQL 基础知识梳理(三) - 聚合和排序 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5926689.html 序 这是<SQL 基础知识梳理 ...

  8. [SQL] SQL 基础知识梳理(四) - 数据更新

    SQL 基础知识梳理(四) - 数据更新 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5929786.html 序 这是<SQL 基础知识梳理( ...

  9. [SQL] SQL 基础知识梳理(五) - 复杂查询

    SQL 基础知识梳理(五) - 复杂查询 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5939796.html 序 这是<SQL 基础知识梳理( ...

随机推荐

  1. IEnumerable<T>和IQueryable<T>

    建议29.区别LINQ查询中的IEnumerable<T>和IQueryable<T> LINQ查询方法一共提供了两类扩展方法,在System.Linq命名空间下,有两个静态类 ...

  2. time模块简介

    Python 程序能用很多方式处理日期和时间,转换日期格式是一个常见的功能. Python 提供了一个 time 和 calendar 模块可以用于格式化日期和时间. 时间间隔是以秒为单位的浮点小数. ...

  3. python3.x中如何实现print不换行

    大家应该知道python中print之后是默认换行的, 那如何我们不想换行,且不想讲输出内容用一个print函数输出时,就需要改变print默认换行的属性, 方法如下: print('contents ...

  4. 使用font-size:0去掉inline-block元素之间的空隙

    现代浏览器的最新版都支持inline-block,只有该死的ie6.7不支持inline-block,但ie6.7可以通过 display:inline: zoom:1: 来模拟 下面是inline- ...

  5. angularjs 路由模块

    1. <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title& ...

  6. Linux 零拷贝技术

    简介 零拷贝(zero-copy)技术可以减少数据拷贝和共享总线操作的次数,消除通信数据在存储器之间不必要的中间拷贝过程,有效地提高通信效率,是设计高速接口通道.实现高速服务器和路由器的关键技术之一. ...

  7. linux下归档、解压缩工具:tar命令

    tar是一个类似于windows下的解压缩工具,可以将一大堆文件或目录打包成一个文件,还可以通过特定选项使用压缩工具进行解压缩. 语法: tar (选项) (参数) 常用选项: -c:创建打包文件. ...

  8. Git相关操作及记录

    一.软件 1.下载Git客户端软件 Widows平台: https://github.com/git-for-windows/git/releases/download/v2.13.0.windows ...

  9. Oracle之分组函数嵌套以及表连接

    --1 数据环境准备 scott 用户下面的emp,dept表 --2 要求 :求平均工资最高的部门编号,部门名称,部门平均工资 select d.deptno,d.dname,e.salfrom(s ...

  10. JEESZ分布式框架简介

    声明:该框架面向企业,是大型互联网分布式企业架构,后期会介绍Linux上部署高可用集群项目. 项目基础功能截图(自提供了最小部分) 介绍 1.      项目核心代码结构截图 <modules& ...