Node(节点)
1.Node概述
【DOM】文档对象模型
基本思想:
(1)把结构文档解析成一系列节点,再由这些节点组成一个树状结构;
(2)均有规范的对外结构,以便于编程(操作)文档(比如增删内容)。
ps:严格地说,DOM不属于JavaScript。
pss:但是操作DOM是JavaScript最常见的任务。
psss:而JavaScript也是最常用于DOM操作的语言。
pssss:所以,DOM往往放在JavaScript里面介绍。
【Node】DOM的最小单位
对于HTML文档,Node主要有以下6中类型:
节点 名称 含义
Document 文档node 整个文档(window.document)
DocumentType 文档类型node 文档的类型(比如:<DOCTYPE html>)
Element 元素node HTML元素
Attribute 属性node HTML属性
Text 文本node HTML文档中出现的文本
DocumentFragment 文档碎片node 文档碎片
思考:节点集合是不是一个数组???
2.【Node属性】
(1)通用属性:nodeName,nodeType
nodeName属性返回节点的名称,
nodeType属性返回节点的常数值
类型 nodeName nodeType
ELEMENT_NODE 大写的HTML元素名 1
ATTRIBUTE_NODE 等同于Attr.name 2
TEXT_NODE #text 3
其它现用现查
(2)返回当前node的相关节点属性:ownerDocument、nextSibling,previousSibling,parentNode,parentElement
【ownerDocument】
ownerDocument属性返回当前节点所在的顶层文档对象,即document对象。
ps:document对象本身的ownerDocument属性,返回null。
思考:若是属性,文本节点呢?(均返回顶层Document对象)
【nextSibling】
nextSibling属性返回紧跟在当前节点后面的第一个同级节点。
如果当前节点后面没有同级节点,则返回null
ps:从上述代码中能够得到的是,该属性还包括文本节点和评论节点。因此如果当前节点后面有空格,该属性会返回一个文本节点,内容为空格。
【previousSibling】
previousSibling属性返回当前节点前面的第一个同级节点。
如果当前节点前面没有同级节点,则返回null。
ps:对于当前节点前面有空格,则previoussibling属性会返回一个内容为空格的文本节点。
pss:回车也算是文本节点
【parentNode】
parentNode属性返回当前节点的父节点。
如果当前节点没有父节点,则返回null(document节点)。
ps:一般情况下,一个节点的父节点只可能是三种类型:element节点、document节点和documentfragment节点。
【parentElement】
parentElement属性返回当前节点的父元素节点。
如果当前节点没有父节点,或者父节点类型不是Element节点,则返回null
(3)返回当前node的内容的属性:textContent,nodeValue
【textContent】
textContent属性返回当前节点和它的所有后代节点的文本内容。
【nodeValue】
nodeValue属性返回或设置当前节点的值,格式为字符串。
但是nodeValue只对Text节点(、Comment节点、XML文档的CDATA节点)有效,其他类型的节点一律返回null。
(4)返回当前节点的子节点的属性:childNodes,firstChild,lastChild
【childNodes】
childNodes属性返回一个节点集合(NodeList),节点集合中包括当前节点的所有子节点。
【注】
①如果当前节点不包括任何子节点,则返回一个空的NodeList集合。
②由于NodeList对象是一个动态集合,一旦子节点发生变化,立刻会反映在返回结果之中。
【firstChild】
firstChild属性返回当前节点的第一个子节点,如果当前节点没有子节点,则返回null。
需要指出的是,除了HTML元素子节点,该属性还包括文本节点和评论节点。
【lastChild】
lastChild属性返回当前节点的最后一个子节点,如果当前节点没有子节点,则返回null。
3.Node的方法
(1) appendChild(),hasChildNodes()
【appendChild()】
appendChild()方法接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点
【hasChildNodes()】
hasChildNodes()方法返回一个布尔值,表示当前节点是否有子节点。
(2) cloneNode(),insertBefore(),removeChild(),replaceChild()
【childNodes()】
cloneNode()方法用于克隆一个节点。它接受一个布尔值作为参数,表示是否同时克隆子节点。默认是false,即不克隆子节点。
【注】需要注意的是,克隆一个节点,会拷贝该节点的所有属性,但是会丧失addEventListener方法和on-属性(即node.onclick = fn)添加在这个节点上的事件回调函数。
【insertBefore()】
insertBefore方法用于将某个节点插入当前节点的指定位置。
它接受两个参数,第一个参数是所要插入的节点,第二个参数是当前节点的一个子节点,新的节点将插在这个节点的前面。
该方法返回被插入的新节点,根据情况不是必须创建变量来保存。
var text1 = document.createTextNode('1');
var li = document.createElement('li');
li.appendChild(text1);
var ul = document.querySelector('ul');
ul.insertBefore(li,ul.firstChild);
上述代码的作用是,将带有文字节点的li插入到ul的第一个子节点之前。
【removeChild()】
removeChild方法接受一个子节点作为参数,用于从当前节点移除该节点。
它返回被移除的节点,根据情况不是必须创建变量来保存。
【replaceChild()】
replaceChild方法用于将一个新的节点,替换当前节点的某一个子节点。
它接受两个参数,第一个参数是用来替换的新节点,第二个参数将要被替换走的子节点。它返回被替换走的那个节点。
var replacedNode = parentNode.replaceChild(newChild, oldChild);
例子:
var divA = document.getElementById('A');
var newSpan = document.createElement('span');
newSpan.textContent = 'Hello World!';
divA.parentNode.replaceChild(newSpan,divA);
上述代码是将界面中的divA替换成了代码创建的span标签。
(3) contains(),isEqualNode()
【contains()】
contains方法接受一个节点作为参数,返回一个布尔值。
contains方法表示判断【参数节点】是否为【当前节点】的子节点。
ps:document.body.contains(node) 检查某个节点,是否包含在当前文档之中。
pss:nodeA.contains(nodeA) // true 节点自身包含自身的结果是真的。虽然理论上很难解释
【isEqualNode()】
isEqualNode方法返回一个布尔值,用于检查两个节点是否相等。
ps:所谓相等的节点,指的是两个节点的类型相同、属性相同、子节点相同。
4、HTML元素
html元素是网页的根元素,document.documentElement就指向这个元素。js中关于html元素也提供了一些相关的属性和方法来帮助我们更好的操作:
(1)视图窗口大小:clientWidth属性,clientHeight属性
(2)html元素大小:offsetWidth属性,offsetHeight属性
ps:clientWidth和clientHeight这两个属性返回的是视口(viewport)的大小,单位为像素。
pss:clientWidth和clientHeight在计算视图大小的时候不计算滚动条部分。
psss:window.innerWidth和window.innerHeight包括了滚动条的高度和宽度。
5.HTML内元素
(1) 元素位置相关属性
【offsetParent】
offsetParent表示获取距离【当前元素】的最靠近的、并且CSS的position属性不等于static的父元素。
若找不到返回body
【offsetTop、offsetLeft】
offsetTop:当前HTML元素左上角相对于offsetParent的垂直位移。
offsetLeft:当前HTML元素左上角相对于offsetParent的水平位移。
(2) 获取元素对象的方法
大致分为四种:
querySelector方法
querySelectorAll方法
getElementsByTagName方法
getElementsByClassName方法
【注意:querySelector系列方法与getElementsBy系列方法对比有什么不同?】
(i)两者的W3C标准不同
querySelector系列属于W3C中的Selectors API规范
getElementsBy系列则属于 W3C的DOM 规范。
(ii)两者浏览器的兼容不同
querySelector系列基本能被所有浏览器支持。
getElementsBy系列则通常只有在考虑兼容性的时候才被提起(尽管两者功能近似)
(iii)接受参数不同
querySelector系列接收的参数是一个css选择器名。
getElementsBy系列接收的参数只能是单一的className、tagName 和 name。
var c1 = document.querySelectorAll('.k1 .u');
var c2 = document.getElementsByClassName('p');
var c3 = document.getElementsByClassName('b2')[0].getElementsByClassName('d');
需要注意的是,querySelector系列所接收的参数是必须严格符合css选择器规范的。所以下面这种写法将会出错。
var e1 = document.getElementsByClassName('1a2b3c');
var e2 = document.querySelectorAll('.1a2b3c');
console.log(e1 && e1[0].className);
console.log(e2 && e2[0].className);
(iv)返回值不同
querySelectorAll()返回的是一个静态节点列表(Static NodeList)
getElementsBy系列的返回的是一个动态节点列表(Live NodeList)。
//查看下面两个经典案例
//案例1
var ul = document.querySelectorAll('ul')[0],
var list = ul.querySelectorAll("li");
for(var i = 0; i < list.length ; i++){
ul.appendChild(document.createElement("li"));
}
//案例2
var ul = document.getElementsByTagName('ul')[0],
var list = ul.getElementsByTagName("li");
for(var i = 0; i < list.length ; i++){
ul.appendChild(document.createElement("li"));
}
结论是:
案例2中的list是一个动态的NodeList,每一次调用list都会重新对文档进行查询,导致无限循环的问题。
案例1中的list是一个静态的Node List,是一个li集合的快照,对文档的任何操作都不会对其产生影响