之前已经有了很多JS知识的铺垫,此时,针对之前的HTML,我们开始DOM的学习。
DOM树
前面已经说过,HTML其实是以树状结构分布的,而JS自然对这一点很好的做了一次利用,用document对HTML形成的网页树状结构进行操作,这个操作提供的一系列API就叫做DOM。全称是document Object Model。这是对应的DOM树的图形结构
<html lang="en">
<head>
<title>文档标题</title>
</head>
<body>
<a href="">我的文本链接</a>
<h1>我的标题</h1>
</body>
但是DOM很难记很难用,所以这篇文章主要注重一个DOM的方法的总结,实际开发中并不使用(事实上很多DOM的操作都用jQuery来代替了)。
DOM获取元素
首先,DOM如何获取元素:
老三招:
document.getElementById('xxx')
document.getElementsByTagName('div')
document.getElementsByClassName('red')
新时代的三招:
HTML上写好id="xxx",然后JS里面直接用xxx.起手就好了
document.querySelector('#xxx')
document.querySelectorAll('#xxx')[0]
工作中如果有要用DOM的地方,请果断新三招,但是第一招最好自己调试的时候用,被人看到了会疑惑的(老师说的,错了别怪我),如果要兼容IE,对不起,老老实实老三招。
但是!!!,对一些特别的元素,DOM有它特定的方法,这也是DOM比较难用的地方,因为老是要忘记。
获取html
document.documenetElement
获取head
document.head
获取body
document.body
获取窗口
window
特别的,任何浏览器都有一个document.all的方法,但是这个IE的表现情况和其他浏览器不同
IE的表现
非IE的表现
很奇怪是吧,但是这是真的,所以用这个就能判断这个浏览器是不是IE啦!
最后,讲一下document获取到的元素是什么吧。
在控制台打印一个试试,发现有个原型
,再点开它的原型,发现是
,这样一层层的下去,依次是
,
,
,
,最后终于找到了它的原型链的根源.所以针对上面的,我做了个原型链的图:
- 依次对每一层原型做一个概述
- 第一层原型:HTMLDivElement.prototype 这是所有的div的共有属性,不用细看
- 第二层原型:HTMLElement 这是所有的HTML的共有属性,也不用细看
- 第三层原型Element.prototype
这里面是所有XML,HTML标签等的共有属性。
在这里主要对属性的操作的方法有:
getAttribute/setAttribute, removeAttribute, querySelector/querySelector, scrollBy/scrollTo - 第四层原型是Node.prototype 这里面是所有节点共有属性,节点包括: appendChild, removeChild, replaceChild, cloneNode, contains, insertBefore
- 第五层的原型是EventTarget addEventListener,removeEventlistener,dispatchEvent 这里面最重要的是addEventListener
- 最后的Object那就看原型链去吧
所以,彻底明白了吧,所有的DOM元素,其实就是一堆原型链链接的Object而已。
得到的元素都有自己的属性,可以用nodeType来查看得到的属性的值。这边只要记住两个,nodeType为1表示是Element,3表示是Text
这是实际操作的代码
节点的增删改查
增
- 创建一个标签节点
- document.createElement('style')
- document.createElement('script')
- document.createElement('li')
- 创建一个文本节点
- text = document.createTextNode('你好')
- 标签里面插入文本
- appendChild(text)
- innerHTML或者textContent(注意这个方法的性能,当innerHMTL过多的时候,页面会卡顿)
- 在页面中插入
- 首先在JS线程中创建标签
- 必须把它插入到head或者body中,它才能生效
- 方法还是appendChild
注意,appendChild,如果对同一个标签,插入两个一样的div,那么效果是插入到你最后加的元素内
html:
<div id="div1"></div>
<div id="div2"></div>
css:
#div1{
height: 100px;
width:100px;
background-color: red;
}
#div2{
background-color: yellow;
height: 100px;
width:100px
}
JavaScript:
let div = document.createElement("div");
div.innerHTML = "nihao"
div1.appendChild(div);
div2.appendChild(div);
请看此时,新创建的div只出现在div2中,而没有出现在div1中,虽然div1同样增加了一个div。
删
- 下列是删除的方法:
- 删除自己的子节点:parenetNode.removeChild(childNode)
- 删除本节点:selfNode.remove()
- 删除本身的属性:selfNode.removeAttribute() 此时,当我们删除节点上本身的子节点的时候,不会造成JS内创建的子节点的消失。
JavaScript:
+ div2.removeChild(divNew)
+ console.log(divNew)
改
- class
- 改clss:div.className = "red blue";
- 改class:div.classList.add('red')
- 改style:div.style='width:100px'
- 改style的一部分:div.style.width='200px',这个改动的时候,有时候要注意大小写,比如style.backgroundColor
- 改data-*属性:div.dataset.x='xiaoznz'
事件的处理函数
- 默认点击div不会有任何事情发生,很多事件的处理函数默认值都是null.
- 当我们对事件处理函数进行赋值函数的时候,就是将这个函数的地址值传给了事件处理函数。
- 当事件被触发的时候,浏览器就会调用这个函数,此时默认浏览器会给函数传一个this,this就是绑定函数的本身DOM元素。
- 然后浏览器会给这个函数传一个event的参数,它包含了点击时间的所有信息,比如坐标
- 有两种给事件处理函数赋值的方法:
- div.onclick
- div.addEventListener('type','options','useCapture')
对应这个,type是绑定的实践类型,options传定义的方法以及其他参数信息,useCaoture用于事件冒泡
HTML内容的修改: 修改文本的内容 div.innerText = 'xxx', div.textContent = 'xxx'
这两个之前说过了,就跟之前一样
查
- HTML
- 父类 node.parentNode或者node.parentElement
- 爷爷 node.parentNode.parenetNode
- 查子代
node.childNodes或者node.children
对childNodes来说,这个得到一堆子节点,包括文本节点
对children来说,只得到他对应HTML的三个子节点
- 查兄弟姐妹 嵌套之前的得到父类和子代就可以了
- 查看大孩子,最小的孩子 node.firstChild,node.lastChild
- 查看上一个下一个节点 node.previousSibling,node.nextSibling
- 查看类: div.classList,div.getAttribute('class'),div.className
最后因为DOM是一棵树,所以我们用遍历二叉树的方法来写一个遍历DOM的算法吧这边有个坑,就是a.href。如果不一般的,我们去取a.href,会得到一个完整的基于你的根路径的链接,但是很可能你的链接内容并不是这么写的foreach = (node)=>{ console.log(node) if (node.children){ for (let i = 0; i < node.children.length; i++) { foreach(node.children[i]) } } } foreach(main)