JavaScript的DOM操作

元素的继承

     class Student extends Person{} 创建一个Student对象继承自Person对象本身会自带Person的属性并且可以创建属于自己的属性

什么是DOM?

  ◼ 前面我们花了很多时间学习JavaScript的基本语法,但是这些基本语法,但是这些语法好像和做网页没有什么关系,和前面学习
  的HTML、CSS也没有什么关系呢?
      这是因为我们前面学习的部分属于ECMAScript,也就是JavaScript本身的语法部分;
      除了语法部分之外,我们还需要学习浏览器提供给我们开发者的DOM、BOM相关的API才能对页面、浏览器进行操作;
  ◼ 前面我们学习了一个window的全局对象,window上事实上就包含了这些内容:
      我们已经学习了JavaScript语法部分的Object、Array、Date等;
      另外还有DOM、BOM部分;
  ◼ DOM:文档对象模型(Document Object Model)
      简称DOM,将页面所有的内容表示为可以修改的对象;
  ◼ BOM:浏览器对象模型(Browser Object Model)
      简称BOM,由浏览器提供的用于处理文档(document)之外的所有内容的其他对象;
      比如navigator、location、history等对象;

深入理解DOM

  浏览器会对我们编写的HTML、CSS进行渲染,同时它又要考虑我们可能会通过JavaScript来对其进行操作:
      于是浏览器将我们编写在HTML中的每一个元素(Element)都抽象成了一个个对象;
      所有这些对象都可以通过JavaScript来对其进行访问,那么我们就可以通过JavaScript来操作页面;
      所以,我们将这个抽象过程称之为文档对象模型(Document Object Model);
  ◼ 整个文档被抽象到document 对象中:
      比如document.documentElement对应的是html元素;
      比如document.body对应的是body元素;
      比如document.head对应的是head元素;
  ◼ 下面的一行代码可以让整个页面变成红色:
  document.body.style.backgroundColor = "red"
  ◼ 所以我们学习DOM,就是在学习如何通过JavaScript对文档进行操作的;

认识DOM Tree

  ◼ 一个页面不只是有html、head、body元素,也包括很多的子元素:
      在html结构中,最终会形成一个树结构;
      在抽象成DOM对象的时候,它们也会形成一个树结构,我们称之为DOM Tree;

document对象

  ◼ Document节点表示的整个载入的网页,它的实例是全局的document对象:
      对DOM的所有操作都是从document 对象开始的;
      它是DOM的入口点,可以从document开始去访问任何节点元素;
  ◼ 对于最顶层的html、head、body元素,我们可以直接在document对象中获取到:
      html元素:<html> = document.documentElement
      body元素:<body> = document.body
      head元素:<head> = document.head
      文档声明:<!DOCTYPE html> = document.doctype

DOM的整体结构

  ◼ DOM相当于是JavaScript和HTML、CSS之间的桥梁
      通过浏览器提供给我们的DOM API,我们可以对元素以及其中的内容做任何事情;
      var htmlEl = document.documentElement;
      var bodyEl = document.body
      var headEl = document.head
      var docTypeEl = document.doctype

      console.log(htmlEl,bodyEl,headEl,docTypeEl)

节点、元素导航

节点(Node)之间的导航(navigator)

  ◼ 如果我们获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的导航。
  ◼ 节点之间存在如下的关系:
       父节点:parentNode
       前兄弟节点:previousSibling
       后兄弟节点:nextSibling
       子节点:childNodes
       第一个子节点:firstChild
       最后一个子节点:lastChild

元素(Element)之间的导航(navigator)

  ◼ 如果我们获取到一个元素(Element)后,可以根据这个元素去获取其他的元素,我们称之为元素之间的导航。
  ◼ 节点之间存在如下的关系:
       父元素:parentElement
       前兄弟元素:previousElementSibling
       后兄弟元素:nextElementSibling
       子元素:children
       第一个子元素:firstElementChild
       最后一个子元素:lastElementChild

表格(table)元素的导航(navigator)

  ◼ <table> 元素支持 (除了上面给出的,之外) 以下这些属性:
      table.rows — <tr> 元素的集合;
      table.caption/tHead/tFoot — 引用元素 <caption>,<thead>,<tfoot>;
      table.tBodies — <tbody> 元素的集合;
  ◼ <thead>,<tfoot>,<tbody> 元素提供了 rows 属性:
      tbody.rows — 表格内部<tr> 元素的集合;
  ◼ <tr>:
      tr.cells — 在给定 <tr> 中的 <td> 和 <th> 单元格的集合;
      tr.sectionRowIndex — 给定的 <tr> 在封闭的 <thead>/<tbody>/<tfoot> 中的位置(索引);
      tr.rowIndex — 在整个表格中 <tr> 的编号(包括表格的所有行);
  ◼ <td> 和 <th>:
      td.cellIndex — 在封闭的 <tr> 中单元格的编号。

