JS中DOM

88 阅读12分钟

DOM 的基本概念

  • DOM(Document Object Model): 文档对象模型
  • 其实就是操作 html 中的标签的一些能力
  • 我们可以操作哪些内容
    • 获取一个元素
    • 移除一个元素
    • 创建一个元素
    • 向页面中添加一个元素
    • 给元素绑定一些事件
    • 获取元素的属性
    • 给元素添加一些 css 样式
    • ...
  • DOM 的核心对象就是 document 对象
  • document 对象是浏览器内置的一个对象, 里面存储这专门用来操作元素的各种方法
  • DOM: 页面中的标签, 我们通过 JS 获取到以后, 就把这个对象叫做 DOM 对象

获取非常规元素

  • document.documentElement -> html标签
  • document.head -> head标签
  • document.body -> body标签
    //1.html标签
    console.log(document.documentElement)
    //2.head
    console.log(document.head)
    //3.body
    console.log(document.body)

获取常规元素

  1. 通过ID名获取标签
    • 语法: document.getElementById('ID名')
    • 因为页面中的 ID 是唯一的, 所以获取到的就是一个元素
<body>
    <div id="box"></div>
    <script>
    	var box = document.getElementById('box')
    	console.log(box) // 页面中 ID 为 box 的标签
    </script>
</body>
  1. 通过class名获取标签
    • 语法: document.getElementsByClassName('class名')
    • 注意:因为页面中可能会有多个元素的 class 相同,所以获取到的是一组元素
    • 哪怕页面中只有一个, 获取到的也是一组元素, 只不过这一组元素只有一个 DOM 元素
    • 获取到后,会把元素放在一个长得很像数组一样的数据结构内,但他并不是数组,我们管这种数据结构叫做 伪数组
    • 伪数组: 长得很像数组,也是通过索引排列的,但是没有数组的方法
<body>
<div class="box"></div>
<script>
    var box = document.getElementsByClassName('box')
    console.log(box) // [<div></div>]
    console.log(box[0]) // <div></div>
</script>
</body>
  1. 通过标签名获取
    • 语法: document.getElementsByTagName('标签名')
    • 注意:获取到的也是一个长得很像数组的数据结构,其实就是获取到了一个伪数组
    • 想要准确的获取到标签元素,我们需要通过索引来帮助我们拿到
<body>
<div></div>
<script>
    var box = document.getElementsByTagName('div')
    console.log(box) // [<div></div>]
    console.log(box[0]) // <div></div>
</script>
</body>
  1. 按照选择器的形式来获取元素
    1. querySelector

      • 这个方法允许我们像写css的时候的选择器一样获取标签
      • 注意:这个方法只能获取到一个元素,就是满足条件的第一个元素
      • 语法: document.querySelector('选择器')
    2. querySelectorAll

      • 这个方法与querySelector 选择器类似,只不过会将所有满足条件的元素都获取到,也放在一个伪数组中
      • 注意:这个方法能获取到所有满足条件的元素
      • 语法: document.querySelectorAll('选择器')
console.log(document.querySelector('div'))  // 获取页面中第一个 div 元素
console.log(document.querySelector('.box'))  // 获取页面中第一个 有 box 类名的元素
console.log(document.querySelector('#box'))  // 获取页面中第一个 id 名为 box 的元素
console.log(document.querySelectAll('div')) // 获取页面中的所有的 div 元素
console.log(document.querySelectAll('.div')) // 获取页面中的所有的 类名为 box 的元素

操作属性

innerHTML

  • 获取元素内部的 HTML 解构
<div>
    <p>
        <span>hello</span>
    </p>
</div>
var div = document.querySelector('div')
console.log(div.innerHTML)    // <p><span>hello</span></p>
  • 设置元素内部的 HTML 解构
<div></div>
var div = document.querySelector('div')
div.innerHTML = '<p><span>hello</span></p>'

