DOM: Document Object Model(文档对象模型)
DOM树:
回流和重绘
重绘(repaint):
当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此损耗较少。
常见的重绘操作有:
改变元素颜色
改变元素背景色
more ……
回流(reflow):
又叫重排(layout)。当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。
常见的回流操作有:
页面初次渲染
浏览器窗口大小改变
元素尺寸/位置/内容发生改变
元素字体大小变化
添加或者删除可见的 DOM 元素
……
重点:回流必定会触发重绘,重绘不一定会触发回流。重绘的开销较小,回流的代价较高。
DOM 操作
1、获取元素
1.1 根据id名获取
document.getElementById(idName)
1.2 根据标签名获取
document.getElementsByTagName(tagName) // 返回一个集合 (类数组对象) 从整个文档获取
element.getElementsByTagName(tagName) // 从element的后代元素中获取
1.3 根据类名获取(不兼容ie6~8)
document.getElementsByClassName(className) // 返回一个集合(类数组对象) 从整个文档获取
element.getElementsByClassName(className) // 从element的后代中获取
1.4 根据 name 属性值获取
正常的规范中,咱们只会给表单元素起name值,如果给其它元素设置name,在ie9以下版本浏览器不兼容,是获取不到的,
所以为了这种情况,咱们以后养成习惯,只给表单元素用name,非表单元素不用name
document.getElementsByName() //返回集合 只有document才有该方法
1.5 根据选择器获取
注意:querySelector 和querySelectorAll 在ie6-ie8 下不兼容
document.querySelector(选择器) //选择器第一个满足选择器条件的
document.querySelectorAll(选择器) //选择所有满足选择器条件的,返回nodeList(类数组对象)
element.querySelector(选择器)
element.querySelectorAll(选择器)
1.6 document.head
获取Head元素对象
1.7 document.body
获取body元素对象
1.8 document.documentElement
获取html元素对象
2、获取一屏幕的宽度或者高度,兼容所有的浏览器
// 获取一屏幕的高度
varvH=document.documentElement.clientHeight||document.body.clientHeight;
// 偶去一屏幕的宽度
varvW=document.documentElement.clientWidth||document.body.clientWidth;
3、id小妙招 直接把id当成变量去用的时候,可以获取相应的id元素。(浏览器的机制)
<script>
console.log(box1)
</script>
2、节点
●文档节点●属性节点●元素节点●文本节点●注释节点
2.1 文档节点
●nodeType(节点类型):9●nodeName(节点名称):"#document"●nodeValue(节点值):null
document.nodeType;
document.nodeName
document.nodeValue;
2.2 属性节点
●nodeType:2●nodeName:属性名●nodeValue:属性值getAttributeNode() 方法从当前元素中通过名称获取属性节点。
vara1=a1.getAttributeNode("href");// 获取属性节点
console.log(a1.nodeType);//2
console.log(a1.nodeName);//"href"; 属性名
console.log(a1.nodeValue);//"http://www.baidu.com"; 属性值
2.3 元素节点
●nodeType:1●nodeName:大写的标签名●nodeValue:null
<script>
console.log(box.nodeName)
</script>
2.4 文本节点
●nodeType:3●nodeName:"#text"●nodeValue:文本内容●在标准浏览器中,换行和空格也属于文本节点
varres=a1.childNodes[0];
console.log(res.nodeType);//3
console.log(res.nodeValue);//"百度";
console.log(res.nodeName);//"#text";
2.5 注释节点
●nodeType:8●nodeName:"#comment"●nodeValue:注释的内容
<!--a标签-->
百度
</a>
a1.childNodes;//NodeList(3) [text, comment, text]
a1.childNodes[1].nodeName;//"#comment"
a1.childNodes[1].nodeType;//8
a1.childNodes[1].nodeValue;//"a标签 "
3、节点之间关系的属性
节点类:
parentNode 父节点
childNodes 所有子节点的集合
firstChild 第一个子节点
lastChild 最后一个子节点
previousSibling 上一个兄弟节点
nextSibling 下一个兄弟节点
元素类:
children 所有子元素的集合
firstElementChild 第一个子元素 IE9+
lastElementChild 最后一个子元素 IE9+
previousElementSibling 上一个兄弟元素 IE9+
nextElementSibling 下一个兄弟元素 IE9+
3.1 parentNode获取当前节点唯一的父亲节点
<div>
<!--111-->
<div id="box">111</div>
</div>
</div>
<script>
console.log(box.parentNode)
</script>
3.2 childNodes获取当前节点所有的子节点
<!--我是注释-->
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
</ul>
<script>
console.log(main.childNodes);//NodeList(11) [text, comment, text, li, text, li, text, li, text, li, text]
3.3 firstChild获取被选节点的第一个子节点。
<!--我是注释-->
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
</ul>
<script>
console.log(main.firstChild);//#text
3.4 lastChild获取被选节点的最后一个子节点。
<!--我是注释-->
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
</ul>
<script>
console.log(main.lastChild);//#text
3.5 previousSibling获取上一个哥哥节点
<!--我是注释-->
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
</ul>
<script>
console.log(main.previousSibling);//#text
3.6 nextSibling获取当前节点的下一个兄弟节点
<!--我是注释-->
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
</ul><!-- 1111 -->
<script>
console.log(main.nextSibling);//<!-- 1111 -->
3.7 children获取当前元素所有的元素子节点,但是在ie6--ie8下不兼容3.8 firstElementChild获取当前节点的第一个元素子节点 ie6-ie8 不兼容
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
</ul>
<script>
console.log(main.firstElementChild);
3.9 lastElementChild获取当前节点的最后一个元素子节点 ie6-ie8 不兼容
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!111</li>
</ul>
<script>
console.log(main.lastElementChild);
3.10 previousElementSibling获取上一个哥哥元素节点 ie6-ie8 不兼容<ul id="main">
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!111</li>
</ul>
<script>
console.log(main.previousElementSibling);
3.11 nextElementSibling获取当前节点的下一个兄弟元素节点 ie6-ie8 不兼容
<ul id="main">
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!111</li>
</ul>
<script>
console.log(main.nextElementSibling);
练习题
自己手动封装一个获取节点下面的所有子元素,要求考虑兼容性。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible"content="ie=edge">
<title>Document</title>
<style>
</style>
</head>
<body>
<span id="span1">1</span>
<ul id="main">
<!--我是注释-->
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
</ul>
<span>2</span>
</body>
</html>
<script>
functionchildren(element){
varnodeLists=element.childNodes;
varresult=[]
for(vari=0;i<nodeLists.length;i++){
nodeLists[i].nodeType===1?result.push(nodeLists[i]):null;
}
returnresult;
}
console.log(children(main));
自己手动封装一个previousElementSibling,要兼容。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible"content="ie=edge">
<title>Document</title>
<style>
</style>
</head>
<body>
<span id="span1">1</span>
<ul id="main">
<!--我是注释-->
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
<li>选择珠峰的,都是明智的!</li>
</ul>
<span id="span2">2</span>
</body>
</html>
<script>
functionpreviousElmentSibling(ele){
varpre=ele.previousSibling;
while(pre&&pre.nodeType!==1){
pre=pre.previousSibling;
}
returnpre;
}
console.log(previousElmentSibling(span2))
</script>
DOM 的增删改
1、createElement:创建一个元素
document.createElement("div");
2、createTextNode: 创建一个文本节点
var otext=document.createTextNode("哈哈!");
3、appendChild:把元素追加到一个容器的末尾
语法:[context].appendChild([元素]);
<div id="id1"></div>
<script>
var odiv = document.createElement("div");
id1.appendChild(odiv);
console.log(id1)
</script>
4、insertBefore: 把一个元素插入到另一个元素的前面
首先要指定一个父节点
var insertedNode = 父节点.insertBefore(要插入的节点, 插在这个节点之前)
var insertedNode = parentNode.insertBefore(newNode, referenceNode)
newNode:将要插入的节点
referenceNode:被参照的节点(即要插在该节点之前)
insertedNode:插入后的节点
parentNode:父节点
<div id="id1">
<p class="p1" id="p1">这是P1</p>
</div>
<script>
var div = document.getElementById('id1');
var p1 = document.getElementById('p1');
var odiv = document.createElement("div");
var returnDom = div.insertBefore(odiv, p1);
console.log(div)
</script>
5、cloneNode:把某一个节点进行克隆
【ele】.cloneNode();浅克隆: 只是克隆了一个节点,里面的内容还有样式都没克隆
【ele】.cloneNode(true);深克隆:把节点包含的所有内容进行克隆
<div id="id1">
<p class="p1" id="p1">这是P1</p>
</div>
<script>
var res = id1.cloneNode();
var res2 = id1.cloneNode(true)
console.log(res)
console.log(res2)
</script>
6、removeChild:移除某个节点
【context】.removeChild(ele);
<div id="id1">
1111
<p class="p1" id="p1">这是P1</p>
</div>
<script>
id1.removeChild(p1);
console.log(id1)
</script>
7、set/get/removeAttribute
设置/获取/删除 当前元素的某一个自定义属性
setAttribute
getAttribute
removeAttribute
box.setAttribute("index", 1);
box.getAttribute("index");
box.removeAttribute("index");
console.log(box)
// 设置
// box["aa"] = 22;
// 获取
// box["aa"]
//移除
// delete box["aa"];
基于键值对方式 增删改:修改当前对象的堆内存空间完成的(在堆内存空间可以看到)
基于Attribute dom方法增删改,是修改html结构来完成的(此种方法设置的在结构上可以看到)
以上两种方式不能混用
题目
利用a标签的href来重新获取url参数
var str = "http://www.zhufengpeixun.cn?name=lili&age=18#123"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var str = "http://www.zhufengpeixun.cn?name=lili&age=18#123"
/*
search: "?name=lili&age=18"
hash: "#123"
*/
function queryParams(str) {
var a = document.createElement("a");
a.href = str;
var obj = {};
console.log(a.search); //?name=lili&age=18
console.log(a.hash); //#123
//[name=lili,age=18]
var search = a.search.substr(1);
console.log(search);//name=lili&age=18
obj.hash = a.hash ? a.hash : null;
if (search) {
var searchAry = search.split("&");
console.log(searchAry)// ['name=lili', 'age=18']
for (var i = 0; i < searchAry.length; i++) {
var itemAry = searchAry[i];//name=lili age=18
var item = itemAry.split("=");
console.log(item)//['name', 'lili']['age', '18']
var key = item[0];
var value = item[1];
obj[key] = value;
}
}
return obj;
}
var dd = queryParams(str);
console.log(dd)
</script>
</body>
</html>
1、时间对象
1.1【作用】:Date 对象用于处理日期和时间。
1.2【创建时间对象】: new Date()获取系统当前时间
var myDate=new Date(); //Tue Dec 24 2019 20:44:00 GMT+0800 (中国标准时间);
typeof myDate;====>"object" // 获取到是一个对象,并不是字符串
1.3 时间对象相关属性和方法
●getFullYear();获取年●getMonth();获取月 0到11 代表1月到12月●getDate();获取日期●getDay();星期几 (0---6)代表周日到到周六●getHours();时●getMinutes();分●getSeconds();秒●getMilliseconds();毫秒●getTime();获取当前日期到1970年1月1号 00:00:00 之间的毫秒差●toLocaleString();// 获取到的是年月日,时分秒"2019/12/25 上午10:15:50"●toLocaleDateString();// 获取到是字符串的年月日,例如:"2019/12/25"●toLocaleTimeString();/ 获取到的是字符串的时分秒上午10:18:28
2、案例:钟表
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible"content="ie=edge">
<title>Document</title>
<style>
#clock{
height:50px;
line-height:50px;
text-align:center;
}
</head>
<body>
<div class="clock"id="clock">
</div>
<script>
functionclock(){
vartime=newDate;
varyear=time.getFullYear();
varmonth=time.getMonth()+1;//0-11 代表1月到12月
vardate=time.getDate();//日期
varday=time.getDay();//0-6代表周日到到六
varhours=time.getHours();
varminutes=time.getMinutes();
varseconds=time.getSeconds();
varweekStr=["日","一","二","三","四","五","六"];
varweek=weekStr[day];
varresult=year+"年"+month+"月"+date+"日"+"周"+week;
result+=hours+"时"+minutes+"分"+seconds+"秒";
returnresult;
}
vartime=clock();
document.getElementById("clock").innerHTML=time;
setInterval(function(){
vartime=clock();
document.getElementById("clock").innerHTML=time;
},1000)
</script>
</body>
</html>
补0操作:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible"content="ie=edge">
<title>Document</title>
<style>
#clock{
height:50px;
line-height:50px;
text-align:center;
}
</head>
<body>
<div class="clock"id="clock">
</div>
<script>
varclock=document.getElementById("clock");
functiongetTime(){
vartime=newDate();
varyear=time.getFullYear();
varmonth=time.getMonth()+1;
vardate=time.getDate();
varday=time.getDay();
varhour=time.getHours();
varminutes=time.getMinutes();
varseconds=time.getSeconds();
varweek=["周日","周一","周二","周三","周四","周五","周六"];
varres=year+"年"+addZero(month)+"月"+addZero(date)+"日"+week[day]+" "+addZero(hour)+":"+addZero(minutes)+":"+addZero(seconds);
returnres;
}
functionaddZero(num){
returnnum<10?"0"+num:num;
}
varres=getTime();
clock.innerText=res;
setInterval(function(){
varres=getTime();
clock.innerText=res;
},1000)
</script>
</body>
</html>
3、定时器:
3.1、setTimeOut
一定的时间后,去执行某些事情,是单次调用
setTimeout(function(){
alert("wasai!you are beautiful");
},1000)
3.2、setInterval
间隔多少时间后,去执行某些事情,是多次调用
setInterval(function(){
console.log("我真美!")
},1000)
3.3、定时器的参数说明
// 定时器可以传递多个参数:
// 1、执行的函数
// 2、时间
// 3、后面的参数就是执行函数在执行的时候传递的实参
setTimeout(function(num,s,m){
console.log(num,s,m);
},1000,2,3,6);
3.4、定时器是有返回值的,返回值代表定时器处于当前页面中的第几个
vartime1=setTimeout(function(){
console.log("wasai!you are beautiful");
},1000)
vartime2=setInterval(function(){
console.log("我真美!")
},1000)
vartime3=setTimeout(function(){
console.log("aa");
},1000)
console.log(time1)=====>1
console.log(time1)=====>2
console.log(time1)=====>3
3.5、定时器是异步任务,只要当咱们同步代码执行完毕之后,才能执行。
setTimeout(function(){
console.log("定时器");
},1000)
console.log("haha")
4、清除定时器的方法
●clearTimeout●clearInterval
5、练习
做一个抽奖程序,页面中有一个区域显示中奖人员的编号,在JS中写一段代码,要求每隔1秒中随机创建一个四位的数字(每一位数字的取值范围0-9),当10秒结束后,结束定时器,最后显示的四位数字即是中奖的号码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>中奖</title>
<style>
#prize{
width:200px;
height:50px;
border:1px solid green;
}
</style>
</head>
<body>
<div class="prize" id="prize"></div>
</body>
</html>
<script>
function getCode(){
var str="0123456789";
var result="";
for(var i=0;i<4;i++){
var index=Math.floor(Math.random()*9);
result+=str[index];
}
prize.innerHTML=result;
return result;
}
var time1=setInterval(function(){
getCode();
},1000);
setTimeout(function(){
clearInterval(time1);
},10000)
</script>
倒计时案例:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible"content="ie=edge">
<title>Document</title>
<style>
*{
margin:0;
padding:0;
}
body {
background:darkslateblue;
}
#time{
height:100px;
line-height:100px;
border:1px dashed yellow;
text-align:center;
color:#fff;
font-size:26px;
}
</head>
<body>
<div id="time">
</div>
<script>
/*
倒计时:
目标时间:2021年12月14号
目标时间-现在的时候=还剩多少时间?
为了方便计算时间差,我们统一转成到1970年...的时间戳
*/
functioncountDown(targetStr){
// 目标时间
vartargetTime=newDate(targetStr);
// 现在的时间
varnowTime=newDate()
// 时间差,得出来的是毫秒
vardiffTime=targetTime-nowTime;
// 把时间差换算成时、分、秒
varhours=Math.floor(diffTime/(1000*60*60));
varminutes=Math.floor((diffTime-hours*1000*60*60)/(1000*60));
varseconds=Math.floor((diffTime-hours*1000*60*60-minutes*60*1000)/1000);
varresult=formatter(hours)+"时"+formatter(minutes)+"分"+formatter(seconds)+"秒";
returnresult;
}
functionformatter(val){
returnval=Number(val)<10?"0"+val:val;
}
varres=countDown("2021-12-24 00:00:00");
time.innerHTML=res;
console.log(res)
setInterval(()=>{
varres=countDown("2021-12-24 00:00:00");
time.innerHTML=res;
},1000);
</body>
</html>