BOM和DOM相关API
一、DOM相关API
1. DOM API
-
DOM(Document Object Model)文档对象模型得树形结构
- 文档对象模型就是一个树形结构,类似于家谱树
html标签里面包裹了所有的文档内容。他是一个父亲节点(parent) 它没有父亲节点,也没有兄弟节点,我们称html标签为树根,也就是根节点,整个html表示整个文档。
在html节点内部通常有两个同级节点head和body,html是head的父亲节点,html也是body的父亲节点,他们同一层级并且相互不包含,我们称同属于同一个父亲的节点为兄弟节点,而head和body都是html的子节点。这样一层一层的关系就是节点树。各个标签在页面中都是元素节点(element node)
-
节点(node)的种类
- 元素节点(element node)
文档对象模型中的标签就是最基本的元素节点。它们层层嵌套形参整个页面。内部可能包含了文本和属性
- 文本节点(text node)
我们称DOM内的文本为文本节点。文本节点有的被包含在元素节点中的,比如p标签内部的文字。但是有的元素节点没有文本节点,
- 属性节点(attribute node)
属性节点从属于元素。比如其中type='radio'是元素节点p的属性节点。不是所有的元素都有属性节点,但是所有的属性节点都必然属于某一个元素。如:class/id/style
-
获取元素的方式
document.getElementById //通过ID获取某个元素,所有浏览器兼容 document.getElementsByClassName //通过class类名获取,获取是一组,不支持IE8及以下 document.getElementsByTagName //通过标签名获取,获取是一组,所有浏览器兼容 document.getElementsByName //通过name获取,获取是一组,很少用,所有浏览器兼容 querySelector、querySelectorAll 其中getElementById和querySelector获取的是元素单个节点。
而其余的方法都是获取若干个元素节点,那么会以类数组的形式存储
let arr1 = document.getElementsByClassName("item") let arr2 = document.querySelectorAll(".item") //arr1 HTMLCollection //arr2 NodeList
HTMLCollection和NodeList本质上共同点和不同:
都是DOM节点的集合,两者都属于Collections范畴,但是NodeList的范围要更广泛一点,它可以包含节点对象和文本对象。HTMLCollection比NodeList多了一个namedItem方法,其他方法保持一致
HTMLCollection
是以节点为元素的列表,可以凭借索引、节点名称、节点属性来对独立的节点进行访问。HTML DOM中的Collections是实时变动的,当原始文件变化,Collections也会随之发生变化。
NodeList
返回节点的有序集合,DOM中的NodeList
也是实时变动的
Node是一个基础类型,document, element, text, comment, documentFragment等都继承于Node. 在这篇文章最开始的测试中`NodeList`结果中有非常多的`text`,其实element, text, comment都是Node的子类,可以将它们视为:**elementNode**, **textNode**以及**commentNode**.平时在DOM中最常用的Element对象,其本质就是elementNode.
<ul class="box"> <li class="item"></li> <li class="item"></li> <li class="item"></li> </ul>
let box = document.querySelector(".box") let qli = document.querySelectorAll(".item") let cli = document.getElementsByClassName("item") let nli = document.getElementsByTagName("li") box.innerHTML+=`<li class="item"></li>`//添加元素 qli.length//3 NodeList 不会动态改变 cli.length//4 HTMLCollection 会动态改变(相当于记录了一个获取方式) nli.length//4 HTMLCollection 会动态改变
-
DOM中的增删改查
- 增加:
以前学习得时候我们用的是innerHTML添加的方式,这中添加方式的本质是什么?
box.innerHTML+="BLABLA" 将box内部的DOM节点转换成字符串,然后字符串添加内容,然后浏览器重新渲染。重新生成DOM结构。等价于重绘和重排
其中缺点有两点:1. 增加浏览器渲染不必要的开销 2. 原先的节点绑定的事件可能会消失。
for(let i = 0; i < 100; i++){ box.innerHTML+=`<li class="item">${i}</li>` } //此时浏览器重写了100次box的内部结构,每次重写都会重新渲染一次,等价于加载了100次页面 let str ="" for(let i = 0; i < 100; i++){ str+=`<li class="item">${i}</li>` } box.innerHTML+=str//两段代码效率完全不同 上面的效率其实不是最重要的。最重要的是你的新的节点已经不是原先的节点了
let box = document.querySelector(".box") let qli = document.querySelectorAll(".item") let cli = document.getElementsByClassName("item") let nli = document.getElementsByTagName("li") box.innerHTML+=`<li class="item"></li>`//添加元素 qli[0].onclick = function(){ alert(1) }//不会有任何改变,因为是静态获取,你绑定的元素已经消失了 let box = document.querySelector(".box") let qli = document.querySelectorAll(".item") let cli = document.getElementsByClassName("item") let nli = document.getElementsByTagName("li") cli[0].onclick = function(){ alert(1) }//虽然是动态获取,但是你绑定的元素还是会消失,注意顺序不同 box.innerHTML+=`<li class="item"></li>`//添加元素 所以综上,不要使用innerHTML添加元素。
正确的方式: DOM的生成节点的方式和添加元素的方式
let li = document.createElement("li")//生成一个li的标签节点。 let text1 = document.createTextNode("哈哈哈")//生成一个文本节点 li.appendChild(text1)//往li盒子中添加一个文本节点,默认添加到最后 let text2 = document.createTextNode("哇") li.insertBefore(text2, text1)//在li标签里面text1前面添加text2 document.body.appendChild(li) 这种添加方式只是对DOM树的某一个枝叶做修改,不会重新种树。但是枝叶的修改也是DOM操作,也会有渲染重排的问题。如何减少dom操作类似于str作为承接减少添加次数得方法有吗?有
let box = document.createDocumentFragment()//生成一个只存在于内存中的节点片段集合,它不是数组,更类似一个虚拟节点,里面存放元素 for(let i =0;i<100;i++){ let li = document.createElement("li")//如果生成元素写在循环外面? box.appendChild(li) } document.body.appendChild(box)//100个li就直接添加到body中了,直接父子关系,不存在中间层 如果生成元素写在循环外面从头到尾就只生成了一个节点。所以添加也只添加一次
- 删除
同样也不要innerHTML。
<ul class='list'> <li></li> <li></li> </ul> let ali = document.querySelectorAll(".list li") let list = document.querySelector(".list") list.removeChild(ali[0])// 从父元素中删除节点。必须是父子关系。 - 修改=增加+删除
list.replaceChild(document.createElement("a"), ali[0])//选中儿子ali[0]替换成新建的a元素节点 list.relaceWith(document.createElement("li"))// 将自己替换成li元素节点 - 查询
查询元素。可以通过绝对位值关键字符查询。也可以通过相对位值查询父节点,相邻节点,子节点查询元素。
- 父元素
let li = document.querySelectorAll(".list li")[0]
li.parentNode//返回li的父元素的节点
li.parentElement//返回li的父元素
li.parentNode===li.parentElement //true
li.offsetParent //定位父级
//两者没有本质上的区别,都可以返回父元素
document.documentElement// html标签节点
document.documentElement.parentElement// null,因为这里已经没有父元素了
document.documentElement.parentNode// #document 文档
document.documentElement.parentNode===document// true
* 同级元素 nextSibling和previousSibling选择上一个或者下一个同胞元素,如果不存在就是返回null ```html <ul> <li></li> asdasd <li></li> </ul>
let li = document.querySelector("li") li.previousSibling // null 前面一个节点 li.nextSibling// "asdasd" 后面节点,是文本 li.nextSibling.nextSibling// 第二个li
如果只想要标签元素节点,可以自行封装方法
其中节点有节点类型。1是标签,3是文本,2是属性
function next(ele){ if(el.nextSibling.nodeType==1) return el.nextSibling return next(ele.nextSibling) } //判断nodeType如果是标签就返回,如果不是就继续找下一个 //pre同理
相邻元素节点的获取去方式2:
li.nextElementSibing //下一个元素节点 li.previousElementSibing //前一个元素节点
- 子元素
子元素可能是多个,也可能是一个。
<ul> <li></li> asdasd <li></li> </ul>
let ul = document.querySelector("ul") ul.childElementCount //子元素节点个数 2 ul.children // 子元素节点集合HTMLCollection 2个li ul.childNodes// 子节点集合 NodeList 3个,其中第二个是text ul.hasChildNodes // 有没有子节点,有就是true没有就是false也可以用length ul.firstChild //第一个子节点 ul.firstElementChild //第一个元素子节点 ul.lastChild //第一个子节点 ul.lastElementChild //第一个元素子节点
- 属性节点和文本节点
let list = document.querySelector(".list") let attr = document.createAttribute("asd") attr.nodeType // 2 list.setAttribute(attr) let text = document.createTextNode("asd") text.nodeType // 3
- 节点对象的继承关系。
任何一个节点都是对象,原型链的终点都是Object。那继承关系是什么呢?在chrome浏览器可以测试
let temp = document.createElement("div")// div标签节点 temp.__proto__ // HTMLDivElement div构造函数的原型,上面有div节点独有的方法 temp.__proto__.__proto__ // HTMLCollection html元素节点构造函数得原型,节点的共有性质都在这 temp.__proto__.__proto__.__proto__ // Element 本质上和Document同级都是Node下面的一个元素 temp.__proto__.__proto__.__proto__.__proto__ // Node 所有节的构造函数得原型,存放节点基础方法 temp.__proto__.__proto__.__proto__.__proto__.__proto__ // EventTarget 事件对象的构造函数的原型 temp.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__ // Object的原型
2. 文档宽高、窗口宽高、元素宽高属性
-
元素宽高相关的属性
client: 读取元素的 content的宽高 + padding 的宽高 , 不包括滚动条和外边距以及边框
获取body的宽高属性:
console.log(document.body.clientWidth) //body的实际宽度,不包括滚动条 文档可视区域的宽高:
document.documentElement.clientWidth //当前文档区域可见宽度 document.documentElement.clientHeight //当前文档区域可见高度 注意: 不包括滚动条. 虽然没有兼容性问题,但是在IE不同的版本的宽度显示数值有一点差异,大概是3px的差异
注意: window下没有client属性
用在元素身上:
clientWidth: 元素的content + padding宽/高, 不包括滚动条, 和border
clientHeight
clientLeft
clientTop: 返回元素的content到边框的距离,也就是边框的厚度
注意: 只读属性,不能进行设置
<style> #box{ width: 200px; height: 200px; padding: 30px; margin: 70px; border: 20px black solid; background-color: #f60; } </style> <div id="box"></div> <script> var oBox = document.getElementById("box") console.log(oBox.clientWidth) console.log(oBox.clientHeight) </script> 获取窗口对的内部宽度
window.innerWidth; // 窗口的宽, 包括滚动条的宽度 如果想获取滚动条的宽度:
window.innerWidth - document.documentElement.clientWidth offset 获取body页面的实际宽高
document.body.offsetWidth document.body.offsetHeight 对于文档元素,只能获取文档的可视区域宽,但是能获取实际高度
document.documentElement.offsetWidth //当前文档的可见区域宽度 document.documentElement.offsetHeight//当前文档的实际高度 在元素上:
offsetWidth: 元素的content+padding+border
offsetHeight:
offsetTop 元素顶部到定位父级的顶部距离,不包括定位父级的边框部分
offsetLeft 元素左边到定位父级元素左边距离, 不包括定位父级的边框部分
元素到body顶部的距离方法: 没有包括父级边框
function getOffset(dom){ let o ={ top: 0, left: 0 } while(dom!==document.body){ o.left += dom.offsetLeft o.top += dom.offsetTop dom = dom.offsetParent //定位父级 } return o } //这里忽略掉了所有的边框 包括父级边框的,元素到body顶部的距离
function getOffset(dom){ let o = { top: 0, left: 0 } while(dom!==document.body){ o.left += dom.offsetLeft+dom.offsetParent.clientLeft o.top += dom.offsetTop+dom.offsetParent.clientTop dom = dom.offsetParent //定位父级 } return o } //包含边框,两方法可以合并 scroll
读取元素的content宽度+padding宽度,包含滚动条
scrollWidth
scrollHeight
子元素超出的时候, 会加上 超出得子元素的宽度/高度。当超出的时候,左padding或者上padding加上内容宽度或者高度。右padding或者下padding已经失去意义所以不会结算。
但是当添加overflow属性的时候。会计算全部的padding + 内容宽/高 + 超出的子级宽/高
滚动高度:可读可写
获取元素的y轴或者x轴方向的被滚动挡住的那部分的宽度高度。等价于滚动过的部分
scrollTop
scrollLeft
document.documentElement.scrollTop document.documentElement.scrollLeft 可以在滚动事件中监听:
window.onscroll = function(){ console.log(document.documentElement.scrollTop) } //滚动的时候返回滚动高度 获取页面滚动高度存在兼容性问题,需要做兼容性写法
document.body.scrollTop || document.documentElement.scrollTop 鼠标位值相关的坐标
事件对象e
document.body.onclick = function(e){ // e 是 前面onclick事件触发之后整理的那一时刻的信息对象,叫事件对象 console.log(e.clientX, e.clientY) // 相对于窗口的左上角位值 console.log(e.pageX, e.pageY) // 相对于文档位置的左上角的位置,包含滚动条隐藏部分 }
事件对象的兼容性写法
e = e || window.event
3. classList对象
classList是元素节点对象下面的一个属性,他是一个记录类名以及相关方法的对象,类数组对象
//<li class="item1 item2 item3"></li> let li = document.querySelector("li") li.classList // ["item1","item2","item3"] li.classList.value // "item1 item2 item3" 获取类名值 //在其原型上拥有很多方法 li.add("item4") // 返回undefined li.contain("item4") // true li.remove("item4","item3") //返回undefined
二-BOM相关
BOM是(Browser Object Model)的简称,中文名叫浏览器对象模型.在浏览器环境中抽象浏览器的语言是JavaScript, 所以JS中的原型链以及继承的性质将在BOM中实现.其中最大的就是window.对应的DOM中的document.
1. window
一个浏览器窗口(网页)就是一个window对象.里面包含了各种方法与参数, 这些东西可以帮助我们使用操作浏览器.比如浏览器的尺寸, 浏览器绑定的事件, 视图动画相关的方法, css单位系统.
因为是一个普通对象所以有原型链, 在原型链上有事件对象构造函数的原型(EventTarget), 这个是和DOM对象原型链上的(EventTarget)一致, 所以window对象和document对象都是可以访问EventTarget构造函数的原型的方法...换句话说都可以绑定事件.并且也继承Object原型的方法.
每当新打开一个页面的时候. 此页面都会新建一个window对象.属性各不相同内容也不近相同(几乎都会初始化).甚至是跳转页面的时候都会使得window变化. 但是在(非新建页面)跳转页面的时候, 会有一个window.name的属性保留下来.就是当前窗口的名字
window.name = "Gin" //跳转或者输入网址回车 console.log(window.name)//"Gin"
window.name 具有如此性质可以做到跨网站交互信息.所以在全局中请不要尝试修改window.name或者var name
- 浏览器的尺寸数据和方法
浏览器和document不同,不具有那么多的尺寸数据, 常用的有:
innerWidth, innerHeight 浏览器内部尺寸, 包含滚动条但是不包含书签栏的尺寸,也不包含控制台
screenLeft, screenTop(screenX, screenY兼容等价) 浏览器距离屏幕左上角的距离
outerHeight, outWidth 浏览器整体尺寸,包含外部的书签栏等
scrollX, scrollY 表示浏览器的滚动的位置
-
名词解释
- screen:屏幕。这一类取到的是关于屏幕的宽度和距离,与浏览器无关,获取window对象的属性。
- client:当前选中区域元素,这里是指浏览器区域。
- offset:偏移。指的是目标甲相对目标乙的距离。
- scroll:卷轴、卷动。指的是包含滚动条的的属性。
- inner:内部。指的是内部部分,不含滚动条。
-
方法等:
- alert(str) 警告
- confirm(str) 确认返回true取消返回false
- prompt(message, placeholder) 提示用户输入, 返回用户输入的内容, message是描述, placehoder是占用默认文本
- close 关闭
- blur 获取焦点
- scrollBy(x, y)偏移多少像素
- scrollTo(x, y)偏移到多少得位值
- confirm() 确认
- prompt() 输入信息并返回
- alert(str) 警告
2. location
location是一个对象,描述的是当前文档的相关位置信息。他是window对象下面的属性
console.log(window.location)
- host: 主机名加端口
- hostname: 主机名不加端口
- port:端口, 一台服务器有很多的入口,每个入口都有不同的处理方法和访问权限,默认是80端口
- pathname: 路径选择,网站分区选择
- protocol: 协议,客户端服务端请求响应标准如http和https等
- search: 请求的内容,通常是get请求发送给后台的信息
- href: 整体路径
通过这个信息你就直到你在哪你在干什么了。
如果我们直接访问location或者参与字符串操作会有内置toString方法输出href
3. history
你访问过的网站都会在浏览器内留下历史记录(滑稽脸)。并且我们可以通过JS代码进行跳转。它也是window对象下面的属性
window.history.length//该整数表示会话历史中元素的数目,包括当前加载的页面 history.back()// 跳转到上个页面 history.forward()//跳转到下个页面 history.go()// 传入一个数值,0表示当前页面,1表示下个页面,-1表示上个,-2表示上上个。。。
4. navigator
有关浏览器的信息。也是window对象下面的属性。
navigator.appVersion//只读:返回浏览器的平台和版本信息 navigator.appCodeName// 声明了浏览器的代码版本, 一般都是Mozilla navigator.userAgent //用户本地的信息组合 // 这些信息会在前后端请求的时候编排在请求头文件中发送给后台方便后台识别 //等等
5. console
window对象下面也有一个console对象,也有很多方法
console.log()//输出当前作用域的值 console.assert(bool, msg) //当为false就弹出msg,当为true就是不返回。 console.clear()//清屏。。。 console.count(str)//传入字符串并计数 console.countReset(str)//清除计数 console.warn(str) //警告 console.error(str) //打印报错信息 console.dir()// 输出可作为对象展开的内容 console.group()//小组内容输出。 console.groupEnd()//小组内容输出关闭。 console.time()//计时 console.timeEnd()//计时结束
本文作者:二价亚铁
本文链接:https://www.cnblogs.com/xw-01/p/17542200.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步