innerText

  • 获取元素内部的文本(只能获取到文本, 获取不到 html 标签)
<div>
    <p>
        <span>hello</span>
    </p>
</div>
var div = document.querySelector('div')
console.log(div.innerHTML)    // hello
  • 设置元素内部的 HTML 解构
<div></div>
var div = document.querySelector('div')
div.innerHTML = '<p><span>hello</span></p>'
//会把 '<p><span>hello</span></p>' 当作一段文本出现在 div 元素内, 而不会吧 p 解析成标签

getAttribute

  • 获取元素的某个属性(包括自定义属性)
  • 语法: 元素.getAttribute('要查询的属性名')
  • 返回值:查询到属性时返回对应的属性值,没有查询到时直接返回 null
<div a="100" class="box"></div>

var div = document.querySelector('div')
console.log(div.getAttribute('a'))  // 100
console.log(div.getAttribute('class'))  // box
console.log(div.getAttribute('b'))//null

setAttribute

  • 给元素设置的一个属性(包括自定义属性)
  • 语法: 元素.setAttribute('对应的属性名','对应的属性值')
  • 注意: 如果元素没有对应的属性名,那么相当于是新增一个属性
<div></div>

var div = document.querySelector('div')
console.log(div.setAttribute('a', 100))
console.log(div.setAttribute('class', box))
removeAttribute
  • 直接移除元素的某个属性
  • 语法: 元素.removeAttribute('要删除的属性名')
<div a="100" class="box"></div>

var div = document.querySelector('div')
console.log(div.removeAttribute('class'))
H5 自定义属性
  • 在 H5 自定义属性中 data- 表示该属性是一个自定义属性
  • data- 以后的内容才是属性名
  • = 后面的内容才叫做属性值
  • 每一个元素身上天生自带一个属性, 叫做 dataset, 是一个类似对象的数据结构
  • 内部记录的是该元素身上所有 data- 开头的自定义属性
  • 对于该元素 H5 自定义属性的操作, 就是对这个数据结构的操作(对象操作语法)
<div data-a="100" class="box"></div>

var oDiv = document.querySelector('div')
// 增
oDiv.dataset.age = 18
// 删
delete oDiv.dataset.a
// 查
console.log(oDiv.dataset.age)

元素行内样式 style

  • 专门用来个元素添加 css 样式的
  • 添加的都是行内样式
  • 语法:元素.style.某个属性
  • 我们也可以给这个语法重新赋值,达到修改元素样式的效果(修改的是行内样式)
<div></div>

var div = document.querySelector('div')
div.style.width = '100px'
div.style.height = '100px'
div.style.backgroundColor = 'pink'
console.log(oDiv.style['backgroundColor']) // 中括号语法

获取非行内样式

getComputedStyle (非IE使用)
  • 语法: window.getComputedStyle(元素).要查询的css属性
  • 注意: 这种方式获取到的属性 是只读的
currentStyle (IE使用)
  • 语法: 元素.currentStyle.要获取的属性

操作元素的 类名

  1. className
  • 语法:元素.className
  • 我们也可以给她重新赋值,达到修改元素的类名
<div class='box'></div>

var div = document.querySelector('div')
console.log(div.className)  // box
div.className = 'ceshi'
console.log(div.className)  // ceshi
//在设置的时候, 不管之前有没有类名, 都会全部被设置的值覆盖
  1. classList
    //0. 获取元素
    var oDiv = document.querySelector('.box')
    console.log(oDiv.className)
    oDiv.className = 'qwer'

    //2.1 获取
    // console.log(oDiv.classList)

    // //2.2 新增
    // oDiv.classList.add('qwer')

    // //2.3 删除
    // oDiv.classList.remove('new_box')

DOM 节点

  • 一般来说我们分为三个大类: 元素节点(标签) / 文本节点(标签内的文字) / 属性节点(标签上的属性)
  • 元素节点
    • 通过 getElementBy... 获取到的都是元素节点
  • 属性节点
    • 通过 getAttribute 获取到的就是元素的属性节点
  • 文本节点
    • 通过 innerText 获取到的就是元素的文本节点

