DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。

它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。

  基础 DOM 属性(常识性知识点):

方法 描 述
document.write 页面上输出值
document.title 获取文档的 title 元素
document.URL 获取文档的 URL
document.cookie 获取文档的 cookie

# DOM 简介

  简单是说:DOM 就是操作 html 中标签的能力。

# 1、节点(node)

  DOM 的最小组成单位叫做节点(node)。节点的类型有七种 ,如下:

  • Element:元素节点(1)
  • Attr:属性节点(2)
  • Text:文本节点(3)

  • Comment:注释节点(8)
  • Document:文档节点(9)
  • DocumentType:文档类型节点(10)
  • DocumentFragment:文档片断节点(11)

注意

  浏览器提供一个原生的节点对象 Node,上面这七种节点都继承 ✍ 了 Node,因此具有一些共同的属性和方法。

# 2、DOM 节点

  首先,我们要知道关于 DOM 的节点,我们一般将其分为三大类:元素节点属性节点文本节点

<body>
  <div id="box">
    <h1>欢迎访问我的页面</h1>
    <p>这是我网页上的一段!</p>
    <!-- 无序列表 -->
    <ul>
      <li>item1</li>
      <li>item2</li>
      <li>item3</li>
    </ul>
  </div>
  <script>
    console.log(box.childNodes) // 打印所有节点
    // 打印结果:NodeList(9) [text, h1, text, p, text, comment, text, ul, text]

    console.log(box.children) // 打印 元素节点 ✔
    // 打印结果:HTMLCollection(3) [h1, p, ul]

    // 使用
    console.log(box.children[1].innerHTML)
    // "这是我网页上的一段!"
  </script>
</body>

通过来说,元素节点是我们重点关注的

  当然,想要详细一点的话,可以加上document节点注释节点

# 一、元素节点(1)

  在学习节点操作时,有时涉及所有节点,有时只涉及元素节点。

  所以关于节点操作,统一放在这里记录 😂。

# 1、获取元素

  • 直接引用元素对象(不推荐)
<body>
  <div id="box"></div>
  <script>
    console.log(box) // 🚩 <div id="box"></div>

    box.innerHTML = 'lencamo'
  </script>
</body>

注意

  使用 document.getElementById()方法获取元素对象与使用直接引用元素对象之间的区别在于:

  • 直接引用元素的方式需要在使用它之前先找到它的位置,并且如果 HTML 结构发生更改,则可能需要更新引用。
  • 通过使用 document.getElementById(),您可以确保始终获取正确的元素对象,无论其在文档中的确切位置如何。

  综上,在实际开发中推荐使用 document.getElementById()方法获取元素对象(比如后面节点的增删改查)

  • 常规方式
// 1、唯一
document.getElementById()
// 2、伪数组(NodeList实例)(有length)
var items = document.getElementsByClassName()
var items = document.getElementsByTagName() // 根据标签名
var items = document.getElementsByName() // 表单中的name属性

// 转换为真数组
// var newitems = Array.from(items)
  • H5 方式

CSS 获取节点的方式在 js 中进行应用

// 返回一个
document.querySelector()

// 返回所有
document.querySelectorAll()

# 2、节点的获取

html 结构
<body>
  <div>
    <div id="header">网页头部区域</div>
    <div id="box">
      <h1>欢迎访问我的页面</h1>
      <p>这是我网页上的一段!</p>
      <!-- 无序列表 -->
      <ul>
        <li id="one">item1</li>
        <li>item2</li>
        <li>item3</li>
      </ul>
      <p>这是一个简单的段落。</p>
    </div>
    <div id="aside">侧边栏区域</div>
    <div id="footer">网页底部区域</div>
  </div>
</body>

  关于后代、兄弟间元素的获取,其实采用 css 选择器中的关系选择器伪类选择器会根据灵活一些。

因此下面这部分用的很少

/* 后代(关键字:子元素) */
// 1、第一个子元素
console.log(box.firstElementChild)

// 2、最后一个子元素
console.log(box.lastElementChild)

/* 兄弟(关键字:相邻) */
// 1、上铺的第一个兄弟
console.log(box.previousElementSibling)

// 2、下铺的第一个兄弟
console.log(box.nextElementSibling)

  但想较于 CSS 选择器,使用 js 方法的一个好处就是它可以操作元素的父子关系

/* 父子(关键字:相邻)🚩 */
// 1、父节点
// 非元素节点(如文本节点、注释节点等),它们并没有parentElement属性
console.log(one.parentNode)

// 2、子节点
// 指的是第一个子节点
console.log(box.children[0])

提示

children 获取的是子元素集合对象 HTMLCollection

完整版

  学习这部分内容,可以与 css 选择器中的关系选择器伪类选择器联想、比较一下 🤔。

/* 后代(关键字:子元素) */
// 1、第一个子元素 —— :first-child……
console.log(box.firstChild) // #text
console.log(box.firstElementChild) // h1 ✔