表单(form) 元素导航

  1.form 元素可以直接通过document来获取:document.forms
      var formEl = document.forms[0]
  2.form 元素中的内容可以通过elements来获取
      var elements = formEl.elements
  我们可以通过设置表单元素的name来获取它们
      <form action="">
      <input type="text" name="account" >
      <input type="password" name="password" autocomplete="off">
      <input type="checkbox" name="hobbies" checked>
      <select name="fruits" >
        <option value="apple">苹果</option>
        <option value="orange">橘子</option>

      </select>
    </form>
    <script>
    // 获取form元素
      // var  formEl =document.body.firstElementChild
      var  formEl = document.forms[0]
      // 获取form中的子元素
      var inputEl = formEl.elements.account
      // 
      setTimeout(function(){
        console.log(inputEl.value)
      },2000)
    </script>

获取元素的方法

  ◼ 当元素彼此靠近或者相邻时,DOM 导航属性(navigation property)非常有用。
    但是,在实际开发中,我们希望可以任意的获取到某一个元素应该如何操作呢?
  方法名                      搜索方式        可以在元素上调用?    实时的?
  querySelector              CSS-selector          ✔                -
  querySelectorAll           CSS-selector          ✔                -
  getElementById             id                    -                 -
  getElementsByName          name                  -                ✔
  getElementsByTagName      tag or '*'             ✔                ✔
  getElementsByClassName    class                  ✔                ✔

Node节点的属性

  ◼ 目前,我们已经可以获取到节点了,接下来我们来看一下节点中有哪些常见的属性:
       当然,不同的节点类型有可能有不同的属性;
       这里我们主要讨论节点共有的属性;
  ◼ nodeType属性:
       nodeType 属性提供了一种获取节点类型的方法;
       它有一个数值型值(numeric value);

其他类型可以查看MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Node/nodeType

  ◼ nodeName:获取node节点的名字;
  ◼ tagName:获取元素的标签名词;
  ◼ tagName和nodeName之间有什么不同呢?
      tagName属性仅适用于Element 节点;
      nodeName是为任意Node 定义的:
          ✓ 对于元素,它的意义与tagName相同,所以使用哪一个都是可以的;
          ✓ 对于其他节点类型(text,comment 等),它拥有一个对应节点类型的字符串;

节点的属性-innerHTML、textContent

  ◼ innerHTML 属性
      将元素中的HTML 获取为字符串形式;
      设置元素中的内容;
  ◼ outerHTML 属性
      包含了元素的完整HTML
      innerHTML 加上元素本身一样;
  ◼ textContent 属性
      仅仅获取元素中的文本内容;
  ◼ innerHTML和textContent的区别:
      使用innerHTML,我们将其“作为 HTML”插入,带有所有HTML 标签。
      使用textContent,我们将其“作为文本”插入,所有符号(symbol)均按字面意义处理。
  ◼ nodeValue/data
      用于获取非元素节点的文本内容

节点的其他属性

  ◼ hidden属性:也是一个全局属性,可以用于设置元素隐藏。
  ◼ DOM 元素还有其他属性:
      value
          ✓ <input>,<select> 和 <textarea>(HTMLInputElement,HTMLSelectElement……)的 value。
      href
          ✓ <a href="...">(HTMLAnchorElement)的 href。
      id
          ✓ 所有元素(HTMLElement)的“id” 特性(attribute)的值。
  ◼ class和style我们会在后续专门讲解的

元素的特性attribute

  ◼ 前面我们已经学习了如何获取节点,以及节点通常所包含的属性,接下来我们来仔细研究元素Element。
  ◼ 我们知道,一个元素除了有开始标签、结束标签、内容之外,还有很多的属性(attribute)
  ◼ 浏览器在解析HTML元素时,会将对应的attribute也创建出来放到对应的元素对象上。
      比如id、class就是全局的attribute,会有对应的id、class属性;
      比如href属性是针对a元素的,type、value属性是针对input元素的;
  ◼ 接下来我们学习一下如何获取和设置这些属性。

