DOM模型
DOM:文档对象模型( Document Object Model ),它的作用是将网页转为一个 js
对象
节点 Node
DOM
的最小组成单位叫做节点
节点的类型有七种:
-
Document
:整个文档节点(包含整个节点树) -
DocumentType
:文档声明类型<!DOCTYPE html>
-
Element
:元素节点,网页的各种HTML标签(<body>
、div
、img
等等) -
Attribute
:网页元素的属性(比如class="right"
) -
Text
:标签之间或标签包含的文本 -
Comment
:注释 -
DocumentFragment
:文档的片段
根节点
document
根节点,文档的第一层只有一个节点
节点的关系
除了根节点,其他节点都有三种层级关系。
- 父节点关系
<!-- button的直接父节点是div -->
<div>
<button>按钮</button>
</div>
- 子节点关系
<!-- div的直接子节点是button -->
<div>
<button>按钮</button>
</div>
- 同级节点关系
<!-- button的同级节点是h2 -->
<div>
<button>按钮</button>
<h2>标题2</h2>
</div>
Node 接口的属性
所有 DOM
节点都继承了 Node
接口
-
Node.nodeType
:
返回一个整数值,表示节点的类型,有以下类型:
- 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 默认是大写
console.log( box.childNodes[0].nodeName );//#text
</script>
-
Node.nodeValue
:
nodeValue
属性返回一个字符串,表示当前节点本身的文本值
注意:该属性主要用于文本节点
var box = document.getElementById('box');
console.log( box.nodeValue );//null
console.log( box.childNodes[0].nodeValue );//您好
-
Node.textContent
:
textContent
属性返回当前节点和它的所有后代节点的文本内容。
自动忽略当前节点内部的HTML标签,提取文本内容,它和innerText功能一样
该属性可读可写
<div id="box">
您好
<p>
张三
</p>
</div>
<script>
var box = document.getElementById('box');
console.log( box.textContent );// 您好 张三
</script>
-
Node.baseURI
:
返回一个字符串,表示当前网页的绝对路径
会受<base href="#">
基础地址影响,而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
:
返回当前节点所在的顶层文档对象,即document
对象。
<div id="box">
</div>
<script>
var box = document.getElementById('box');
console.log( box.ownerDocument == document );//true
</script>
节点关系查找
父节点关系
-
Node.childNodes
:父节点下所有的子节点,注意包含文本、注释、元素节点
-
Node.children
:父节点下所有的子元素节点,只是元素节点
<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>
子节点关系
-
parentNode
:当前节点的直接的父节点
-
parentElement
:当前节点的直接的父元素节点
<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>
同级节点关系
-
Node.nextSibling
:下一个节点,注意包含文本、注释、元素节点
-
Node.nextElementSibling
:下一个元素节点,只是元素节点。
<button onclick="setBox(this)">按钮</button>
<div></div>
<script>
//单击按钮,给div设置宽200 高200 背景颜色红色
function setBox( btn ){
btn.nextElementSibling.style.cssText = `width:200px;height:200px;background-color:red`;
}
</script>
-
Node.previousSibling
:上一个节点,注意包含文本、注释、元素节点
-
Node.previousElementSibling
:上一个元素节点,只是元素节点。
综合的例子
<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;
//查找当前元素往上的所有元素
while( THIS1.previousElementSibling ){
THIS1 = THIS1.previousElementSibling;
THIS1.className = 'active'
}
//查找当前元素往下的所有元素
while( THIS2.nextElementSibling ){
THIS2 = THIS2.nextElementSibling;
THIS2.className = 'active'
}
}
})
</script>
经典面试题:请用至少3种办法生成一个随机不重复的字符串
// 方法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
返回一个布尔值,表示当前节点是否在文档之中
<div id="box1"></div>
<script>
//文档本身就存在
var box1 = document.getElementById('box');
console.log( box1.isConnected );//true
//创建了元素,但是并没有插入到文档中
var box2 = document.createElement('div');
console.log( box2.isConnected );//false
</script>
Node 接口的方法
document.createElement
在文档中创建一个元素
语法:
document.createElement('标签名');
示例
var div = document.createElement('div');
console.log( div );//<div></div>
综合示例
//1. 创建一个div元素
var div = document.createElement('div');
//2. 创建一个属性节点
var cls = document.createAttribute('class');
//3. 设置属性节点的值
cls.value = 'red';
//4. 把属性节点应用到目标元素上
div.setAttributeNode( cls );
//5. 创建注释
var zs = document.createComment('新的div');
//6. 将上面的注释添加到div的内部后面
div.appendChild( zs );
//7. 创建一个p标签
var p = document.createElement('p');
//8. 创建一个文本标签
var text = document.createTextNode('今天的天气有点阴沉!');
//9. 将文本节点追加到p标签内部的后面
p.appendChild( text );
//10. 将上面的p标签追加到div内部后面
div.appendChild( p );
//11. 输出新创建的节点
console.log( div );
使用JS标准API创建节点示例
<script>
var data = [
{id:1,name:'首页',url:'#'},
{id:2,name:'关于我们',url:'#', active: true},
{id:3,name:'联系我们',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()">创建盒子并附加样式</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';
}
//因为创建的是dom对象,该对象尚未插入到文档中,所以这里可以直接加上事件
btn.onclick = function(){
// console.log( p );
makePage( id, totalPage, p );
}
target.appendChild( btn );
}
}
</script>
Node.appendChild
往目标节点内部的后面插入新节点,返回被插入的新节点
var div = document.createElement('div');
div.innerText = '内容';
var text = document.createTextNode('段落');
div.appendChild( text );
console.log( div );// <div>内容段落</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');
//创建一个代码片段节点,代码片段节点没有名称
var fragment = document.createDocumentFragment();
fragment.appendChild( p );
fragment.appendChild( span );
fragment.appendChild( a );
box.appendChild( fragment );
}
</script>
-
Node.hasChildNodes
:
注意:节点包含7种类型
<div id="box">
</div>
<script>
var box = document.getElementById('box');
console.log( box.hasChildNodes() );//true 因为包含文本节点
</script>
-
Node.cloneNode
:
用于克隆一个节点。它接受一个布尔值作为参数,表示是否同时克隆子节点。它的返回值是一个克隆出来的新节点。
注意事项:
- 克隆一个节点,会拷贝该节点的所有属性,但是会丧失事件方法
- 该方法返回的节点不在文档之中,即没有任何父节点,必须使用诸如
Node.appendChild
这样的方法添加到文档之中。 - 克隆一个节点之后,
DOM
有可能出现两个有相同id
属性
<span id="span">
<b>文本</b>
</span>
<div id="div"></div>
<button onclick="copy()">复制文本</button>
<script>
function copy(){
var span = document.getElementById('span');
var div = document.getElementById('div');
//复制节点,加true表示连同子节点一起复制
var _span = span.cloneNode( true );
div.appendChild( _span );
}
</script>
-
Node.insertBefore
:
用于将某个节点插入父节点内部的指定位置,给参考节点外部前面插入新节点。
语法:
父节点.insertBefore( 新节点, 参考节点);
示例
<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>