DOM编程
title: “DOM编程” date: 2019-11-28T15:09:36+08:00
draft: false
title: “DOM编程” date: 2019-11-28T15:09:36+08:00
draft: false
前言
//在进行认识DOM开始之前呢,希望你有如下知识:
1. JS语法,如变量、if else、循环
2. JS的其中数据类型: Number String Bool Symbol Null Undefined Object
3. 五个falsy值: 0 NaN '' null undefined
4. 函数是对象,数组也是对象
5. div和span标签
6. Css布局:flex
什么是 falsy 值? 在 Bool 上下文中认定为 false 的值
1. 什么是 DOM(Document Object Model 文档对象模型)
1 . 网页里的各种标签按照数据结构来看是一个树的结构.(Document)
<!doctype html>
<html>
<head>
<title>我是第三层</title>
</head>
<body>
<div>我是第三层</div>
</body>
</html>
<!-- 是不是很想树的结构?-->
2 . JS 如何操作这棵树?(Object)
浏览器往 window 上加一个 document 即可,JS 用 document 操作网页。这就是 Document Object Model 文档对象模型
不过有个事实是自带的 API 有点难用,或者你认为 DOM 有点傻呢?不奇怪,下次文章解答你的疑惑
至于模型两字(Model)应该还有其他的模型以作区分.
2. 如何获取元素?(标签)
//标签必须有一个id的属性和属性值,可以通过以下API获取到
1. window.id或者id //通过ID值找到标签
//实例(假设id值是kw):
window.kw //可以获取到这个元素
kw //也可以获取到这个元素
2. document.getElementById() //通过ID值找到标签
//实例
document.getElementById('kw')
3. document.getElementsByTagName() //通过标签name去找,返回是一个数组
//实例
document.getElementsByTagName('div)
4. document.getElementsByClassName() //通过class属性值来寻找标签,返回是一个数组
//实例
document.getElementsByClassName('red')document.querySelectorAll()
5. document.querySelector() //放回文档中与指定选择器或选择器组匹配的第一个HTML元素element。找不到返回
//null
//实例
document.querySelector('#wrapper') //带#是找id的值,(符合有效的CSS选择器字符串即可)
6. document.querySelectorAll() //返回与指定的选择器匹配的文档中的元素列表。返回的是NodeList(节点列表及
//数组)
//实例
document.querySelectorAll('.red')[0]
共有 6 个。该怎么用呢?工作上如果要兼容 IE 用 2 3 4.不兼容用 5 6.自己写 demo 可以用 1
3. 如何获取特定元素?(标签)
//获取html元素
document.documentElement
//获取head元素
document.head
//获取body元素
document.body
//获取窗口(窗口不是元素)
window
//获取所有元素
document.all //它是第六个falsy值. 返回一个数组包含了所有标签.
4. 追究元素本质
由于我们已知如何获取元素并且已知 DOM,文档对象模型。每个元素都是一个对象吗?是的.我们拿一个来看看它的原型
let div = document.getElementsByTagName('div')[0]
console.dir(div) //
//可以看出
div.__proto__ === HTMLDivElement.prototype //div共有属性
HTMLDivElement.__proto__ === HTMLElement.prototype //html共有属性
HTMLElement.__proto__ === Element.prototype //xml html标签的共有属性
Element.__proto__ === Node.prototype //节点共有属性,该元素是是标签还是文本?
Node.__proto__ === EventTarget.prototype //一些事件
EventTarget.__proto__ === Object.prototype //对象的共有属性
可以看出原型链的终点是 object.prototype 所以 div 是一个对象
什么是节点?元素?标签?
//本质上区分元素,标签,节点
<div></div> //这是一个div标签(tag),
<div>hello world</div> //如果加上内容等等其他的是一个元素(element)
//节点呢?显然单独无法区别出来,待我们把整个网页整体来看,形成一个树状时,每个点是一个节点,在
//div原型链上个有个Node对象,它是用来干嘛的?区分这个节点它是element(为1)还是text(hello world)(3)和其他
let div = document.getElementsByTagName('div')[1] //上面第二个标签
console.log(div.nodeType) //1 是element
console.log(div.firstChild.nodeType) //3 是text
5. 节点的增删改查
增
//创建一个标签节点 let div1 = document.createElement('div) document.createElement('style') document.createElement('script') document.createElement('li') //创建一个文本节点 text1 = document.createTextNode('你好') //标签插入文本 div1.appendChild(text1) div1.innerText = '你好' or div1.textContent = '你好' //当前创建的标签节点和文本节点处于 JS 线程中,你必须把它放到 head 或 body 里面它才生效 document.body.appendChild(div) 已在页面中的元素.appendChild(div) //一个节点只能在页面中出现一次如果想出现两次需要复制一份,可以调用cloneNode()API div2 = div1.cloneNode(true)
Node.cloneNode() 方法 :接收一个参数,如果为 true 则该节点的所有后代都会克隆,如果 false 则只会克隆该节点本身
删
//有两种方法删除元素 let div = document.createElement('div') document.body.appendChild(div) div.parentNode.removeChild(div) //旧方法 // Node.parentNode()方法 : 返回该指定节点在DOM树中的父节点 // Node.removeChild()方法:从DOM中删除一个子节点.返回删除的节点 document.body.appendChild(div) div.remove() //新方法 //ChildNode.remove()方法:把对象从它所属的DOM树中删除
该删除的方法仅仅只是把元素从页面中删除了,
改
let div = document.createElement('div') //创建一个div document.body.appendChild(div) //在body里最后添加一个子元素, //改id div.id = div //改class div.className ='red' //如果class有属性值需要改成 +=。这是旧方法 div.classList.add('green') //新方法,直接添加 //改style div.style = ' color:red' //这种方法会覆盖原有的,不推荐 div.style.color = 'red' //即可,其他同理 div.background-color = 'black' //带有杠的这种变成下面这种 div.backgroundColor = 'black' //改指定某个属性值 div.setAttribute('data-x','text') setAttribute() //该方法设置指定元素上的某个属性值。如果属性已经存在,则更新该值;否则, //使用指定的名称和值添加一个新的属性 //如何得到上面的值? div.getAttribute('data-x') div.dataset.x //该方法的前提属性一定是data-开头才可以 div.dataset.x = 'xiaoxiao' //也可以直接改 //在读标准属性的的时候有两种方法 div.classList / a.href //第一种 div.getAttribute('class') / a.getAttribute('href') //第二种 // 为什么有这种呢?因为有时a的标签不是跳转到其他域名还是跳转其他子页面href="/xxx" //要是还是用 a.href会得到当前域名+a.href //改事件处理函数(div.onclick默认为null) //如果把div.onclick改为一个函数fn,那么点击div的时候,浏览器就会调用这个函数, //并且是这样调用的fn.call(div,event) div会被当成this event包含了点击事件的所有信息 div.onclick = function (x){ console.log(this) //如果写成匿名函数在点击时浏览器会传进去,this === div console.log(x) // x === event }
let div = document.createElement('div') let div2 =div.cloneNode(true) //深拷贝(当前节点+后代所有节点)false是浅拷贝(当前节点) document.body.appendChild(div) // 改内容 div.innerText = 'hi' div.textContent = 'hi' //改HTML内容 div.innerHTML = '<string>重要内容</string>` //改标签 div.innerHTML = '' //清空 div.appendChild(div2)
查
let div = document.createElement('div') document.body.appendChild(div) //查自己 div //查父节点 div.parentNode //返回指定的节点在DOM树中的父节点 div.parentElement //返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个DOM元素, //则返回null //查父节点的父节点 div.parentNode.parentNode //以此类推 //查子节点 div.childNodes //返回包含指定节点的子节点的集合,该集合为即时更新的集合有空格会也为一个子节点(text) div.children //返回一个Node的子elements(元素) //查所有相邻节点(没有API可以调) //思路是:找到父节点所有的子节点,排除自己,得到所有的相邻节点 let siblings = [] let c = div.parentNode.children for(let i=0;i<c.length;i++>){ if(c[i]!==div){ siblings.push(c[i]) } }
let div = document.createElement('div') document.body.appendChild(div) //查老大 div.parentNode.children[0] div.firstChild //返回树中节点的第一个子节点,如果节点是无子节点,则返回null //查老幺 div.lastChild //返回当前节点的最后一个子节点 //如果父节点为一个元素节点,则子节点通常为一个元素节点,或一个文本节点,或一个注释节点.如果没有节点 // 则返回null //查前一个兄弟节点 div.previousSibling //返回当前节点的前一个兄弟节点,没有返回null //查后一个兄弟节点 div.nextSibling //返回其父节点的childNodes列表中紧跟在其后面的节点,如果指定节点是最后一 // 个节点则返回null //上面两个查前和后一个兄弟节点,如果前后的节点是text节点或其他的话, //返回的就不是element节点下面两个API是返回element节点的 div.previousElementSibling //返回当前元素在其父元素的子元素节点中的前一个元素节点,如果该元素已经是第一个元素节点,则返回null div.nextElementSibling //返回当前元素在其父元素的子元素节点中的后一个元素节点,如果该元素是最后一个元素节点,则返回null
遍历一个 div 元素里所有元素
let div = document.createElement('div') document.body.appendChild(div) let travel = (node,fn) =>{ fn(node) if(node.children){ for(let i=0;i<node.children.length;i++){ travel(node.children[i],fn) } } } travel(div,(node)=>{console.log(node)})
6. DOM 操作是跨线程的
前言:JS 引擎和渲染引擎不是一个引擎它们是独立工作的,那桥梁是谁?浏览器
let div = document.createElement('div')
//上述代码由JS引擎去做的,存在内存里
document.body.appendChild(div)
//上述代码是往页面body里添加一个div标签由JS引擎 浏览器 渲染引擎共同工作
//经过上述代码.那它们之间是怎么运行的?
当浏览器发现 JS 在 body 里面加了个 div 对象,浏览器就会通知渲染引擎在页面里也新增一个 div 元素新增的 div 元素所有属性都照抄上述代码。这就是跨线程的操作
我们直到一个 div 标签不仅仅是个标签,它还有属性呢.我们用 JS 去操作它的属性会怎样呢?
let div = document.createElement('div')
document.body.appendChild(div)
//我们知道上面是JS引擎和浏览器以及渲染引擎共同工作才能完成,那下面的属性呢?
div.id = 'xiaoxiao' //标准属性会同步修改
div.title = 'new title' //标准属性会同步修改
div.x = 'x' //(页面标签先有自定义属性)非标准属性不会同步修改
div.data-x = 'xx' //data-* 属性会同步修改
//已知标准属性和data-*属性会同步修改,自定义属性不会同步修改,如果要自定义属性同步修改加上前缀data-。
//用dataset来访问得到
假设我上下文代码同时修改一个元素的 width 会渲染两次吗?答:不会就想我欠你 20.你说两次我也只是还 20
最后一个问题,div 的属性在 JS 里和在标签里,是一样的吗?
//答:标准属性和前缀data-是一样的,非标准属性就不一样了,
//还有在标签里属性的值只能是字符串,在js里是对象属性值不单单是一个字符串了,还是可以布尔值(boolean)
//我们怎么区分呢?
//property属性是JS线程中所有属性叫做div的property.
//attribute也是属性是渲染引擎对应标签的属性,叫做attribute
see you later