DOM模型
DOM:文檔對象模型( Document Object Model ),它的作用是將網(wǎng)頁轉(zhuǎn)為一個(gè) js 對象
節(jié)點(diǎn) Node
DOM的最小組成單位叫做節(jié)點(diǎn)
節(jié)點(diǎn)的類型有七種:
-
Document:整個(gè)文檔節(jié)點(diǎn)(包含整個(gè)節(jié)點(diǎn)樹) -
DocumentType:文檔聲明類型<!DOCTYPE html> -
Element:元素節(jié)點(diǎn),網(wǎng)頁的各種HTML標(biāo)簽(<body>、div、img等等) -
Attribute:網(wǎng)頁元素的屬性(比如class="right") -
Text:標(biāo)簽之間或標(biāo)簽包含的文本 -
Comment:注釋 -
DocumentFragment:文檔的片段
根節(jié)點(diǎn)
document 根節(jié)點(diǎn),文檔的第一層只有一個(gè)節(jié)點(diǎn)
節(jié)點(diǎn)的關(guān)系
除了根節(jié)點(diǎn),其他節(jié)點(diǎn)都有三種層級關(guān)系。
- 父節(jié)點(diǎn)關(guān)系
<!-- button的直接父節(jié)點(diǎn)是div -->
<div>
<button>按鈕</button>
</div>
- 子節(jié)點(diǎn)關(guān)系
<!-- div的直接子節(jié)點(diǎn)是button -->
<div>
<button>按鈕</button>
</div>
- 同級節(jié)點(diǎn)關(guān)系
<!-- button的同級節(jié)點(diǎn)是h2 -->
<div>
<button>按鈕</button>
<h2>標(biāo)題2</h2>
</div>
Node 接口的屬性
所有 DOM 節(jié)點(diǎn)都繼承了 Node 接口
-
Node.nodeType:
返回一個(gè)整數(shù)值,表示節(jié)點(diǎn)的類型,有以下類型:
- document:9 表示 文檔
- element: 1 表示 元素
- attr: 2 表示 屬性
- text: 3 表示 文本
- DocumentFragment: 11 表示 文檔片段
- DocumentType: 10 表示 文檔類型
- Comment: 8 表示 注釋
<div id="box">你好</div>
<script>
var box = document.getElementById('box');
console.log( box.nodeType ); //1 元素
console.log( box.childNodes[0].nodeType );//3 文本
</script>
-
Node.nodeName:
<div id="box">你好</div>
<script>
var box = document.getElementById('box');
console.log( box.nodeName ); //DIV 默認(rèn)是大寫
console.log( box.childNodes[0].nodeName );//#text
</script>
-
Node.nodeValue:
nodeValue屬性返回一個(gè)字符串,表示當(dāng)前節(jié)點(diǎn)本身的文本值
注意:該屬性主要用于文本節(jié)點(diǎn)
var box = document.getElementById('box');
console.log( box.nodeValue );//null
console.log( box.childNodes[0].nodeValue );//您好
-
Node.textContent:
textContent屬性返回當(dāng)前節(jié)點(diǎn)和它的所有后代節(jié)點(diǎn)的文本內(nèi)容。
自動(dòng)忽略當(dāng)前節(jié)點(diǎn)內(nèi)部的HTML標(biāo)簽,提取文本內(nèi)容,它和innerText功能一樣
該屬性可讀可寫
<div id="box">
您好
<p>
張三
</p>
</div>
<script>
var box = document.getElementById('box');
console.log( box.textContent );// 您好 張三
</script>
-
Node.baseURI:
返回一個(gè)字符串,表示當(dāng)前網(wǎng)頁的絕對路徑
會受<base href="#"> 基礎(chǔ)地址影響,而location.href不會。
console.log( document.baseURI );
//file:///F:/studyspace/web1905/javascript/dom.html
console.log( window.location.href );
//file:///F:/studyspace/web1905/javascript/dom.html
-
Node.ownerDocument:
返回當(dāng)前節(jié)點(diǎn)所在的頂層文檔對象,即document對象。
<div id="box">
</div>
<script>
var box = document.getElementById('box');
console.log( box.ownerDocument == document );//true
</script>
節(jié)點(diǎn)關(guān)系查找
父節(jié)點(diǎn)關(guān)系
-
Node.childNodes:父節(jié)點(diǎn)下所有的子節(jié)點(diǎn),注意包含文本、注釋、元素節(jié)點(diǎn)
-
Node.children:父節(jié)點(diǎn)下所有的子元素節(jié)點(diǎn),只是元素節(jié)點(diǎn)
<ul id="box">
<li>一分</li>
<li>二分</li>
<li>三分</li>
<li>四分</li>
<li>五分</li>
</ul>
<script>
var box = document.getElementById('box');
var lis1 = box.childNodes;
var lis2 = box.children;
console.log( lis1, lis2);
</script>
子節(jié)點(diǎn)關(guān)系
-
parentNode:當(dāng)前節(jié)點(diǎn)的直接的父節(jié)點(diǎn)
-
parentElement:當(dāng)前節(jié)點(diǎn)的直接的父元素節(jié)點(diǎn)
<div>
<span>
<button id="btn">按鈕</button>
</span>
</div>
<script>
document.getElementById('btn').onclick = function(){
var THIS = this;
while( THIS.parentElement ){
THIS = THIS.parentElement;
if(THIS.nodeName == 'BODY'){
THIS.style.backgroundColor='red';
}
}
}
</script>
同級節(jié)點(diǎn)關(guān)系
-
Node.nextSibling:下一個(gè)節(jié)點(diǎn),注意包含文本、注釋、元素節(jié)點(diǎn)
-
Node.nextElementSibling:下一個(gè)元素節(jié)點(diǎn),只是元素節(jié)點(diǎn)。
<button onclick="setBox(this)">按鈕</button>
<div></div>
<script>
//單擊按鈕,給div設(shè)置寬200 高200 背景顏色紅色
function setBox( btn ){
btn.nextElementSibling.style.cssText = `width:200px;height:200px;background-color:red`;
}
</script>
-
Node.previousSibling:上一個(gè)節(jié)點(diǎn),注意包含文本、注釋、元素節(jié)點(diǎn)
-
Node.previousElementSibling:上一個(gè)元素節(jié)點(diǎn),只是元素節(jié)點(diǎn)。
綜合的例子
<ul>
<li>一分</li>
<li>二分</li>
<li>三分</li>
<li>四分</li>
<li>五分</li>
</ul>
<script>
var lis = document.querySelectorAll('li');
lis.forEach( function( item ){
item.onclick = function(){
var THIS1 = this;
var THIS2 = this;
//查找當(dāng)前元素往上的所有元素
while( THIS1.previousElementSibling ){
THIS1 = THIS1.previousElementSibling;
THIS1.className = 'active'
}
//查找當(dāng)前元素往下的所有元素
while( THIS2.nextElementSibling ){
THIS2 = THIS2.nextElementSibling;
THIS2.className = 'active'
}
}
})
</script>
經(jīng)典面試題:請用至少3種辦法生成一個(gè)隨機(jī)不重復(fù)的字符串
// 方法1
var id = new Date().getTime();
console.log( id );
// 方法2
var str = Math.random().toString(36).slice(2);
console.log(str);
// 方法3
var str = 'abcdefghijkmnopqrstuvwxyzABCDEFGHIJKMNOPQRSTUVWXYZ0123456789';
var arr = str.split('');
var newArr = arr.sort(function(){
return .5 - Math.random();
}).slice(0,20);
console.log( newArr.join('') );
// 方法4
var str = 'abcdefghijkmnopqrstuvwxyzABCDEFGHIJKMNOPQRSTUVWXYZ0123456789';
var arr = str.split('');
var temp = '';
for(var i=0;i<arr.length;i++){
var index = Math.floor(Math.random()*arr.length);
temp += arr[index];
}
console.log( temp );
Node.isConnected
返回一個(gè)布爾值,表示當(dāng)前節(jié)點(diǎn)是否在文檔之中
<div id="box1"></div>
<script>
//文檔本身就存在
var box1 = document.getElementById('box');
console.log( box1.isConnected );//true
//創(chuàng)建了元素,但是并沒有插入到文檔中
var box2 = document.createElement('div');
console.log( box2.isConnected );//false
</script>
Node 接口的方法
document.createElement
在文檔中創(chuàng)建一個(gè)元素
語法:
document.createElement('標(biāo)簽名');
示例
var div = document.createElement('div');
console.log( div );//<div></div>
綜合示例
//1. 創(chuàng)建一個(gè)div元素
var div = document.createElement('div');
//2. 創(chuàng)建一個(gè)屬性節(jié)點(diǎn)
var cls = document.createAttribute('class');
//3. 設(shè)置屬性節(jié)點(diǎn)的值
cls.value = 'red';
//4. 把屬性節(jié)點(diǎn)應(yīng)用到目標(biāo)元素上
div.setAttributeNode( cls );
//5. 創(chuàng)建注釋
var zs = document.createComment('新的div');
//6. 將上面的注釋添加到div的內(nèi)部后面
div.appendChild( zs );
//7. 創(chuàng)建一個(gè)p標(biāo)簽
var p = document.createElement('p');
//8. 創(chuàng)建一個(gè)文本標(biāo)簽
var text = document.createTextNode('今天的天氣有點(diǎn)陰沉!');
//9. 將文本節(jié)點(diǎn)追加到p標(biāo)簽內(nèi)部的后面
p.appendChild( text );
//10. 將上面的p標(biāo)簽追加到div內(nèi)部后面
div.appendChild( p );
//11. 輸出新創(chuàng)建的節(jié)點(diǎn)
console.log( div );
使用JS標(biāo)準(zhǔn)API創(chuàng)建節(jié)點(diǎn)示例
<script>
var data = [
{id:1,name:'首頁',url:'#'},
{id:2,name:'關(guān)于我們',url:'#', active: true},
{id:3,name:'聯(lián)系我們',url:'#'},
];
var div = document.createElement('div');
var divClass = document.createAttribute('class');
divClass.value = 'nav';
var divStyle = document.createAttribute('style');
divStyle.value = 'width:100px;';
div.setAttributeNode( divClass );
div.setAttributeNode( divStyle );
var ul = document.createElement('ul');
for(var i=0;i<data.length;i++){
var li = document.createElement('li');
var a = document.createElement('a');
var aHref = document.createAttribute('href');
aHref.value = data[i].url;
a.setAttributeNode( aHref );
if( data[i].active ){
var aClass = document.createAttribute('class');
aClass.value = 'active';
a.setAttributeNode( aClass );
}
var text = document.createTextNode( data[i].name );
a.appendChild( text );
li.appendChild( a );
ul.appendChild( li );
}
div.appendChild( ul );
document.getElementsByTagName('body')[0].appendChild( div );
</script>
示例
<button onclick="createBox()">創(chuàng)建盒子并附加樣式</button>
<script>
function createBox(){
var div = document.createElement('div');
// var divClass = document.createAttribute('class');
// divClass.value ='box';
// div.setAttributeNode( divClass );
//等同于上面的寫法
div.className = 'box';
// var text = document.createTextNode('段落');
// div.appendChild( text );
//等同于上面的寫法
div.innerText = '段落';
var style = document.createElement('style');
style.innerHTML = `
.box{
width: 100px;
height: 100px;
background-color: red;
}`;
document.getElementsByTagName('head')[0].appendChild( style );
document.getElementsByTagName('body')[0].appendChild( div );
}
</script>
頁碼示例
<div id="box1"></div>
<div id="box2"></div>
<script>
makePage( 'box1', 10, 1 );
makePage( 'box2', 5, 2 );
function makePage( id, totalPage, page=1 ){
var target = document.getElementById( id );
target.innerHTML = '';
for(let p=1;p<=totalPage;p++){
var btn = document.createElement('button');
btn.innerText = p;
if(p==page){
btn.className = 'active';
}
//因?yàn)閯?chuàng)建的是dom對象,該對象尚未插入到文檔中,所以這里可以直接加上事件
btn.onclick = function(){
// console.log( p );
makePage( id, totalPage, p );
}
target.appendChild( btn );
}
}
</script>
Node.appendChild
往目標(biāo)節(jié)點(diǎn)內(nèi)部的后面插入新節(jié)點(diǎn),返回被插入的新節(jié)點(diǎn)
var div = document.createElement('div');
div.innerText = '內(nèi)容';
var text = document.createTextNode('段落');
div.appendChild( text );
console.log( div );// <div>內(nèi)容段落</div>
穿梭框效果
<style>
ul{
float: left;
list-style: none;
margin: 0;
padding: 0;
overflow: auto;
height: 200px;
width: 160px;
border: 1px solid #ccc;
margin-right: 20px;
}
.btns{
float: left;
padding-top: 22px;
width: 100px;
}
.btns button{
margin-bottom: 20px;
}
ul li{
line-height: 30px;
font-size: 14px;
padding: 0 10px;
cursor: pointer;
}
ul li:nth-child(even){
background-color: #eee;
}
ul li.active{
background-color: rgb(2, 141, 255);
color: white;
}
</style>
<h1>穿梭框效果</h1>
<ul id="left">
<li>陳旻</li>
<li>李杰</li>
<li>曾崇博</li>
<li>鄧聰</li>
<li>鐘旺</li>
<li>鄧紹志</li>
<li>劉江湖</li>
<li>代晨</li>
</ul>
<div class="btns">
<button onclick="addRightAll()"> 全部追加 </button>
<button onclick="addRight()"> 向右添加 </button>
<button onclick="addLeft()"> 向左添加 </button>
<button onclick="addLeftAll()"> 全部移除 </button>
</div>
<ul id="right">
</ul>
<button onclick="getVal()">取值</button>
<script>
var left = document.getElementById('left');
var leftLis = left.children;
var right = document.getElementById('right');
var rightLis = right.children;
//添加單擊選中事件
for(var i=0;i<leftLis.length;i++){
leftLis[i].onclick = function(){
this.classList.toggle('active');
}
}
//向右追加全部
function addRightAll(){
addItem( right, leftLis, 'all' );
}
//向左移除全部
function addLeftAll(){
addItem( left, rightLis, 'all' );
}
//向右追加
function addRight(){
addItem( right, leftLis );
}
//向左追加
function addLeft(){
addItem( left, rightLis );
}
//追加li元素
function addItem( target, lis, flag ){
//倒著刪除
for( var i = lis.length-1; i >= 0 ; i-- ){
if( lis[i].classList.contains('active') || flag == 'all' ){
lis[i].classList.remove('active');
target.appendChild( lis[i] );
}
}
}
//取值
function getVal(){
var val = '';
for( var i=0; i < rightLis.length; i++ ){
val += rightLis[i].innerText+',';
}
val = val.slice(0, -1);
alert( val );
}
</script>
代碼片段
<div id="box">
</div>
<button onclick="add()">添加</button>
<script>
var box = document.getElementById('box');
function add(){
var p = document.createElement('p');
var span = document.createElement('span');
var a = document.createElement('a');
//創(chuàng)建一個(gè)代碼片段節(jié)點(diǎn),代碼片段節(jié)點(diǎn)沒有名稱
var fragment = document.createDocumentFragment();
fragment.appendChild( p );
fragment.appendChild( span );
fragment.appendChild( a );
box.appendChild( fragment );
}
</script>
-
Node.hasChildNodes:
注意:節(jié)點(diǎn)包含7種類型
<div id="box">
</div>
<script>
var box = document.getElementById('box');
console.log( box.hasChildNodes() );//true 因?yàn)榘谋竟?jié)點(diǎn)
</script>
-
Node.cloneNode:
用于克隆一個(gè)節(jié)點(diǎn)。它接受一個(gè)布爾值作為參數(shù),表示是否同時(shí)克隆子節(jié)點(diǎn)。它的返回值是一個(gè)克隆出來的新節(jié)點(diǎn)。
注意事項(xiàng):
- 克隆一個(gè)節(jié)點(diǎn),會拷貝該節(jié)點(diǎn)的所有屬性,但是會喪失事件方法
- 該方法返回的節(jié)點(diǎn)不在文檔之中,即沒有任何父節(jié)點(diǎn),必須使用諸如
Node.appendChild這樣的方法添加到文檔之中。 - 克隆一個(gè)節(jié)點(diǎn)之后,
DOM有可能出現(xiàn)兩個(gè)有相同id屬性
<span id="span">
<b>文本</b>
</span>
<div id="div"></div>
<button onclick="copy()">復(fù)制文本</button>
<script>
function copy(){
var span = document.getElementById('span');
var div = document.getElementById('div');
//復(fù)制節(jié)點(diǎn),加true表示連同子節(jié)點(diǎn)一起復(fù)制
var _span = span.cloneNode( true );
div.appendChild( _span );
}
</script>
-
Node.insertBefore:
用于將某個(gè)節(jié)點(diǎn)插入父節(jié)點(diǎn)內(nèi)部的指定位置,給參考節(jié)點(diǎn)外部前面插入新節(jié)點(diǎn)。
語法:
父節(jié)點(diǎn).insertBefore( 新節(jié)點(diǎn), 參考節(jié)點(diǎn));
示例
<button onclick="before( this )">按鈕 </button>
<script>
function before( btn ){
var parent = btn.parentNode;
var p = document.createElement('p');
p.innerText= '文本';
parent.insertBefore( p, btn );
}
</script>