// 2、最后一个子元素 —— :last-child……
console.log(box.lastChild) // #text
console.log(box.lastElementChild) // p ✔

/* 兄弟(关键字:相邻) */
// 1、上铺的第一个兄弟
console.log(box.previousSibling) // #text
console.log(box.previousElementSibling) // div#header ✔

// 2、下铺的第一个兄弟
console.log(box.nextSibling) // #text
console.log(box.nextElementSibling) // div#aside ✔

  相较于 css,js 方法还可以操作元素的父子关系:

/* 父子(关键字:相邻)🚩 */
// 1、父节点
// 非元素节点(如文本节点、注释节点等),它们并没有parentElement属性
console.log(one.parentNode) // ul ✔ ✨
console.log(one.parentElement) // ul

// 2、子节点
// 指的是第一个子节点
console.log(box.childNodes) // NodeList []
console.log(box.children) // HTMLCollection [] ✔ ✨

# 二、节点操作

前面是节点的一些属性操作,下面要学习的是一些方法操作

# 1、创建新节点

// 1、createElement
var odiv = document.createElement('div')

// 2、模板字符串
var odiv = `<div></div>`

# 2、插入子节点 ✨

注意

  如果给定的子节点是对文档中现有节点的引用,appendChild、🤔insertBefore() 会将其从当前位置移动到新位置。

  所以要重复使用同一个节点时,需要使用可以使用 Node.cloneNode() 复制节点

createElmClone = createElm.cloneNode(true)
<div id="box">
  <div id="child">欢迎访问我的页面</div>
</div>
<script>
  const boxElm = document.querySelector('#box')
  const childElm = document.querySelector('#child')

  // =====================

  // 方式1 👀
  var createElm = document.createElement('span')
  createElm.className = 'newSpan'
  createElm.style['background-color'] = 'green'
  createElm.innerHTML = 'span标签的内容'

  // —— 在末尾插入
  boxElm.appendChild(createElm)
  // —— 指定位置插入
  createElmClone = createElm.cloneNode(true)
  boxElm.insertBefore(createElmClone, childElm)

  // =====================

  // 方式2 👀
  var strElm = `<span class="newSpan" style="background-color:green">span标签的内容</span>`

  // —— 全局覆盖插入
  boxElm.innerHTML = strElm
  // —— 尾部追加
  boxElm.innerHTML += strElm
</script>

思考?

如何将strElm字符串转换为,一个真正的 DOM 节点对象

答案

  简单方法就是,在将 strElm 包裹在一个 DOM 节点中

步骤:

  • 先使用 createElement 创建一个 template 节点
  • 然后使用 innerHTML 向 template 节点中填充strElm字符串即可

# 3、删除子节点 ✨

<body>
  <div id="box">
    <h1>欢迎访问我的页面</h1>
    <p>这是我网页上的一段!</p>
    <ul>
      <li id="one">item1</li>
      <li>item2</li>
      <li>item3</li>
    </ul>
    <p>这是一个简单的段落。</p>
  </div>
  <script>
    const ulElm = document.querySelector('ul')
    const item1Elm = document.querySelector('#one')

    // 1、删除当前节点
    ulElm.remove()

    // 2、删除指定子节点
    ulElm.removeChild(item1Elm)
  </script>
</body>

# 4、替换子节点

<body>
  <div id="box">
    <p>-----</p>
    <div id="child">欢迎访问我的页面</div>
    <p>-----</p>
  </div>
  <script>
    const boxElm = document.querySelector('#box')
    const childElm = document.querySelector('#child')

    // 创建的新节点
    var newElm = document.createElement('span')
    newElm.className = 'newSpan'
    newElm.style['background-color'] = 'green'
    newElm.innerHTML = '替换操作测试的新增span节点'
    // console.log(newElm) // span.newSpan

    // 1、替换子节点
    boxElm.replaceChild(newElm, childElm)
  </script>
</body>

# 5、克隆节点

<body>
  <div id="box">
    <h1>欢迎访问我的页面</h1>
    <p>这是我网页上的一段!</p>
    <ul id="list">
      <li id="one">item1</li>
      <li>item2</li>
      <li>item3</li>
    </ul>
    <p>这是一个简单的段落。</p>
  </div>
  <script>
    const ulElm = document.querySelector('ul')

    // 1、克隆当前节点
    const cloneElm1 = ulElm.cloneNode()
    console.log(cloneElm1) // <ul id="list"></ul>

    // 2、克隆完整节点(含子节点)
    const cloneElm2 = ulElm.cloneNode(true)
    console.log(cloneElm2) // <ul id="list">...</ul>
  </script>
</body>

应用:

// 1、待办事项
// 2、评论的动态删除

# 三、属性节点(2)

🎉🎉 属性的操作 (opens new window)

# 1、属性使用

  • 常规使用
<input type="checkbox" checked id="rember" />
<img src="" alt="" id="photo" />