获取节点

childNodes: 获取某一节点下 所有的子一级 节点

  • 语法: 元素.childNodes
<div>
    <p>hello</p>
</div>;

var pDiv = document.querySelector("div");
console.log(pDiv.childNodes);
/**
 *  NodeList(3) [text, p, text]
 *
 *  0 : text
 *  1 : p
 *  2 : text
 *  length : 3
 */
  • 拿到的是一个伪数组, 里面有三个节点
  • 一个 text: 从 <div> 一直到 <p> 中间有一个换行和一堆空格, 这是第一个节点, 是一个文本节点
  • 一个 p: 这个就是 p 标签, 他是第二个节点, 这是一个元素节点
  • 又一个 text: 从 </div> 一直到 </p> 中间有一个换行和一堆空格, 是一个文本节点

children

  • 获取某一节点下所有的子一级 元素节点
  • 语法: 元素.children
  • 获取到的是一个伪数组
<div>
    <p>hello</p>
</div>;

var pDiv = document.querySelector("div");
console.log(pDiv.children);
/**
 *  HTMLCollection(1) [p]
 *
 *  0 : p
 *  length : 1
 */
  • 因为 children 只要元素节点 所以就只拿到了 div 下边的唯一一个 元素节点, 也就是 p
  • 虽然获取到的只有一个, 但也是一个 伪数组

firstChild

  • 获取某一节点下子一级的 第一个节点
  • 语法: 元素.firstChild
<div>
    <p>hello</p>
</div>;

var pDiv = document.querySelector("div");
console.log(pDiv.firstChild);
// #text
  • 这个是只获取第一个节点, 所以不再是伪数组了
  • 根据标签结构我们可以得知, 第一个节点是从 <div> 一直到 <p> 的那个换行和空格, 是个文本节点

lastChild

  • 获取某一节点下子一级的 最后一个节点
  • 语法: 元素.lastChild
<div>
    <p>hello</p>
</div>;

var pDiv = document.querySelector("div");
console.log(pDiv.lastChild);
// #text
  • 这个是只获取最后一个节点, 所以不再是伪数组了
  • 根据标签结构我们可以得知, 最后一个节点是从 </div> 一直到 </p> 的那个换行和空格, 是个文本节点

firstElementChild

  • 获取某一节点下子一级的 第一个元素节点
  • 语法: 元素.firstElementChild
<div>
    <p>hello</p>
</div>;

var pDiv = document.querySelector("div");
console.log(pDiv.firstElementChild);
  • 这个是只获取第一个节点, 所以不再是伪数组了
  • 获取的是第一个元素节点, 所以是 p 标签

lastElementChild

  • 获取某一节点下子一级的 最后一个元素节点
  • 语法: 元素.lastElementChild
<div>
    <p>hello</p>
</div>;

var pDiv = document.querySelector("div");
console.log(pDiv.lastElementChild);
  • 这个是只获取最后一个节点, 所以不再是伪数组了
  • 获取的是最后一个元素节点, 所以是 p 标签

获取兄弟节点

nextSibling

  • 获取某一个节点的 下一个兄弟节点
  • 语法:元素.nextSibling
<ul>
    <li id="a">hello</li>
    <li id="b">world</li>
    <li id="c">!!!</li>
</ul>;

var oLi = document.querySelector("#b");
console.log(oLi.nextSibling); // #text
  • 只获取一个节点,不再是伪数组
  • 获取的是 id="b" 这个 li 的下一个兄弟节点
  • 因为 id="b" 的下一个节点,是两个 li 标签之间的换行和空格,所以是一个文本节点

previousSibling

  • 获取某一个节点的 上一个兄弟节点
  • 语法:元素.previousSibling
