1.什么是DOM(DOM很难用,要用别的东西来操作DOM,不用DOM自带的)

1.1.网页其实就是一棵树

image.png

1.2.JS如何操作这棵树(DOM的由来)

  • 浏览器往 window 上加一个 document

image.png

  • JS用document 操作网页—这就是 Document Object Model (文档对象模型)

DOM作用:把网页变成一个对象,方便我们操作

2.获取元素的API

2.1.获取任意元素

image.png
如何选择用哪一种

  • 工作中只用最后两种来获取
  • 自己写demo图方便快捷可以用直接用id来获取(这种方法可能部分人根本不知道,可能会觉得你是野路子)
  • 最后中间的三种 get 方法兼容 IE 才用
  • 第一种:window.idxxx 或者直接 idxxx

获取 id = “u1” 的标签
image.png

  • 这种方法大部分时候可以用,只要 id 不跟全局属性冲突就行
  • 比如下面这种 window.parent 就不行,这是一个全局属性,如果有id叫这个,就要用下面那种get方法

image.png

  • 第二种:**document.getElementById('idxxx')**—获取 id

image.png

  • 第三种:**document.getElementsByTagName('div')[0]—获取标签名**
  • 注意中间比第二种多了个 s ,意思就是获取全部的 div (此时是一个伪数组),然后用获取数组里的哪一项来获取对应的 div
  • 后面的 0 不能省略
  • 第四种:**document.getElementsByClassName('bottom')[0]—获取类名**
  • 意思就是获取所有 class = “bottom” 的类
  • 第五种:document.querySelector('#idxxx')—获取单个标签(不加其他的就只会获取找到的第一个)

这种写法括号里可以用 JS 里的语法(比较活灵活现)

  • id加 # class加 . body、head加了引号就行
  • [x] 选择器写法 document.querySelector('div>span:nth-child(2)')找到所有第一个div里的第二个span元素(加All的话就是找到所有的div里的第二个span元素)

  • 第六种:document.querySelectorAll('')—获取所有符合条件的标签

  • [x] 一些常用操作示例如下:

    1. document.querySelectorAll("p") //返回所有的p标签
    2. document.querySelectorAll("div.note, div.alert") //返回所有class为note和alert的div
  • [x] 可以在最后面加数组的下标选择—-document.querySelectorAll('')[0]

    1. document.querySelectorAll('div>span:nth-child(2)')[1]
    2. //此时就不是选择所有的div里的第二个span元素,而是在[1]之前的获取到的NodeList这个类似数组的对象,找到其中的获取到的第二个元素

    2.2.获取特定元素(叫元素或标签都可以)

    image.png

  • 获取 html 元素(并不是说获取到了html里的所有元素,注意区分)

document.documentElement

  • 获取 head 元素

document.head

  • 获取 body 元素

document.body

  • 获取窗口

window
image.png每点一次屏幕都会打印一次 hi

  • 获取所有元素

document.all第六个false值
image.png
image.png

  • 可以区分是否是IE浏览器,但是也可以直接使用
  • [x] 多一个false值,document.all

    2.3.元素的六层原型链(我们获取到的元素到底是什么呢)

    image.png

  • 第一层:div.__proto__===HTMLDivElement.prototype (用原型公式就知道了)

HTML里div元素的共有属性
浏览器上显示的是错的,如下图
image.png

  • 第二层:HTMLElement.prototype—所有HTML元素的共有属性

同上,浏览器都没有加后面的 .prototype

  • 第三层:Element.prototype—所有HTML、XML等元素的共有属性
  • 第四层:Node.prototype—所有节点的共有属性(节点主要就是包括元素和文本)
  • 节点类型详见 MDN

image.png

  • [x] xxx.nodeType 可以查看节点类型

    1. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597500239072-92fe5967-c3c9-48fb-a168-774d3b45c5b6.png#align=left&display=inline&height=157&margin=%5Bobject%20Object%5D&name=image.png&originHeight=313&originWidth=444&size=48699&status=done&style=none&width=222)
  • 第五层:EventTarget.prototype—就三个

image.png

  • 第六层(最后一层):Object.prototype—根对象(再往下就没有了,null)

    div完整的原型链如下:

    image.png

    3.节点的增删改查

    3.1.增

    image.png

    3.1.1.创建一个标签节点

    1. let div1 = document.createElement('div') //括号里是不同的标签
    2. document.createElement('style')
    3. document.createElement('script')
    4. document.createElement('li')

    3.1.2.创建一个文本节点

    1. let text1 = document.createTextNode('你好') //括号里是不同的字符串

    image.png

    3.1.3.标签里面插入文本

    1. div1.appendChild(text1)
    2. div.innerText='你好' //div.textContent='你好'

    两种不能串用:div1.appendChild('你好')

    3.1.4.插入到页面中(上面的做法在页面是看不见的,要插入到页面才能看得见)

    1. document.body.appendChild(div1)
    2. document.head.appendChild(div1) //注意head里的是看不见的

    image.png

  • 插入页面之后,再加入样式就可以看的到了

image.png

3.1.5.关于appendChild

image.png
image.png

