DOM节点
- 文档本身就是一个文档节点,nodeType=9
- 所有html元素都是元素节点,nodeType=1
- 所有HTML属性都是属性节点,nodeType=2
- 插入到 HTML 元素文本是文本节点,nodeType=3
- 注释是注释节点,nodeType=8
元素element节点
特性:
1、nodeType=1
2、nodeName和tagName都是返回标签名的大写形式
3、nodeValue=null
4、父节点可能是document / 元素节点
5、子节点可能是元素节点、注释节点、文本节点
6、获取元素的子节点列表是:element.childNodes
7、获取元素的子元素列表是:element.children(只包含元素节点)
element.childNodes属性
<body>
<p id="demo">单击“按钮”获取有关身体元素的子节点的信息</p>
<button onclick="myFunction()">点我</button>
<script>
function myFunction(){
var txt="";
var c=document.body.childNodes;
for (i=0; i<c.length; i++){
txt=txt + c[i].nodeName + "<br>";
};
var x=document.getElementById("demo");
x.innerHTML=txt;
}
</script>
<p><strong>注意:</strong> 空格内元素看作是文本,文本是节点。</p>
</body>
结果:
#text
P
#text
BUTTON
#text
SCRIPT
#text
P
#text
element.children属性
<body>
<p>点击按钮获取 body 元素子元素的标签名。</p>
<button onclick="myFunction()">点我</button>
<p id="demo"></p>
<script>
function myFunction() {
var c = document.body.children;
var txt = "";
var i;
for (i = 0; i < c.length; i++) {
txt = txt + c[i].tagName + "<br>";
}
document.getElementById("demo").innerHTML = txt;
}
</script>
</body>
结果
P
BUTTON
P
SCRIPT
属性节点
特性:
1、nodeType=2
获取元素上的所有属性:element.attributes,返回的是一个集合,含length属性
获取元素上的某个属性:element.getAttribute('attributeName') / element.getAttributeNode('attributeName').value
获取元素上的某个属性节点:element.getAttributeNode('attributeName'),返回的是一个属性节点对象
判断元素有没有某个属性:element.hasAttribute(attributeName)
判断元素是否含有任何属性:element.hasAttributes()
设置或修改已有属性:element.setAttribute(attributeName, attributeValue)
移除节点的某个属性:element.removeAttribute(attributeName)
Text节点
特性:
1、nodeType=3
2、nodeName为#text
3、nodeValue为文本内容
4、父节点是元素节点
5、没有子节点
文档节点
特性:
1、nodeType=9
2、document.nodeName='#document'
3、父元素:document.parentElement=null
4、父节点:document.parentNode=null
5、nodeValue=null
6、document.documentElement.nodeName='HTML'
DocumentFragment文档碎片 -- 轻量级节点
特性
1、nodeType=11
2、nodeName的值为#document-fragment.
3、没有父节点
4、被作为一个轻量版的 Document 使用
5、不是真实DOM树的一部分,它的变化不会触发 DOM 树的(重新渲染) ,且不会导致性能等问题。
6、创建文档碎片:document.createDocumentFragment()
7、文档碎片的实例的属性都继承了Node 的属性
<body>
<ul id="ul"></ul>
</body>
<script>
(function(){
var ul=document.getElementById('ul');
var fragment=document.createDocumentFragment();
for(var i=0;i<5;i++){
var li=document.createElement('li');
li.textContent=`第${i+1}项`; //文本内容
/**
* 给li节点添加文本内容也可以写成:
* var text=document.createTextNode('我是文本节点');
* li.appendChild(text)
*/
fragment.appendChild(li)
}
ul.appendChild(fragment)
})
</script>
创建节点API
createElement(存在的标签名 / 自定义的标签名)
语法:document.createElement(存在的标签名 / 自定义的标签名);
例子:
var ele=document.createElement('div');
ele.id='div';
ele.style='color:red';
ele.textContent='新创建的节点';
document.body.appendChild(ele) //将节点添加到文档中
createTextNode(文本内容)
语法:document.createTextNode(文本内容");
用来创建文本节点,参数是文本节点的nodeValue
var node = document.createTextNode("我是文本节点");
document.body.appendChild(node);
cloneNode()
语法:var clone=node.cloneNode(isDeep)
node是要被克隆的节点,clone是克隆生成的副本节点,isDeep参数可选,表示是否采用深度克隆,如果为true,则node的所有后代节点也会被克隆,否则只会克隆node本身。
注意:
1、如果复制的节点含有id,则其副本同样会包含该id,由于id具有唯一性,所以在复制节点后必须要修改其id
2、如果复制的节点通过js的addEventListener或者onclick绑定事件,则副本节点不会绑定该事件
3、如果复制的节点是通过内联方式绑定事件:<div onclick="showParent()"></div>,这样的话,副本节点同样会触发事件。
4、cloneNode创建的节点只是游离有HTML文档外的节点,要调用appendChild方法才能添加到文档树中
例子:
<body>
<div id="parent">
我是父元素的文本
<br/>
<span>
我是子元素
</span>
</div>
<button id="btnCopy">复制</button>
</body>
<script>
var parent = document.getElementById("parent");
document.getElementById("btnCopy").onclick = function(){
var parent2 = parent.cloneNode(true);
parent2.id = "parent2";
document.body.appendChild(parent2);
}
</script>
修改页面节点API
appendChild()
语法:parent.appendChild(child)
说明:参数child将会作为父节点parent的最后一个子节点
注意:
如果被添加的节点已在页面存在,则会在原本所在的位置移除该节点,然后添加到新位置
如果child上绑定了事件,则被移动时,它依然绑定着该事件
insertBefore()
语法:parent.insertBefore(newNode,refNode)
newNode是要添加的节点,refNode是参照的节点,newNode将添加在refNode节点前面
注意:
refNode必传,否则出错
如果refNode传入null或undefined,则会将newNode插入父节点的子元素的末尾,相当于appendChild
removeChild()
语法:var deletedChild = parent.removeChild(node);
删除指定节点并返回,deletedChild指向被删除节点node的引用,
被删除的节点仍然存在于内存中,可以对其进行下一步操作。
如果node不是其子节点,则会报错
确保移除的是其子节点:
if(node.parentNode){
node.parentNode.removeChild(node);
}
replaceChild()
语法:parent.replaceChild(newChild,oldChild)
newChild是替换的节点,可以是新的节点,也可以是页面上的节点,如果是页面上的节点,则其将被转移到新的位置 oldChild是被替换的节点
修改页面节点API总结
这四个接口api
- 不管是新增还是替换节点,如果新增或替换的节点是原本存在页面上的,则其原来位置的节点将被移除,也就是说同一个节点不能存在于页面的多个位置
- 节点本身绑定的事件会不会消失,会一直保留着。
节点关系
父关系
- ele.parentNode:返回元素的父节点
- ele.parentElement:返回元素的父节点,该节点必须是元素类型,如果没有则返回null
子关系
- ele.childNodes:返回一个即时的NodeList,元素的子节点列表,可能包含文本节点、注释节点、元素节点
- ele.children:返回一个即时的HTMLCollection集合,非数组。子节点都是元素节点。
- ele.firstChild:返回第一个子节点,如果无子节点则返回null
- ele.lastChild:返回最后一个子节点
- ele.hasChildNodes:元素是否有子节点
兄弟关系
- ele.previousSibling:返回节点的前一个兄弟节点,可能会得到一个空白符的文本节点
- ele.previousElementSibling:前一个兄弟元素节点
- ele.nextSibling:返回节点的下一个兄弟节点,可能会得到一个空白符的文本节点
- ele.nextElementSibling:下一个兄弟元素节点
节点查询型API
document.getElementById()
语法: var element = document.getElementById(id);
getElementsByClassName()
语法: var elements = document.getElementsByClassName(names);
- elements是一个实时集合,类数组,包含了找到的所有元素
- 返回的集合是动态的,意味着它会自动更新来保持和DOM树的同步,而不用再次调用
- names是字符串,表示匹配的类目列表。多个类名以空格隔开
- getElementsByClassName()可以在任何元素调用,调用者将作为父节点,在其子节点中查找,前提是这个元素是一个对象而不是集合。
var elements = document.getElementById('main').getElementsByClassName('test');
//getElementById()获取到的是一个对象
- 可以对任意的HTMLCollection 使用Array.prototype的方法,调用时传递HTMLCollection 作为方法的参数。这里我们将查找到所有class为'test'的div元素:
var testElements = document.getElementsByClassName('test');
var testDivs = Array.prototype.filter.call(testElements, function(testElement){
return testElement.nodeName === 'DIV';;
});
getElementsByTagName()
语法:var elements = document.getElementsByTagName(name);
- elements:包括所有给定标签名称的元素的HTML集合HTMLCollection
- 返回的 HTML集合是动态的, 意味着它可以自动更新自己来保持和 DOM 树的同步而不用再次调用document.getElementsByTagName()
- 如果不存在指定的标签,该接口返回的不是null,而是一个空的HTMLCollection
- name是一个代表元素的名称的字符串。特殊字符 "*" 代表了所有元素。
- getElementsByTagName()和getElementsByClassName一样,可以被任何元素调用,前提是这个元素是一个对象而不是集合。
document.getElementsByName()
语法:var elements = document.getElementsByName(name)
注意:在HTML元素中,并不是所有元素都有name属性,比如div是没有name属性的,但是如果强制设置div的name属性,它也是可以被查找到的
document.querySelector()
语法:var element = document.querySelector(selectors);
document.querySelector返回第一个匹配到的元素,如果没有匹配的元素,则返回null- 采用深度优先搜索,在前面的优先被搜索到,即使是很深的级别
<body>
<div>
<div>
<span class="test">第三级的span</span>
</div>
</div>
<div class="test">
同级的第二个div
</div>
<input type="button" id="btnGet" value="获取test元素" />
</body>
<script>
document.getElementById("btnGet").addEventListener("click",function(){
var element = document.querySelector(".test");
alert(element.textContent); //第三级的span
})
</script>
document.querySelectorAll()
语法:var elementList = document.querySelectorAll(selectors);
- elementList是一个静态的NodeList类型的对象,即结果不会随着文档树的变化而变化
- selectors是一个由逗号连接的包含一个或多个CSS选择器的字符串
- querySelectorAll也是通过深度优先搜索,搜索的元素顺序和选择器的顺序无关
例子:
var matches = document.querySelectorAll("div.note, div.alert");
返回一个文档中所有的class为"note"或者"alert"的div元素
元素样式型
window.getComputedStyle()
语法:var CSSStyleDeclaration=window.getComputedStyle(ele [,pseudo-element])
- 第一个元素就是想要获取样式的目标元素
- 第二个元素不是必须的,当不查询伪类元素的时候可以忽略或者传入 null。(比如a:hover)
- IE8使用document.currentStyle
<body id="myid" class="mystyle">
<div id="elem-container">dummy</div>
<div id="output"></div>
<script>
function getTheStyle(){
let elem = document.getElementById("elem-container");
let theCSSprop = window.getComputedStyle(elem,null).height;//或者['height'],得到100px
document.getElementById("output").innerHTML = theCSSprop;
}
getTheStyle();
</script>
window.getComputedStyle()与element.style的区别
- getComputedStyle是函数类型
- getComputedStyle()获取的是指定元素的最终样式,包括内联样式、嵌入样式、外部样式
- style获取的是元素的内联样式,即在标签上的style属性
- getComputedStyle()是只读的,style返回的CSSStyleDeclaration对象是只读的,但style属性的属性能够用来设置样式。所以用getComputedStyle()来读取样式,通过 element.style.属性名 修改样式
直接修改元素样式
例子:
//设置一个样式
ele.style.color='red';
//在单个语句设置多个样式
ele.style.cssText='color:blue; font-size:50px'
//或者
ele.seAttribute('style','color:blue; font-size:50px')
动态添加样式规则
例子:
var style=document.createElement('style');
style.textContent="body{color:red} div{width:200px; height:200px; border:1px solid #000}"
document.head.appendChild(style)
getBoundingClientRect
语法: var clientRect = element.getBoundingClientRect();
用于获取某个元素相对于视窗的位置的集合,集合有top、left、right、bottom、width、height、x、y属性
- getBoundingClientRect().top:获取元素的上边距离视窗顶部的距离
- getBoundingClientRect().bottom:获取元素的下边距离视窗底部的距离
- getBoundingClientRect().left:获取元素的左边距离视窗的左边的距离
- getBoundingClientRect().right:获取元素的右边距离视窗的左边的距离
- getBoundingClientRect().width:获取元素的offsetWidth(width+左右padding+border),无单位
- getBoundingClientRect().height:获取元素的offsetHeight
- getBoundingClientRect().x:获取元素左上角的x坐标,无单位
- getBoundingClientRect().y:获取元素左上角的y坐标,无单位
offsetWidth、clientWidth、scrollWidth属性
元素的属性
element.clientWidth:width+左右padding
element.clientHeight:height+上下padding
element.offsetWidth:width+左右padding+左右border-width
element.offsetHeight:height+上下padding+上下border-width
element.offsetTop:当前元素的上边框外边缘到最近的有定位的父元素(除了static)的内壁的距离,即padding
element.offsetLeft:同理
element.clientTop:元素顶部边框的宽度,border-width
element.clientLeft:同理
element.scrollWidth:元素内容真实的宽度,内容不超出盒子高度时为盒子的clientWidth
element.scrollHeight:同理
Window的属性
window.innerHeight:获取浏览器可视区的高度,随着浏览器窗口大小的变化而变化
window.innerWidth:同理
document
document.documentElement.offsetWidth:整个文档的宽度(包含body的margin)
document.body.offsetWidth:整个文档的宽度(不包含body的margin)
都是无单位,通过element.style获取的就有单位
DOM中property和attributes的区别
<input type="text" id="input" self="name">
let dom=document.getElementById('input');
console.log(dom.self); //undefined
console.log(dom.attributes.self); //self='name'
console.log(typeof dom.attributes); //‘object’
console.log(dom.attributes.id); //id='input'
console.log(typeof dom.attributes.id); //"object"
- property:每一个DOM对象都会有它默认的基本属性,当创建dom对象时,就会含有默认的基本属性,比如input有id、class、value、type、attributes、nodeName、nodeType、className等基本属性,这些就是dom的property
- attributes:是dom对象的一个属性,是一个property,所以attributes是property的子集。它是对象类型,它保存了html标签里定义的属性,每个属性都是attr对象类型,当访问attributes中的属性时,返回 属性名=属性值
console.log(dom.value); //没有出错,只是返回空字符串
- 访问property:就算没有在html标签上定义的属性,也能访问
dom.setAttribute('id','new id');
console.log(dom.attributes.id); //id='new id'
console.log(dom.getAttribute('id')); //new id
- 访问attribute
- 通过attributes访问属性会得到attr类型的对象
- 通过getAttribute函数会得到属性值
dom.value='new val';
console.log(dom.value); //new val
console.log(dom.attributes.value); //value='val'
dom.id='dom';
console.log(dom.id); //dom
console.log(dom.attributes.id); //id='dom'
- 修改property:
- 对于input的value,修改后的值是无法同步到attributes的同名属性上
- 对于input的id,修改后的值能同步到attributes的同名属性上,即双向数据绑定
dom.attributes.value.value='new val' //修改的是value的属性值,也可以value.nodeValue
console.log(dom.value); //new val
dom.setAttribute('id','new id'); //也可以这样修改属性值
console.log(dom.id); //new id
*****注意不能这样修改attributes的属性******
dom.attributes.value='new val'
console.log(dom.attributes.value); //'new val'
console.log(dom.value); //val,没有变化
dom.attributes.id='new id'
console.log(dom.attributes.id); //'new id'
console.log(dom.id); //input,没有变化
- 修改attributes的属性
- 修改后的属性值会同步到同名的property,所以attribute --> property
总结:
- property指的是dom对象上的基本默认属性,包括attributes
- attributes是一个对象,保存了html标签上对于的属性
- attributes上的属性会同步到同名的property,反过来不一定
- 对于value、class是单项数据绑定,attribute --> property
- 对于id是双向数据绑定,property <--> attribute
- 资料