<ul>
    <li id="a">hello</li>
    <li id="b">world</li>
    <li id="c">!!!</li>
</ul>;

var oLi = document.querySelector("#b");
console.log(oLi.previousSibling); // #text
  • 只获取一个节点,不在是伪数组
  • 获取的是 id="b" 这个 li 的上一个兄弟节点
  • 因为 id="b" 的上一个节点,是两个 li 标签之间的换行和空格,所以是一个文本节点

nextElementSibling

  • 获取对应的 下一个兄弟元素节点
  • 语法:元素.nextElementSibling
<ul>
    <li id="a">hello</li>
    <li id="b">world</li>
    <li id="c">!!!</li>
</ul>;

var oLi = document.querySelector("#b");
console.log(oLi.nextElementSibling); // <li id="c">!!!</li>
  • 只获取一个节点,不在是伪数组
  • 获取的是 id="b" 这个 li 的下一个兄弟元素节点
  • 因为 id="b" 的下一个节点,是 id="c" 的 li, 所以应该打印 li

previousElementSibling

  • 获取某一个节点的 上一个兄弟元素节点
  • 语法:元素.previousElementSibling
<ul>
    <li id="a">hello</li>
    <li id="b">world</li>
    <li id="c">!!!</li>
</ul>;

var oLi = document.querySelector("#b");
console.log(oLi.previousElementSibling); // <li id="a">hello</li>
  • 只获取一个节点,不在是伪数组
  • 获取的是 id="b" 这个 li 的上一个兄弟元素节点
  • 因为 id="b" 的上一个节点,是 id="a" 的 li, 所以应该打印 li

获取父节点与属性节点

获取某一个节点的 父节点 parentNode

  • 语法: 元素.parentNode
<ul>
    <li id="a">hello</li>
    <li id="b">world</li>
    <li id="c">!!!</li>
</ul>;

var oLi = document.querySelector("#b");
console.log(oLi.parentNode); // <ul>...</ul>
  • 只获取一个节点,不在是伪数组
  • 获取的是当前这个 li 的父元素节点
  • 因为这个 li 的父亲就是 ul,所以获取到的就是 ul,是一个元素节点

获取某一个 元素节点 的所有 属性节点

  • 语法: 元素.attributes
<ul>
    <li id="a" a="100" test="test">
        hello
    </li>
</ul>;

var oLi = document.querySelector("#a");
console.log(oLi.attributes); // NamedNodeMap {0: id, 1: a, 2: test, id: id, a: a, test: test, length: 3}
  • 获取该元素的所有属性, 也是一个伪数组
  • 这个 li 有三个属性 id; a; test, 所以就获取到了这三个

节点属性

1. nodeType 节点类型

  • 节点中的一个属性 nodeType 能够区分当前节点是什么类型
  • nodeType === 1 就表示该节点是一个 元素节点
  • nodeType === 2 就表示该节点是一个 属性节点
  • nodeType === 3 就表示该节点是一个 文本节点

2. nodeName 节点名称

  • 元素节点的 nodeName 就是 大写标签名
  • 属性节点的 nodeName 就是 属性名
  • 文本节点的 nodeName 就是 #text

3. nodeValue 节点的值

  • 元素节点 没有 nodeValue
  • 属性节点的 nodeValue 就是 属性值
  • 文本节点的 nodeValue 就是 文本内容 | - | nodeType | nodeName | nodeValue | | -------- | -------- | ---------- | --------- | | 元素节点 | 1 | 大写标签名 | null | | 属性节点 | 2 | 属性名 | 属性值 | | 文本节点 | 3 | #text | 文本内容 |

操纵 DOM 节点

  • 常规意义上 '增删改查'
  • 在增加节点前, 我们应该先有一个节点, 所以应该是
  1. 创建
  2. 向页面中增加一个节点
  3. 删除页面中的某一个节点
  4. 修改页面中的某一个节点
  5. 获取页面中的某一个节点