3.2.删

  • 两种删除方法

    image.png
    代码如下:

    1. div1.parentNode.removeChild(div1) //找到该节点的爸爸,然后删除里面的该节点
    2. div1.remove() //新方法,IE不兼容,简单快捷

    注:当把一个node移出页面(DOM树)之后,还可以再回到页面中(移出了,该node还在内存里,还可以再把它调用出来)
    如下图(删除之后再添加还是可以的):
    image.png

    3.3.改

    image.png

    3.3.1.写标准属性

  • 改class

    1. div1.className = 'red'
    2. div1.className += ' blue' //或者直接 div1.className = 'red blue' 覆盖前面的
    3. div1.classList.add('gg')
    4. //前面已经有了red和blue,这种方法再加red和blue不会改变什么,加不同的才会改变

    className有一个bug,后面的会覆盖前面的,要不然写+=要不然直接把所有的class都加上
    image.png

  • 改style

    1. div1.style = 'width: 100px; color: blue;'
    2. //一般来说不要用这种,会直接覆盖之前的style;在某种意义上来说,这个也算是可以直接添加style
    3. div1.style.width = '200px;'
    4. //只修改width
  • [x] 大小写问题 比如背景色为 background-color 中间有一个中划线,需要写成下面的形式

  • [ ] 不然会被JS理解成前面的东西减去后面的

    1. div1.style.backgroundColor = 'black' //同上面改部分style形式,但是中划线后面的第一个字母大写
    2. div1.style['background-color'] = 'white' //按照JS里的写法就写成这样的,比较麻烦
  • 改 data-* 属性

    1. div1.setAttribute("data-x","test") //会在div1里面添加一个名为 data-x 值为 test 的属性
    2. div1.getAttribute("data-x") //获取data-x
    3. div1.dataset.x = "gg" //.data-x也可以获取,然后直接修改就可以了

    下图为添加 data-x
    image.png
    image.png

    3.3.2.读标准属性

    1. div1.style
    2. div1.id //这是正常的,直接 标签名.属性名
  • class

    1. div1.className
    2. div1.classList //返回数组形式
    3. div1.getAttribute('class') //获取class

    三种不同形式的结果如下:
    image.png

  • a标签

    1. a.href //有可能浏览器会自动加工一下(把前面的域名补全了)
    2. a.getAttribute('href') //获取原本的href

    两种不同的写法结果:
    image.png

    3.3.3.改事件处理函数

    image.png

  • [x] div和event都是浏览器在用户点击div的时候用call传进来的

  • test.onclick.call(test,event) fn 代指这个函数 xxx.onclick

链接
image.png

3.3.4.改内容(即改子代)—JSBin

  • 改文本内容(两者都可使用,几乎没区别)

    1. div.innerText='xxx'
    2. div.textContent='xxx' //前面的div不是说是该标签是个div标签,而是说该标签的id为div,如果没有id会直接报错
  • 改 HTML 内容

    1. div.innerHTML='<strong>重要内容</strong>'
  • [x] 注意这里的 HTML 均为大写

  • 虽然好用,但是里面内容不要太多,最大容量2W字符左右,太多会导致浏览器卡顿
  • 里面可以添加多层任意标签内容,不仅仅是只能用一个标签
  • 改标签

    1. div.innerHTML='' //先清空
    2. div.appendChild(div2)//再添加,div2是一个节点
  • 上面三个都是改儿子,那么能不能改爸爸呢

    1. newParent.appendChild(div) //直接这样就从原来的地方消失了,到了新的地方
  • [x] newParent 代指新爸爸的 id

  • [x] div 是该标签的 id

    3.4.查

    查爸爸;查爷爷

    1. //查爸爸
    2. node.parentNode
    3. node.parentElement
    4. //查爷爷(调用两次node)
    5. node.parentNode.parentNode

    查子代—JSBin

    1. node.childNodes
    2. node.children //优先使用这种
  • [x] node.childNodes.length 打出来有问题。它把每一块空的地方合并成一个空格,也算是一个长度了

  • 所以优先使用 node.children

image.png
image.png

  • 改变子代时,两种方法都会实时更新
  • [ ] 但是如果是打印 document.querySelectorAll('li').length 只会认定第一次的长度3,后面怎么改都是第一次打印的3

    image.png

    查兄弟姐妹

  • [x] 首先都需要先找到自己的爸爸,再其子元素里找兄弟姐妹

    1. node.parentNode.children //还需要排除自己
    2. node.parentNode.childNodes //不仅要排除自己,还要排除其他的节点(空格等)

    image.png

    1. let siblings = []
    2. let c2 = div1.parentElement.children //先找到它的爸爸,再从爸爸的子代里找兄弟姐妹
    3. for(let i=0;i<c2.length;i++){
    4. if(c2[i] !== div1){
    5. siblings.push(c2[i])
    6. }
    7. }

    查看老大和老幺

    1. //node.children[0]
    2. node.firstChild //查看第一个
    3. node.lastChild //查看最后一个

    这里 node 如果是 div1 了,那就是找 div1 的子元素了
    image.png
    这是在百度页面,我插入了一个div1到head里

    查看当前位置前后的(下面的方法是查找文本节点)

    1. node.previousSibling //查看前一个兄弟
    2. node.nextSibling //查看后一个兄弟

    image.png

  • [x] 如果不想要文本节点,在 Sibling 前面再加一个 Element 也行,表示元素兄弟

  • node.previousElementSibling
  • node.next``Element``Sibling

    汇总:

    image.png

    遍历一个 div 里的所有元素

    1. travel = (node, fn) => {
    2. fn(node)
    3. if (node.children) {
    4. for (let i = 0; i < node.children.length; i++) {
    5. travel(node.children[i], fn) //递归
    6. }
    7. }
    8. }
    9. travel(div1, (node) => console.log(node)) //fn在这里定义是什么函数了
    同数据结构里的tree — 链接
    DOM也都是树结构