attribute的分类

  ◼属性attribute的分类:
      标准的attribute:某些attribute属性是标准的,比如id、class、href、type、value等;
      非标准的attribute:某些attribute属性是自定义的,比如abc、age、height等;

attribute的操作

  ◼ 对于所有的attribute访问都支持如下的方法:
      elem.hasAttribute(name) — 检查特性是否存在。
      elem.getAttribute(name) — 获取这个特性值。
      elem.setAttribute(name, value) — 设置这个特性值。
      elem.removeAttribute(name) — 移除这个特性。
      attributes:attr对象的集合,具有name、value属性;
  ◼ attribute具备以下特征:
      // 1.所有的attribute都支持的操作
      console.log(boxEl.hasAttribute("age"),boxEl.hasAttribute("abc"),boxEl.hasAttribute("id"))
      console.log(boxEl.getAttribute("age"),boxEl.getAttribute("abc"),boxEl.getAttribute("id"))
      boxEl.setAttribute("id","cba")
      boxEl.removeAttribute("id")

      var boxArributes = boxEl.attributes
      for (var attr of boxArributes){
        console.log(attr.name,attr.value)
      }

      它们的名字是大小写不敏感的(id 与ID 相同)。
      它们的值总是字符串类型的。

元素的属性property

      ◼ 对于标准的attribute,会在DOM对象上创建与其对应的property属性:
        // 获取box元素
        var boxEl = document.querySelector(".box")
        console.log(boxEl.id,boxEl.title,boxEl.age,boxEl.height)
        var inputEl = document.querySelector("input")
        console.log(inputEl.checked)
        if(inputEl.checked){
          console.log(`checkbox处于选中状态`)
        }
      ◼ 在大多数情况下,它们是相互作用的
          改变property,通过attribute获取的值,会随着改变;
          通过attribute操作修改,property的值会随着改变;
            ✓ 但是input的value修改只能通过attribute的方法;
      ◼ 除非特别情况,大多数情况下,设置、获取attribute,推荐使用property的方式:
          这是因为它默认情况下是有类型的

元素的class、style

      ◼ 有时候我们会通过JavaScript来动态修改样式,这个时候我们有两个选择:
          选择一:在CSS中编写好对应的样式,动态的添加class;
          选择二:动态的修改style属性;
      ◼ 开发中如何选择呢?
          在大多数情况下,如果可以动态修改class完成某个功能,更推荐使用动态class;
          如果对于某些情况,无法通过动态修改class(比如精准修改某个css属性的值),那么就可以修改style属性;
        // 1.获取box
        var boxEl = document.querySelector(".box")
        console.log(boxEl)
        boxEl.onclick = function(){
          // 直接修改style属性
          // boxEl.style.color = "red"
          // boxEl.style.fontSize = "24px"
          // boxEl.style.backgroundColor = "green"
          //2. 动态添加class
          boxEl.className = "active"
        }

元素的className和classList

  ◼ 元素的class attribute,对应的property并非叫class,而是className:
      这是因为JavaScript早期是不允许使用class这种关键字来作为对象的属性,所以DOM规范使用了className;
      虽然现在JavaScript已经没有这样的限制,但是并不推荐,并且依然在使用className这个名称;
  ◼ 我们可以对className进行赋值,它会替换整个类中的字符串。
      var boxEl = document.querySelector(".box")
      // 1.方法一:className
      boxEl.className = "abc"
      var obj = {}
  ◼ 如果我们需要添加或者移除单个的class,那么可以使用classList属性。
  ◼ elem.classList 是一个特殊的对象:
      elem.classList.add (class) :添加一个类
      elem.classList.remove(class):添加/移除类。
      elem.classList.toggle(class) :如果类不存在就添加类,存在就移除它。
      elem.classList.contains(class):检查给定类,返回 true/false。
  ◼ classList是可迭代对象,可以通过for of进行遍历。

元素的style属性

  ◼ 如果需要单独修改某一个CSS属性,那么可以通过style来操作:
      对于多词(multi-word)属性,使用驼峰式camelCase
    boxEl.style.backgroundColor = "red"
  ◼ 如果我们将值设置为空字符串,那么会使用CSS的默认样式:
  ◼ 多个样式的写法,我们需要使用cssText属性:
       boxEl.style.cssText = `
          width:100px;
          height:100px;
          background-color:red;
        `
      不推荐这种用法,因为它会替换整个字符串