<script>
  // 原生属性是可以直接在DOM对象上获取的
  rember.checked = false
  photo.src = 'https://www.gov.cn/govweb/xhtml/2016gov/images/public/logo.jpg'
</script>

<!-- 
  应用:
// 1、密码的显示与隐藏
// 2、全选和全不选的切换
-->
  • 自定义属性

h5 上明确规矩:自定义属性前添加data-来与其他属性进行区分

<div id="box1">采用js方法</div>
<div id="box2">采用H5挂载</div>

<script>
  // 1、使用js方法
  box1.setAttribute('effect', 'copy')
  console.log(box1) // <div id="box1" effect="copy">采用js方法</div>

  box1.getAttribute('effect')
  box1.removeAttribute('effect')

  // 2、使用H5🎈挂载
  box2.dataset.effect = 'copy'
  console.log(box2) // <div id="box2" data-effect="copy">采用H5挂载</div>

  box2.dataset.effect
  delete box.dataset.effect

  // 其他
  Element.hasAttribute('effect')
</script>
  • 应用
// 批量操作 CSS 样式
box.setAttribute('style', 'width:100px;' + 'background-color:skyblue;')

# 2、元素样式 ✨

// 1、行内样式
// 获取
console.log(box.style['background-color'])
console.log(box.style.backgroundColor)

// 修改/添加
box.style.width = '60px'

// 2、css样式
var styleObj = window.getComputedStyle(box) // // CSSStyleDeclaration 实例
styleObj.backgroundColor
window.getComputedStyle() 方法

  其中,Element.style返回的只是行内样式,并不是该元素的全部样式。通过 css 文件设置的样式、父元素继承的样式,Element.style是无法获得的,因为它操作的只是行内样式

  但我们可以通过window.getComputedStyle()来获取元素的全部样式了。(只读的)

# 3、class 类名 🎉

  • Element.className

可以联想一下前面的 innerHTML

<div>
  <span id="icon" class="iconfont icon-yijieshu"></span>
</div>

<script>
  // 1、覆盖
  icon.className = 'iconfont icon-yibaoming'
  // console.log(icon.className)

  // 2、追加
  icon.className += ' personLook' // 注意加一个👀空格
</script>
  • Element.classList

  通过打印box.classList,我们可以发现:获取的是一个DOMTokenList集合(一个 class 类名集合对象)

DOMTokenList 集合
<div>
  <div id="box" class="item item1 item2"></div>
</div>

<script>
  console.log(box.classList)
  // [object DOMTokenList]
  // {
  //   "0":"item",
  //   "1":"item1",
  //   "2":"item2"
  // }
</script>

提示

  使用 classList 比上面的 className 操作 class 要 NB 的一点是已经存在于元素的 class 属性不会被重复添加,并且不存在覆盖问题。

// 1、常规
box.classList.add('item3')
box.classList.remove('item3')

// 2、切换
box.classList.toggle('itme3') // 将某个 class 移入或移出当前元素

// 3、其他
box.classList.contains() // 检查当前元素是否包含某个 class
  • 应用
// 1、导航栏切换(action样式切换)
// 2、选项卡组件(样式、内容同时变化)😂

通过下面原生 js 的编写方式,我们就可以感受到 🤔 jQuery 的链式编程隐式迭代的强大。

答案
  • 原生方式
let tabNodeList = document.querySelectorAll('.tabs > li')
let containNodeList = document.querySelectorAll('#content > li')

// 事件绑定(for一层)
for (let i = 0; i < tabNodeList.length; i++) {
  tabNodeList[i].onclick = function () {
    // 排它思想(for二层)
    for (let j = 0; j < tabNodeList.length; j++) {
      tabNodeList[j].classList.remove('active')
      containNodeList[j].classList.remove('active')
    }

    tabNodeList[i].classList.add('active')
    containNodeList[i].classList.add('active')
  }
}
  • jQuery 方式
// 事件绑定
$('.tabs').on('click', 'li', function () {
  // 排它思想
  $(this).addClass('active').siblings().removeClass('active')

  const index = $(this).index()
  $('#content > li').eq(index).addClass('active').siblings().removeClass('active')
})

# 四、文本节点(3)

Element 节点 (opens new window)

# 1、文本内容

注意

  不管是 innerText、还是 innerHTML,都其打印/传入的内容必须是字符串

<body>
  <div id="box">
    hello world
    <div>lencamo</div>
  </div>

  <script>
    // 1、打印
    console.log(box.innerHTML) // 获取所有

    console.log(box.innerText) // 不解析 html

    // 2、修改(全部覆盖)
    box.innerHTML = '呵呵'
  </script>
</body>

# 2、value 属性

<body>
  <select name="" id="select">
    <option value="1">one</option>
    <option value="2" selected>two</option>
    <option value="3">three</option>
  </select>

  <script>
    // 更改默认值 🤔
    // console.log(select.value)

    select.value = '3' // 注意匹配字符串
  </script>
</body>
更新于 : 8/7/2024, 2:16:31 PM