DOM、查找、修改
1. DOM
1.1. DOM介绍
什么是:
DOM: Document Object Model -> 专门操作网页内容的API,W3C指定的标准, 所有浏览器厂商遵照实现
- 何时: 今后只要操作网页内容, 就必须用
DOM - 为什么: 为了统一操作网页内容的
API- 用
DOM标准操作网页内容几乎 100% 兼容
- 用
- 分为: 核心
DOM和HTML DOM- 核心
DOM: 万能, 繁琐 HTML DOM: 对核心DOM部分常用API的简化- 不是万能, 但简化
- 核心
1.2. DOM 树
- 网页中的一切内容在内存中都是以树形结构存储
- 所有内容( 元素, 属性, 文本 )在树上, 都是一个节点对象
DOM树有唯一的一个根节点document- 所有内容节点都是
document的后代节点
-
节点对象: 网页中的所有内容, 都是
DOM树上的节点对象-
类型:
Node -
三大属性:
nodeType/nodeName/nodeValue-
nodeType: 表示节点的类型-
值是整数: 4 个
document: 9element: 1attribute: 2text: 3
-
何时: 只要区分节点类型时
-
为什么: 不同类型的节点可执行的操作不同
-
问题: 无法进一步区分不同元素
-
解决:
nodeName
-
-
nodeName: 节点名( 元素的标签名 )- 何时: 只要进一步细致区分元素种类时
- 包括: 1.
document:#document2.element: 元素的标签名( 全大写 ) 3.attribute: 属性名 4.text:#text - 其实,
nodeName可代替nodeType来鉴别节点类型
-
nodeValue: 节点值- 包括: 1.
document:null2.element:null3.attribute: 属性值 4.text: 文本内容
- 包括: 1.
-
-
1.3. DOM 操作
查找触发事件的元素 -> 绑定事件 -> 查找要操作的元素 --> 修改 / 添加 / 删除
1.3.1 DOM查找: 4 种
-
不需要查找就可直接得到
documentdocument.documentElement->htmldocument.headdocument.body
-
按节点间关系查找
-
何时: 只要已经获得一个节点, 要找周围节点时
-
包括: 2 大类关系
- 节点树: 包含一切内容节点的树结构
- 父子:
elem.parentNode: 父节点elem.childNodes: 返回直接子节点的集合elem.firstChild: 第一个子节点elem.lastChild: 最后一个子节点
- 兄弟:
elem.previousSibling: 前一个兄弟节点elem.nextSibling: 后一个兄弟节点
- 问题: 包括看不见的空字符文本节点的干扰
- 父子:
- 元素树: 只包含元素节点的树结构
- 父子:
elem.parentElement: 父元素elem.children: 返回直接子元素的集合elem.firstElementChild: 第一个子元素elem.lastElementChild: 最后一个子元素
- 兄弟:
elem.previousElementSibling: 前一个兄弟元素elem.nextElementSibling: 后一个兄弟元素
- 优点: 不受空文本的干扰
- 缺点:
IE8不兼容 - 说明: 元素树不是一棵新树, 仅是节点树的一个子集
- 父子:
- 节点树: 包含一切内容节点的树结构
-
强调:
childNodes和children返回的不是数组, 而是类数组对象 -
其实,
childNodes和children都返回动态集合 -
试题: 用节点间关系, 遍历指定父节点下所有后代节点
-
递归遍历: 2 步
-
先仅查找直接子节点
-
对每个直接子节点, 调用和父节点完全相同的函数
- 算法: 深度优先遍历: 每个节点都优先遍历子节点, 子节点遍历完, 才遍历兄弟节点
- 问题: 递归时, 函数内的函数名不能写死
- 解决: 用
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]) } }-
用循环代替递归:
-
节点迭代器对象: 依次遍历, 并返回每个节点对象的小工具 -> 内置深度优先算法
-
如何: 2 步
-
创建节点迭代器对象
var iterator = docment.createNodeIterator( parent, NodeFilter.SHOW_ALL, null, false, SHOW_ELEMENT ) -
循环调用迭代器的
nextNode(), 调到下一个节点 -> 直到返回null结束do { var node = iterator.nextNode() if (null != node) { // 输出 node console.log(node.nodeType!=3 ? node.nodeName : node.nodeValue) } else break } while ( true )
-
-
-
-
-
按
HTML查找-
按
id查找:var elem = document.getElementById('id名称')-
强调: 必须用
document调用
-
-
按标签名查找:
var elems = parent.getElementsByTagName('标签名')-
强调: 1. 可在任意父元素上调用, 仅找当前父元素下的指定元素
- 返回动态集合
- 不但找直接子元素, 而且找所有后代元素
-
-
按
name查找:var elems = document.getElementsByName('name')-
何时: 在表单中查找有
name属性的表单元素时 -
强调: 1. 只能用
document调用- 返回动态集合
-
-
按
class属性查找:var elems = parent.getElementsByClassName('class')-
强调: 1. 在任意父元素上调用
- 返回动态集合
- 不但找直接子元素, 而且在所有后代中查找
- 不要求完整匹配
class, 只要包含就找到
-
-
问题: 每次只能按一个条件查找, 如果条件复杂, 代码会很繁琐
- 解决: 当查找条件复杂时, 要用选择器查找
-
-
按选择器查找:
selector API-> 2 个-
只找一个元素:
var elem = parent.querySelector('selector') -
找多个元素:
var elems = parent.querySelectorAll('selector')
-
强调: 1. 可在任意父元素上调用
- 不仅查找直接子元素, 且查找所有后代元素
- 返回非动态集合
- 非动态集合: 数据直接保存在集合本地, 无序反复查找
- 选择器兼容性, 受制于当前浏览器
-
-
试题: 按
HTML查找 vs 按选择器查找- 返回值
- 按
HTML查找: 返回动态集合- 优点: 效率高!只需返回需要的数据即可, 不需要返回完整数据
- 缺点: 造成反复查找
- 按选择器查找: 返回非动态集合
- 缺点: 不会反复查找
- 缺点: 首次执行效率低
- 按
- 易用性: 当查找条件复杂时
- 按
HTML查找: 繁琐 - 按选择器查找: 简洁
- 按
- 返回值
-
总结:
-
如果根据一个条件就可获得想要的元素 -> 首选按
HTML查找 -
如果查找条件复杂时 -> 首选按选择器查找
-
返回值总结:
-
凡是返回一个元素的
API, 如果没找到, 都返回null -
凡是返回多个元素的
API, 如果没找到, 都返回空元素的集合
-
-
1.3.2. 类数组对象
- 长的像数组的对象 -> 简称: 集合
- VS 数组
- 相同: 1. 都有下标 2. 都有
length - 不同: 1. 类型不同 2.
API不通用
- 相同: 1. 都有下标 2. 都有
- 动态集合: 不实际存储数据, 每次访问集合时, 都重新查找
DOM树 - 问题: 反复访问集合, 会导致反复查找
DOM树 - 遍历动态集合:
- 错误:
for (var i = 0; i < childNodes.length; i++) { ...... } - 正确:
for (var i = 0, len = childNodes.lenght; i < len; i++) { ...... }
- 错误: