DOM、DOM查找

187 阅读5分钟

DOM、查找、修改

1. DOM

1.1. DOM介绍

什么是: DOM: Document Object Model -> 专门操作网页内容的 API, W3C 指定的标准, 所有浏览器厂商遵照实现

  • 何时: 今后只要操作网页内容, 就必须用 DOM
  • 为什么: 为了统一操作网页内容的 API
    • DOM 标准操作网页内容几乎 100% 兼容
  • 分为: 核心 DOMHTML DOM
    • 核心 DOM: 万能, 繁琐
    • HTML DOM: 对核心 DOM 部分常用 API 的简化
      • 不是万能, 但简化
1.2. DOM
  • 网页中的一切内容在内存中都是以树形结构存储
  • 所有内容( 元素, 属性, 文本 )在树上, 都是一个节点对象
  • DOM 树有唯一的一个根节点 document
  • 所有内容节点都是 document 的后代节点
  • 节点对象: 网页中的所有内容, 都是 DOM 树上的节点对象

    • 类型: Node

    • 三大属性: nodeType / nodeName / nodeValue

      1. nodeType: 表示节点的类型

        • 值是整数: 4 个

          1. document: 9
          2. element: 1
          3. attribute: 2
          4. text: 3
        • 何时: 只要区分节点类型时

        • 为什么: 不同类型的节点可执行的操作不同

        • 问题: 无法进一步区分不同元素

        • 解决: nodeName

      2. nodeName: 节点名( 元素的标签名 )

        • 何时: 只要进一步细致区分元素种类时
        • 包括: 1. document: #document 2. element: 元素的标签名( 全大写 ) 3. attribute: 属性名 4. text: #text
        • 其实, nodeName 可代替 nodeType 来鉴别节点类型
      3. nodeValue: 节点值

        • 包括: 1. document: null 2. element: null 3. attribute: 属性值 4. text: 文本内容
1.3. DOM 操作

查找触发事件的元素 -> 绑定事件 -> 查找要操作的元素 --> 修改 / 添加 / 删除