创建节点

  • 语法:document.createElement('要创建的标签名称')
var oDiv = document.createElement('div')
console.log(oDiv)   // <div></div>
  • createTextNode 创建一个文本节点
var oDiv = document.createTextNode('我是一个文本节点')
console.log(oDiv)   // 我是一个文本节点

向页面增加一个节点

  • 语法1:元素.appendChild(要添加的节点)
  • 作用:向元素的末尾追加一个节点
  • 语法2:元素.insertBefore(要插入的节点, 插入到哪个节点的前面)
  • 注意:两个参数都是必填项
    • 第二个参数传递正常节点时,代表插入到这个节点的前面
    • 第二个参数传递的是 null 时,代表插入到 "元素"的末尾
<div>
    <p>我是一个 p 标签</p>
</div>
var oDiv = document.querySelector('div')
var oP = oDiv.querySelector('p')

// 创建一个元素节点
var oSpan = document.createElement('span')

// 将这个元素节点添加到 div 下的 p 的前面
oDiv.insertBefore(oSpan, oP)

console.log(oDiv)
/**
 *  <div>
 *      <span></span>
 *      <p>我是一个 p 标签</p>
 *  </div>
*/

删除页面某一个节点

  • 语法1: 父节点.removeChild(要删除的子节点)
  • 语法2: 节点.remove()
<div>
    <p>我是一个 p 标签</p>
</div>
var oDiv = document.querySelector('div')
var oP = oDiv.querySelector('p')

oDiv.removeChild(oP)
console.log(oDiv) // <div></div>

修改页面某一个节点

  • 语法: 父节点.replaceChild(新节点,旧节点/要被修改的节点)
  • 作用:将页面中的某一个节点 做一个替换
<div>
    <p>我是一个 p 标签</p>
</div>
var oDiv = document.querySelector('div')
var oP = oDiv.querySelector('p')
// 创建一个 span 节点
var oSpan = document.createElement('span')
// 向 span 元素中加点文字
oSpan.innerHTML = '我是新创建的 span 标签'

// 用创建的 span 标签替换原先 div 下的 p 标签
oDiv.replaceChild(oSpan, oP)

console.log(oDiv)
/*
    <div>
        <span>我是新创建的 span 标签</span>
    </div>
*/

克隆页面某一个节点

  • 语法: 节点.cloneNode(参数)
  • 参数:
    • 默认是 false,表示不可隆后代节点
    • 选填是 true,表示克隆后代节点

获取元素偏移量

offsetParent

  • 获取元素的相对父级
  • 其实就是假设你要给一个元素 绝对定位 的时候
  • 它是根据谁来进行定位的, 那么这个元素的偏移量参考父级就是谁

offsetLeft / offsetTop

  • 获取的是元素左边的偏移量和上边的偏移量
    • offsetLeft 该元素相对于参考父级的左侧偏移量
    • offsetTop 该元素相对于参考父级的上侧偏移量

获取元素尺寸(元素的占地面积)

offsetWidth 和 offsetHeight

  • offsetWidth: 获取元素内容 + padding + border 的宽度
  • offsetHeight: 获取元素内容 + padding + border 的高度

clientWidth 和 clientHeight

  • clientWidth 获取元素内容 + padding 的宽度
  • clientHeight 获取元素内容 + padding 的高度
  • 注意:
  • 获取到的尺寸是没有单位的数字
  • 当元素在页面中不占位置的时候, 获取到的是 0
    • display: none 元素在页面不占位
    • visibility: hidden 元素在页面占位

获取浏览器窗口尺寸

  • 1.window.innerxxx -> 计算的时候 会包含浏览器的滚动条
    1. document.documentElement.clientxxx -> 计算的时候,不会计算滚动条(只计算浏览器的可视区域)
    • document.documentElement.clientWidth: 浏览器可视窗口的宽度
    • document.documentElement.clientHeight: 浏览器可视窗口的高度