深说浅谈DOM对象操作,用4个版本demoplus让你扭断history.state头
什么是节点
HTML 文档中的每个成分都是一个节点。
(1)节点类型
DOM 是这样规定的:
- 整个文档是一个文档节点
- 每个 HTML 标签是一个元素节点
- 包含在 HTML 元素中的文本是文本节点
- 每一个 HTML 属性是一个属性节点 注释属于注释节点
(2)节点层次关系-节点彼此都有等级关系。
HTML 文档中的所有节点组成了一个文档树(或节点树)。
HTML文档中的每个元素、属性、文本等都代表着树中的一个节点。
树起始于文档节点,并由此继续伸出枝条,直到处于这棵树最低级别的所有文本节点为止。
当节点分享同一个父节点时,它们就是同辈(同级节点)。
节点也可以拥有后代,后代指某个节点的所有子节点,或者这些子节点的子节点。
节点也可以拥有先辈。先辈是某个节点的父节点,或者父节点的父节点,以此类推。
(3)节点属性
每个节点都拥有包含着关于节点某些信息的属性。这些属性是:
nodeName(节点名称)
元素节点的 nodeName 是标签名称
文本节点的 nodeName 永远是 #text
注释节点的 nodeName 永远是 #comment
文本节点,nodeValue 属性包含文本。
节点nodeValue元素节点nodeValue不可用不可用
nodeValue包括注释内容
nodeType(节点类型)
获取body后节点列表
document.body.childNodes
<p>a</p>
<form action="">
<input type="text" name="user" id="">
<input type="radio" name="sex" value="男" class="input1">
<input type="radio" name="sex" value="女" class="input2">
</form>
<div id="div2">
<span class="div1"></span>
<span class="div1"></span>
</div>
<span class="div1"></span>
console.log(document.body.childNodes);
//text, p, text, comment, text, script
console.log(document.body.childNodes[1].nodeName==="P");
//true
获取DOM元素
document.documentElement //html标签
document.body //body 标签
document.title //标题
//所有的style标签列表和link标签列表有多少个样式style就有多少length
方法名称 | 调用描述 |
---|---|
document.getElementById() | 通过元素的id值获取 |
document.getElementsByTagName() | 返回元素的HTML集合 |
document.getElementsByName() | ame来获得对象,同一个Name可以对应多个对象(Name不是唯一的),所以它得到的是一个数组 |
document.getElementsByClassName(新) | 通过类名来获取 元素 |
document.querySelector( 新) | 根据标签名(类名)获取第一个元素 |
document.querySelectorAll(新) | 获取所有标签名的元素 |
querySelector的用法
在js中可以通过标签选择器获取标签,现在发现还有querySelector可以实现选择器的功能。可以是标签名,也可以是类名。
<p id="demo" class="demo_1">单击按钮获取按钮元素的节点值。</p>
<button onclick="myFunction()">点我</button>
<script>
console.log(document.getElementById('demo'));
console.log(document.querySelector('.demo_1'));
console.log(document.querySelector('p'));
getElementsByTagName的用法
传回指定名称的元素集合。也就是一个数组集合,所以用的时候得注意这个后面指定是哪个元素。
var c=document.getElementsByTagName("BUTTON")[0];
节点遍历
方法名称 | 调用描述 |
---|---|
childNodes | 所有子节点 获取所有子节点(包括注释) |
children | 所有是标签类型的子节点 获取所有子元素 |
parentNode | 获取已知节点的父节点 |
firstElementChild | 第一个子节点 (元素) |
firstChild | 第一个子节点 |
lastElementChild() | 最后一个子节点(元素) |
lastChild: | 最后一个子节点 |
nextElementSiblin | 下一个兄弟节点(元素) |
nextSibling | 下一个兄弟节点 |
previousElementSibling | 上一个兄弟节点 |
previousSibling | 上一个兄弟节点 |
document.createTextNode(“”) | 创建文本节点 |
<div id="div2">
<span class="div1"></span>
<span class="div1"></span>
</div>
console.log( div2.nextSibling);//#text
console.log(div2.childNodes);
//[text, span.div1, text, span.div1, text]
节点/元素 操作 | 功能描述 |
---|---|
document.createElement(“标签名”) | 创建新元素 |
父元素.appendChild() | 追加在尾部 |
父元素.insertBefore(要插入的元素,要插入在谁的前面) | 追加在谁之前 |
textContent | 获取包含换行符合空格的结构性文本 |
innerText | 获取父元素中所有文本和后代文本 |
innerHTML | 父元素中所有的html标签文本及内容 |
document.createTextNode(“”) | 创建文本节点 |
replaceChild(newNode,oldNode) | newNode替换节点oldNode |
removeChild(node) | 移除父节点的某个子节点 |
cloneNode(boolean) | 复制一个节点 |
被替换的元素.replaceWith(新元素) | 替换 |
父元素.replaceChild(新元素,被替换的元素) | 替换 |
innerText可以设置转义字符和空格换行等,textContent只能设置其文本,不能含转义,传统的获取渲染会丢失对象
DOM树
CSS树
DOM渲染树
异步加载渲染树
数据驱动显示
传统点击事件重新渲染丢失对象元素
<div id="div1">
<span>1</span>
</div>
var div1=document.querySelector("#div1");
var span=div1.querySelector("span");
span.onclick = function(){
span.style.color = 'red'
}
div1.innerHTML+="<span>2</span>";
//这样会失去点击对象
dom创建元素直接添加渲染
不改变原来对象:
div id="div1">
<span>1</span>
</div>
var span_1 = document.createElement('span')
span_1.textContent = '2'
div1.appendChild(span_1,span);
//创建一个dom对象(元素/标签)然后在给赋值
span.onclick = function(){
span.style.color = 'red'
}
//不会失去捕获对象
创建碎片容器(js创建一个table)
<script>
// table.tr.dd
/* 首先创建一个元素table */
var table = document.createElement('table')
for(var i=0;i<100;i++){
//创建tr对象标签
var tr = document.createElement('tr')
for(var j=0;j<10;j++){
// 创建td对象标签
var td = document.createElement('td')
// 给td添加内容
td.textContent = '1'
// 追加在这个父标签的最后位
tr.appendChild(td)
}
// 追加在这个父标签的最后位
table.appendChild(tr)
}
console.log(table);
document.body.appendChild(table)
</script>
创建100个div插入到body中
var elem=document.createDocumentFragment();//创建碎片容器
for(var i=0;i<100;i++){
var div=document.createElement("div");
elem.appendChild(div);
}
document.body.appendChild(elem);
创建文本结点插在第一个子元素的前面
<div id="div1">
<span>1</span>
</div>
var txtNode=document.createTextNode("3");
div1.insertBefore(txtNode,div1.firstChild);
只有img标签才可以new 创建
var img=document.createElement("img");
//var img=new Image();
img.src="./img/img_21.JPG"
document.body.appendChild(img);
var img1=img.cloneNode(false);
document.body.appendChild(img1);
删除元素
span.remove();
// 父元素删除子元素
div1.removeChild(span);
// 清空元素所有内容
div1.innerHTML="";
替换元素
var p=document.createElement("p");
// 父元素.replaceChild(新元素,被替换的元素)
document.body.replaceChild(p,div1)
// 被替换的元素.replaceWith(新元素)
div1.replaceWith(p)
根据loaction下的这个hash值变化,触发事件驱动函数
添加内容(数据驱动显示)版本一
无历史记录版本,纯dom操作
<input type="text"> <button>点击添加</button>
<ul></ul>
<script>
var input,button,ul;
init();
function init(){
/* 获取三个元素对象 */
input = document.querySelector('input')
button = document.querySelector('button')
ul = document.querySelector('ul')
/* 给button设置点击事件 */
button.onclick = onclickHander;
}
function onclickHander(){
/* 点击之后创建一个li元素 */
var li = document.createElement('li')
console.log(input.value);
li.textContent = input.value;
ul.insertBefore(li,ul.firstChild);
input.value = ''
}
</script>
</body>
数据驱动显示,碎片容器打包渲染(版本二)
<body>
<input type="text"> <button>点击添加</button>
<ul></ul>
<script>
var input,button,ul;
var list =[];
init();
function init(){
/* 获取三个元素对象 */
// window.onpopstate = onclickHander;
input = document.querySelector('input')
button = document.querySelector('button')
ul = document.querySelector('ul')
// var li = history.state?
/* 给button设置点击事件 */
button.onclick = onclickHander;
}
function onclickHander(){
/* 点击之后创建一个li元素 */
// var li = document.createElement('li')
// console.log(input.value);
list.push(input.value)
upData();
console.log(list);
// ul.insertBefore(li,ul.firstChild);
input.value = ''
// history.pushState(li,li);
// console.log(history.state);
}
function upData(){
//每次渲染之前给ul清空内容
ul.innerHTML = ''
var emel = document.createDocumentFragment();
for(var i=0;i<list.length;i++){
var li = document.createElement('li');
li.textContent = list[i];
//每一次生成的li包裹在这emel胶囊中
emel.appendChild(li)
}
ul.append(emel)
}
</script>
新增删除按钮。添加属性标记条件,利于程序删除(版本三)
<input type="text"> <button>点击添加</button>
<ul></ul>
<script>
var input,button,ul;
var list =[];
init();
function init(){
/* 获取三个元素对象 */
// window.onpopstate = onclickHander;
input = document.querySelector('input')
button = document.querySelector('button')
ul = document.querySelector('ul')
// var li = history.state?
/* 给button设置点击事件 */
button.onclick = onclickHander;
}
function onclickHander(){
/* 点击之后创建一个li元素 */
// var li = document.createElement('li')
// console.log(input.value);
if(!input.value){
return;
}
list.push(input.value)
upData();
console.log(list);
// ul.insertBefore(li,ul.firstChild);
input.value = ''
// history.pushState(li,li);
// console.log(history.state);
}
function upData(){
//每次渲染之前给ul清空内容
ul.innerHTML = ''
var emel = document.createDocumentFragment();
for(var i=0;i<list.length;i++){
var li = document.createElement('li');
li.textContent = list[i];
var bn = document.createElement('button');
bn.innerHTML = '×';
bn.onclick = removeData;
bn.i=i;
li.append(bn)
//每一次生成的li包裹在这emel胶囊中
emel.appendChild(li)
}
ul.append(emel)
}
function removeData(){
list.splice(this.i,1)
upData()
}
添加history历史记录(版本四)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text"> <button>点击添加</button>
<ul></ul>
<script>
var input,button,ul;
var list =[];
init();
function init(){
/* 初始化启动history监听 */
window.onpopstate = changehistoryHander;
input = document.querySelector('input')
button = document.querySelector('button')
ul = document.querySelector('ul')
button.onclick = onclickHander;
/* 历史第一次替换为list */
history.replaceState(list,'a')
}
/* 把数据框的里值放在数组当中 */
function onclickHander(){
if(!input.value){
return;
}
list.push(input.value)
upData();
console.log(list);
input.value = ''
//添加历史状态
history.pushState(list,'b')
}
/* 数据驱动显示 */
function upData(){
ul.innerHTML = ''
var emel = document.createDocumentFragment();
for(var i=0;i<list.length;i++){
var li = document.createElement('li');
li.textContent = list[i];
var bn = document.createElement('button');
bn.innerHTML = '×';
bn.onclick = removeData;
bn.i=i;
li.append(bn)
emel.appendChild(li)
}
ul.append(emel)
}
/* 过程中做好数据标识 */
function removeData(){
list.splice(this.i,1)
history.pushState(list,'c')
upData()
}
function changehistoryHander(){
list = history.state;
upData()
}
</script>
</body>
</html>
Dom属性
DOM 的对象属性 部分标签属性和对象属性是相通的
大部分系统默认的标签属性都是相通
自定义的标签属性是不相通的
class 是特殊的,在对象属性中使用className
表单\超链接的name与对象属性name相同,但是其他元素的name不相通
<div id="div1" class="div2" name="abc" title="div3" names="xietian" data-main="asds-asdwe"></div>
<input type="checkbox" name="user" checked>
<a href="" name="abc"></a>
DOM标签上的属性叫做标签属性
标签属性配合样式来用
方法 | 功能 |
---|---|
对象.getAttribute(“title”) | 获取属性值 |
对象.setAttribute(“标签名”,“new内容”) | 修改属性值 |
removeAttribute(“标签名”); | 删除标签属性值 |
var div=document.querySelector("div");
console.log(div.style);//CSSStyleDeclaration
for(var i=0;i<div.style.length;i++){
var key=div.style[i];
console.log(key,div.style[key]);
//color blue
//width 100px
}
客户区大小
document.documentElement.clientWidth
document.documentElement.clientHeight
document.body.clientWidth
document.body.clientHeight
二级菜单收缩
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
ul{
overflow: hidden;
max-height: 0px;
transition: all 0.5s;
}
ul[open]{
max-height: 1000px;
}
</style>
</head>
<body>
<ul class="city" open>
<li>北京
<ul>
<li>海淀</li>
<li>朝阳</li>
<li>昌平</li>
<li>大兴</li>
<li>密云</li>
<li>顺义</li>
</ul>
</li>
<li>河北</li>
<li>山西</li>
<li>陕西
<ul>
<li>西安</li>
<li>咸阳</li>
<li>宝鸡</li>
<li>铜川</li>
<li>榆林</li>
</ul>
</li>
<li>河南</li>
<li>山东</li>
</ul>
<script>
var lis;
init();
function init(){
lis = document.querySelectorAll('li');
for(var i=0;i<lis.length;i++){
lis[i].onclick = clickHandler;
}
}
function clickHandler(){
if(!this.firstElementChild) return
if(this.firstElementChild.getAttribute('open') === null){
this.firstElementChild.setAttribute('open','')
}else{
this.firstElementChild.removeAttribute('open')
}
}
</script>
</body>
</html>
样式
知识点:增加样式的优先级
background-color: red !important
有两种属性:
- dom对象属性
- dom标签上的叫做标签属性
- 标签属性的名称和值都不区分大小写所以必须使用
‘-’
来链接字符串的方式 - 标签的属性值必须是字符串
div{
width: 50px;
height: 50px;
background-color: red !important
}
</style>
</head>
<body>
<div style="color:blue ;width: 100px;">123</div>
<script>
var div = document.querySelector('div')
console.log(div.style);//CSSStyleDeclaration
console.log(document.styleSheets);//所有的stylecss标签;列表CSSStyleDeclaration
</script>
修改css样式
document.styleSheets[1] 第几个style列表
document.styleSheets[1].cssRules[0] 第几个选择器
.cssRules[i].selectorText,第i个选择器的名称
document.styleSheets[1].rules[0].style.width ='500px'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
width: 50px;
height: 50px;
background-color: red ;
}
</style>
<style>
.div1{
width: 100px;
height: 100px;
}
.div2{
width: 50px;
height: 50px;
}
.div3{
width: 200px;
height: 200px;
}
.div4>.div1{
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div style="color:blue ;;"></div>
<div class="div1"></div>
<div class="div2"></div>
<div class="div3"></div>
<div class="div4">
<div class="div1"></div>
</div>
<script>;
var styleSheet = document.styleSheets[document.styleSheets.length-1];
for(var i=0;i<styleSheet.cssRules.length;i++){
if(styleSheet.cssRules[i].selectorText.includes('.div1')){
styleSheet.cssRules[i].style.width = '500px'
}
styleSheet.cssRules[i].style.backgroundColor = Array(6).fill(1).reduce(function(v,item){
return v+(~~(Math.random()*16)).toString(16)
},"#")
}
</script>
</body>
</html>
dom常见的属性(宽高)
项目 | Value |
---|---|
宽高 | width height(对象) |
clientWidth clientHeight | 客户宽高 |
offsetWidth offsetHeight | 偏量宽高 |
scrollWidth scrollHeight | 滚动宽高 |
对象.getBoundingClientRect(); | 获取元素的相对视口的矩形位置 |
返回值 | {width,height,x,y,left,top,right,bottom} |
div{
width: 50px;
height: 50px;
background-color: red !important
}
var div = document.querySelector('div')
console.log(div.style);//CSSStyleDeclaration
console.log(div.clientWidth,div.clientHeight);//width+padding*2-滚动条轨道宽高
console.log(div.offsetWidth,div.offsetHeight);//width+padding*2+border*2 实际占位宽高
console.log(div.scrollWidth,div.scrollHeight);//实际内容宽高
console.log(document.documentElement.clientWidth,document.documentElement.clientHeight);// 视口宽高
console.log(document.body.clientWidth,document.body.clientHeight); //body的宽高
onsole.log(document.documentElement.offsetWidth,document.documentElement.offsetHeight);//html被撑开的宽高
console.log(document.body.offsetWidth,document.body.offsetHeight);//body的宽高
//滚动条的宽高
console.log(document.documentElement.scrollWidth,document.documentElement.scrollHeight); console.log(document.body.scrollWidth,document.body.scrollHeight);
dom常见的属性(位置)
位置Left Top | (DOM) |
---|---|
没有定位的是 | 相对于视口 |
做了定位的是 | 相对父容器的距离 |
div.clicentLeft | 边线粗细 |
div.clientLeft | 边线粗细 |
div.offsetLeft | 相对于视口/相对父容器的左上角距离 |
div.offsetTop | 相对于视口/相对父容器的左上角距离 |
div.scrollLeft | div滚动条的位置 |
div.scrollTop | div滚动条的位置,可以设置也可以获取 |
.div1{
width: 300px;
height: 300px;
position: relative;
left: 100px;
top: 3000px;
background-color: red;
}
.div2{
width: 50px;
height: 50px;
position: absolute;
left: 50px;
top: 50px;
background-color: blue;
}
var div2=document.querySelector(".div2");
console.log(div2.offsetLeft,div2.offsetTop)//50.50
获取元素的相对视口的矩形位置 | Value |
---|
var rect=div2.getBoundingClientRect();
{width,height,x,y,left,top,right,bottom}
console.log(rect);