1.3.1 DOM查找: 4 种
  1. 不需要查找就可直接得到

    • document
    • document.documentElement -> html
    • document.head
    • document.body
  2. 按节点间关系查找

    • 何时: 只要已经获得一个节点, 要找周围节点时

    • 包括: 2 大类关系

      1. 节点树: 包含一切内容节点的树结构
        • 父子:
          • elem.parentNode: 父节点
          • elem.childNodes: 返回直接子节点的集合
          • elem.firstChild: 第一个子节点
          • elem.lastChild: 最后一个子节点
        • 兄弟:
          • elem.previousSibling: 前一个兄弟节点
          • elem.nextSibling: 后一个兄弟节点
        • 问题: 包括看不见的空字符文本节点的干扰
      2. 元素树: 只包含元素节点的树结构
        • 父子:
          • elem.parentElement: 父元素
          • elem.children: 返回直接子元素的集合
          • elem.firstElementChild: 第一个子元素
          • elem.lastElementChild: 最后一个子元素
        • 兄弟:
          • elem.previousElementSibling: 前一个兄弟元素
          • elem.nextElementSibling: 后一个兄弟元素
        • 优点: 不受空文本的干扰
        • 缺点: IE8 不兼容
        • 说明: 元素树不是一棵新树, 仅是节点树的一个子集
    • 强调: childNodeschildren 返回的不是数组, 而是类数组对象

    • 其实, childNodeschildren 都返回动态集合

    • 试题: 用节点间关系, 遍历指定父节点下所有后代节点

      1. 递归遍历: 2 步

      2. 先仅查找直接子节点

      3. 对每个直接子节点, 调用和父节点完全相同的函数

      • 算法: 深度优先遍历: 每个节点都优先遍历子节点, 子节点遍历完, 才遍历兄弟节点
      • 问题: 递归时, 函数内的函数名不能写死
        • 解决: 用 arguments.callee 指代当前函数自己
      function getChildrenFn (parent) {
        // 如果不是文本节点, 就输出节点名; 否则, 输出文本节点的内容
        console.log(parent.nodeType != 3 ? parent.nodeName : parent.nodeValue)
        // 获取 parent 的直接子节点
        var childNodes = parent.childNodes
        for(var i = 0, len = childNodes.length; i < len; i++) {
          argument.callee(childNodes[i])
        }
      }
      
      1. 用循环代替递归:

        • 节点迭代器对象: 依次遍历, 并返回每个节点对象的小工具 -> 内置深度优先算法

        • 如何: 2 步

          1. 创建节点迭代器对象

            var iterator = docment.createNodeIterator( parent, NodeFilter.SHOW_ALL, null, false, SHOW_ELEMENT )
            
          2. 循环调用迭代器的 nextNode(), 调到下一个节点 -> 直到返回 null 结束

            do {
              var node = iterator.nextNode()
              if (null != node) {
                // 输出 node
                console.log(node.nodeType!=3 ? node.nodeName : node.nodeValue)
              } else break
            } while ( true )
            
  3. HTML 查找

    • id 查找: var elem = document.getElementById('id名称')

      • 强调: 必须用 document 调用

    • 按标签名查找: var elems = parent.getElementsByTagName('标签名')

      • 强调: 1. 可在任意父元素上调用, 仅找当前父元素下的指定元素

        1. 返回动态集合
        2. 不但找直接子元素, 而且找所有后代元素
    • name 查找: var elems = document.getElementsByName('name')

      • 何时: 在表单中查找有 name 属性的表单元素时

      • 强调: 1. 只能用 document 调用

        1. 返回动态集合
    • class 属性查找: var elems = parent.getElementsByClassName('class')

      • 强调: 1. 在任意父元素上调用

        1. 返回动态集合
        2. 不但找直接子元素, 而且在所有后代中查找
        3. 不要求完整匹配 class, 只要包含就找到
    • 问题: 每次只能按一个条件查找, 如果条件复杂, 代码会很繁琐

      • 解决: 当查找条件复杂时, 要用选择器查找
  4. 按选择器查找: selector API -> 2 个

    1. 只找一个元素: var elem = parent.querySelector('selector')

    2. 找多个元素: var elems = parent.querySelectorAll('selector')

    • 强调: 1. 可在任意父元素上调用

      1. 不仅查找直接子元素, 且查找所有后代元素
      2. 返回非动态集合
        • 非动态集合: 数据直接保存在集合本地, 无序反复查找
      3. 选择器兼容性, 受制于当前浏览器
  • 试题: 按 HTML 查找 vs 按选择器查找

    1. 返回值
      • HTML 查找: 返回动态集合
        • 优点: 效率高!只需返回需要的数据即可, 不需要返回完整数据
        • 缺点: 造成反复查找
      • 按选择器查找: 返回非动态集合
        • 缺点: 不会反复查找
        • 缺点: 首次执行效率低
    2. 易用性: 当查找条件复杂时
      • HTML 查找: 繁琐
      • 按选择器查找: 简洁
  • 总结:

    • 如果根据一个条件就可获得想要的元素 -> 首选按 HTML 查找

    • 如果查找条件复杂时 -> 首选按选择器查找

    • 返回值总结:

      1. 凡是返回一个元素的 API, 如果没找到, 都返回 null

      2. 凡是返回多个元素的 API, 如果没找到, 都返回空元素的集合

1.3.2. 类数组对象
  • 长的像数组的对象 -> 简称: 集合
  • VS 数组
    • 相同: 1. 都有下标 2. 都有 length
    • 不同: 1. 类型不同 2. API 不通用
  • 动态集合: 不实际存储数据, 每次访问集合时, 都重新查找 DOM
  • 问题: 反复访问集合, 会导致反复查找 DOM
  • 遍历动态集合:
    • 错误: for (var i = 0; i < childNodes.length; i++) { ...... }
    • 正确: for (var i = 0, len = childNodes.lenght; i < len; i++) { ...... }