元素style的读取- getComputedStyle

    ◼ 如果我们需要读取样式:
        对于内联样式,是可以通过style.*的方式读取到的;
        对于style、css文件中的样式,是读取不到的;
    ◼ 这个时候,我们可以通过getComputedStyle的全局函数来实现:
    var boxEl = document.querySelector(".box")
    console.log(boxEl.style.backgroundColor)
    console.log(getComputedStyle(boxEl).fontSize)

元素的常见操作

创建元素

◼ 前面我们使用过document.write 方法写入一个元素:
    这种方式写起来非常便捷,但是对于复杂的内容、元素关系拼接并不方便;
    它是在早期没有DOM的时候使用的方案,目前依然被保留了下来;
◼ 那么目前我们想要插入一个元素,通常会按照如下步骤:
    步骤一:创建一个元素;
    步骤二:插入元素到DOM的某一个位置;
◼ 创建元素: document.createElement(tag)
var boxEl = document.querySelector(".box")
var h2El = document.createElement("h2")
h2El.innerHTML = "我是标题"
h2El.classList.add("title")
boxEl.append(h2El)

插入元素

◼ 插入元素的方式如下:
  node.append(...nodes or strings) —— 在 node 末尾 插入节点或字符串 ----->子元素的最后,
  node.prepend(...nodes or strings) —— 在 node 开头 插入节点或字符串 ---->子元素的最前方,
  node.before(...nodes or strings) —— 在 node 前面 插入节点或字符串 ----->变成兄弟元素放在前面,
  node.after(...nodes or strings) —— 在 node 后面 插入节点或字符串------->变成兄弟元素放在后面,
  node.replaceWith(...nodes or strings) —— 将 node 替换为给定的节点或字符串。
案例:
    // 将元素插入到boxEl里面
    // 方法一:追加到子元素最后
    boxEl.append(h2El)
    // 追加到子元素最前面
    boxEl.prepend(h2El)
    // 变成兄弟元素,放在前面
    boxEl.before(h2El)
    // 变成兄弟元素,放在后面
    boxEl.after(h2El)
    // 替换
    boxEl.replaceWith(h2El)

移除和克隆元素

   ◼ 移除元素我们可以调用元素本身的remove方法:
      setTimeout(()=>{
        h2El.remove()
      },2000)
   ◼ 如果我们想要复制一个现有的元素,可以通过cloneNode方法:
      可以传入一个Boolean类型的值,来决定是否是深度克隆;
      深度克隆会克隆对应元素的子元素,否则不会
    案例:
      var cloneBoxEl = boxEl.cloneNode(true)
      document.body.append(cloneBoxEl)

旧的元素操作方法

   ◼ 在很多地方我们也会看到一些旧的操作方法:
    parentElem.appendChild(node):
      ✓ 在parentElem的父元素最后位置添加一个子元素
    parentElem.insertBefore(node, nextSibling):
      ✓ 在parentElem的nextSibling前面插入一个子元素;
    parentElem.replaceChild(node, oldChild):
      ✓ 在parentElem中,新元素替换之前的oldChild元素;
    parentElem.removeChild(node):
      ✓ 在parentElem中,移除某一个元素;

元素的大小和滚动

    ◼ clientWidth:contentWith+padding(不包含滚动条)
    ◼ clientHeight:contentHeight+padding
    ◼ clientTop:border-top的宽度
    ◼ clientLeft:border-left的宽度
    ◼ offsetWidth:元素完整的宽度
    ◼ offsetHeight:元素完整的高度
    ◼ offsetLeft:距离父元素的x
    ◼ offsetTop:距离父元素的y
    ◼ scrollHeight:整个可滚动的区域高度
    ◼ scrollTop:滚动部分的高度

window的大小和滚动

    ◼ window的width和height
        innerWidth、innerHeight:获取window窗口的宽度和高度(包含滚动条)
        outerWidth、outerHeight:获取window窗口的整个宽度和高度(包括调试工具、工具栏)
        documentElement.clientHeight、documentElement.clientWidth:获取html的宽度和高度(不包含滚动条)
    ◼ window的滚动位置:
        scrollX:X轴滚动的位置(别名pageXOffset)
        scrollY:Y轴滚动的位置(别名pageYOffset)
    ◼ 也有提供对应的滚动方法:
        方法scrollBy(x,y) :将页面滚动至 相对于当前位置的 (x, y) 位置;
        方法scrollTo(pageX,pageY) 将页面滚动至 绝对坐标
posted @ 2024-10-21 09:53  韩德才  阅读(25)  评论(0编辑  收藏  举报