目录
DOM的定义
Document Object Model 专门操作网页内容的API标准——W3C
什么时候会用到DOM
只要操作网页内容,都必须用DOM提供的API
DOM的分类
核心 DOM和HTML DOM
核心 DOM:最初制定的操作一切结构化文档的统一API 优点:万能 缺点:繁琐
HTML DOM:专门操作HTML文档的API 优点:简洁 缺点:不是万能
总结:实际开发的时候不需要区分核心 DOM和HTM LDOM,优先使用简单的API,如果简单的实现不了再用复杂的补充
DOM树
DOM树的定义
网页中一切内容在内存中都是以树型结构存储在一起,每项内容(元素,文本,属性)都是树上的一个节点对象
为什么会是树形结构
树形结构是保存不确定层级深度的上下级包含关系最好的结构
根节点
树结构都有唯一的一个树根节点: document节点,所有网页内容,都是document的子节点
节点对象
网页中每一项内容都是一个节点对象,节点对象封装了节点可用的属性和功能
节点三大属性
nodeType:节点类型 nodeName:节点名称 nodeValue:节点值
nodeType:
作用:不同类型的节点可用的属性和可执行的操作不一样
包括4种类型:
document 9
element 1
attribute 2
text 3
问题:无法进一步区分具体的元素名
nodeName:
作用:进一步区分具体的元素名
包括4种类型:
document #document
element 标签名(全大写)
attribute 属性名
text #text
nodeValue:
作用:保存节点的值
包括4种类型:
document null
element null
attribute 属性值
text 文本内容
DOM的操作流程
增删改查+事件处理
查找触发事件的元素 ---->>绑定事件处理函数
查找要修改的元素---->>修改属性/样式,添加,删除
DOM查找
1、不需要查找可直接获取的元素
html document.documentElement
head document.head
body document.body
2、按HTML查找
1、按id查找
var elem = document.getElementById("id"); //直接写id名
强调:1、必须用 document 调用 2、只会返回一个元素
2、按标签名查找多个元素
var elems = document.getElementsByTagName("标签名");
强调:
1、可在任意父元素上调用 表示仅查找当前父元素下的后代元素
2、不但找直接子元素,且查找所有后代元素
3、返回多个元素组成的动态集合
3、按class属性查找
var elems = document.getElementsByClassName("class");
强调:
1、可在任意父元素上调用
2、返回多个元素组成的动态集合
3、只要包含指定的类名,就选择改元素 不必完整匹配
4、不但找直接子元素,且找所有后代元素
4、按name属性查找
var elems = document.getElementsByName("name");
查找拥有 name 属性的表单时才会用name属性查找
强调:
1、只能用 document 调用
2、返回多个元素组成的动态集合
3、按节点关系查找
什么时候用到:如果已经获取了一个元素,想要查找周围元素
1、父子关系
child.parentNode 获取一个节点的父节点
parent.childNodes 获取父节点下的所有直接子节点
parent.firstChild 获取父节点下的第一个子节点
parent.lastChild 获取父节点下的最后一个直接子节点
2、兄弟关系
elem.nextSibling 获取一个节点相邻的下一个兄弟节点
elem.previousSibling 获取一个节点相邻的前一个兄弟节点
问题:会受看不见的空字符的干扰
解决:元素树
元素树:仅包含元素节点的树结构 元素树不是一颗新树,只时节点树的部分子集
如何实现:
1、父子关系
child.parentElement 获取一个节点的父元素
parent.children 获取父元素下的所有直接子元素
parent.firstElementChild 获取父元素下的第一个直接子元素
parent.lastElementChild 获取父元素下的最后一个直接子元素
2、兄弟关系
elem.nextElementSibling 获取一个元素相邻的下一个兄弟元素
elem.previousElementSibling 获取一个元素相邻的前一个兄弟元素
4、按选择器查找
1、只查找一个元素
var elem = parent.querySelector("selector");
2、查找多个元素
var elem = parent.querySelectorAll("selector");
强调:
1、可在任意父元素上调用
2、返回非动态集合(非动态集合:实际存储数据,即反复访问,也不会导致反复查找DOM树)遍历不用先缓存length
3、受制于当前浏览器的兼容性要求
5、遍历节点
遍历节点的定义:查找一个父元素下所有后代节点
如何操作:
1、定义函数仅遍历直接子节点
2、对每个碰到的子节点调用和父节点完全相同的操作
算法:深度优先(每当同时有子元素和兄弟元素时,总是先遍历子元素。子元素遍历完,才返回遍历兄弟元素)
问题:递归的执行效率极低
解决:用循环代替
如何操作:用节点迭代器对象(NodeIterator 专门按照深度优先遍历的顺序依次访问每个子元素的对象)
1、创建NodeIterator
var iterator = documnet.createNodeIterator(parent,NodeFilter.SHOW_ALL,null,false,SHOW_ELEMENT);
参数:
parent:想要作为搜索起点的树中的节点
NodeFilter.SHOW_ALL:显示所有类型 的节点
NodeFilter.SHOW_ELEMENT:显示元素节点
NodeFilter.SHOW_TEXT:显示文本节点
NodeFilter.SHOW_COMMENT:显示注释节点
NodeFilter.SHOW_DOCUMENT:显示文档节点
2、用循环反复调用 NodeIterator 的 nextNode()函数
NodeIterator的主要方法是nextNode()和previousNode()
如何操作:
1.、返回当前节点
2.、蹦到下一个节点 到返回null退出
举个实例:
HTML的结构:
<div id="root">
<p>hello</p>
<div>
<p>world</p>
<ul>
<li>
<p>html</p>
</li>
</ul>
</div>
</div>
遍历root下的所有元素:
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, null, false);
var node = iterator.nextNode();
while(node !== null){
console.log( node.tagName );
node = iterator.nextNode();
}
遍历root下的所有p元素:
var filter = function(node){
return node.tagName.toLowerCase() == 'p' ?
NodeFilter.FILTER_ACCEPT:
NodeFilter.FILTER_REJECT;
}
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, filter, false);
var node = iterator.nextNode();
while(node !== null){
console.log( node.tagName );
node = iterator.nextNode();
}
DOM查找总结
1. 如果已经获得一个元素,找周围元素: 用节点间关系查找
2. 如果通过一个条件就可找到想要的元素: 用按HTML查找
3. 如果查找条件复杂时: 用selector API
按HTML vs 按Selector
1. 返回值:按HTML查找: 返回动态集合
按Selector查找: 返回非动态集合
2. 效率:首次查找按HTML效率快!
按selector查找稍慢!
3. 易用:按selector查找更简洁
按HTML查找更繁琐
修改
1、内容
.innerHTML .textContent .value
2、属性
标准属性:HTML标准中规定的属性
1、核心DOM (4个API)
1、获取指定属性的值:
elem.getAttribute("属性名")
2、修改指定属性的值:
elem.setAttribute("属性名","值")
3、判断是否包含指定属性:
elem.hasAttribute("属性名")
4、移除指定属性:
elem.removeAttribute("属性名")
2、HTML DOM: 所有的标准属性都被提前封装在了元素对象中,可用"."直接访问
1、获取指定的属性的值:
elem.属性名
2、修改指定属性的值:
elem.属性名 = "值"
3、判断是否包含指定属性:
elem.属性名!==""
4、移除指定属性:
elem.属性名 = ""
3、clas属性
class是E5中保留字,DOM不能使用。用className代替class
4、状态属性
disabled checked selected
不能用核心DOM访问,只能用HTML DOM访问。值都是bool类型
5、自定义扩展属性(自定义扩展属性不能用HTML DOM访问)
1、核心DOM
2、HTML5:
定义属性:data-属性名 = "值"
访问:elem.dataset.属性名
3、样式
1、内联
1、修改:
elem.style.css属性名
css属性名必须去横线变驼峰 优点:优先级最高,仅当前元素自有
2、读取:
不能用style.css属性名。因为style只能获得内联样式,无法获得样式表中继承/层叠来的样式
解决方案:
今后凡是读取样式,都要读取计算后的样式。(计算后的样式:最终应用到元素上的所有样式的综合,并将相对单位计算为绝对单位)
1、获得计算后的所有css属性的总和:style对象
var style = getComputedStyle(elem)
2、从style中获得想要的css属性
style.css属性
注意:计算后的样式都是只读,不允许修改
问题:一句话只能修改一个css属性
解决:如何批量修改一个元素的多个属性:
1、先在css中将多个属性定义为一个class
2、在JS中修改元素的className为指定的class
添加
1、创建新的空元素
var a = document.createElement("a");
2、设置关键属性
a.href = "https://www.baidu.com";
a.innerHTML = "百度一下,你就知道"
3、将新元素加载到DOM树
一个新元素,必须追加到DOM树上的指定父元素下,才能被浏览器显示出来
1、末尾追加:
parent.appendChild(a)
2、中间插入
parent.insertBefore(a,child)
3、替换
parent.replaceChild(a,child)
优化:尽量少的操作DOM树
每改变一次DOM树,都会反复触发layout,降低页面的响应速度
如何优化:
1、如果同时添加父元素和子元素时,就要现在内存中将子元素添加到父元素中。最后再将拼好的父元素一次性添加到DOM树
2、如果是同时添加多个平级子元素,就要用文档片段(文档片段:内存中临时保存子节点的虚拟父节点)
如何操作:
1、创建文档片段
var frag = document.createDocumentFragment();
2、将子元素临时追加到frag中
frag.appendChild(child);
3、将frag整体追加到DOM树
parent.appendChild(frag);
注意:frag不会成为实际页面元素 将子元素添加到DOM树后,自动释放
删除
removeChild();
parent.removeChild(child);
替换
replaceChild();
将p1的内容换成“这是一个新的段落。”
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="div1">
<p id="p1">这是一个段落。</p>
<p id="p2">这是另外一个段落。</p>
</div>
<script>
var para = document.createElement("p");
var node = document.createTextNode("这是一个新的段落。");
para.appendChild(node);
var parent = document.getElementById("div1");
var child = document.getElementById("p1");
parent.replaceChild(para, child);
</script>
</body>
</html>
HTML DOM常用对象
HTML DOM在创建和删除元素方面,对常用的复杂HTML元素也进行了简化
HTML DOM常用对象:
Image Select/OPtion Table Form
Image
var img=new Image();
Select
属性:
.value 获得select元素选中项的value
如果选中项没有value属性,则用内容代替
.selected Index 当前选中项的下标
.options 获得select下所有option元素的集合
.options.length 表示选项的个数 不但可获取个数,还可清空所有选项:
.options.length = 0
.length => .options.length
清空select,还可写为: select.length=0
方法:
.add(option) 追加一个option
问题:不支持frag
vs appendChild 支持frag
.remove(i) 移除i位置的选项
事件:
.onchange 当选中项改变时触发
Option
var opt=new Option(text,value);
Table
管着行分组
创建行分组:
var thead=table.createTHead()
var tbody=table.createTBody()
var tfoot=table.createTFoot()
删除行分组:
table.deleteTHead()
table.deleteTFoot()
获取行分组:
table.tHead
table.tBodies[i]
table.tFoot
行分组:
管着行
创建行:
var tr=行分组.insertRow(i)
固定套路:
1、开头插入新行:行分组.insertRow(0)
2、末尾追加:行分组.insertRow()
删除行:
.deleteRow(i)
1、行分组.deleteRow(i) i 是在行分组内的相对下标位置
问题:没有办法自动获得行分组内的相对下标位置
2、table.deleteRow(i) i是在整个table中的绝对下标位置
如何自动获得行在整个表格中的位置: tr.rowIndex
总结:今后只要删除行,都要首选用table删除:
固定套路:table.deleteRow(tr.rowIndex)
强调:i是要删除的行在行分组内的相对位置
获取所有行:
行分组.rows
行:管着格
创建格:
var td=tr.insertCell(i)
强调:insertCell只能创建td
固定套路:末尾追加: tr.insertCell()
删除格:
tr.deleteCell(i)
获取所有格:
tr.cells
form
获取:
document.forms[i/id]
属性:
.elements 获得表单中的所有表单元素
包括:input select button textarea
.elements.length 表示表单中表单元素的个数
.length => elements.length
方法:
form.submit(); 用程序手动提交表单
何时:只要希望通过自定义的验证决定是否提交表单
事件:form.onsubmit...
表单中的表单元素:
获得表单元素:
一般:var elem=form.elements[i/id/name]
简写:前提: 必须有name属性的元素:
form.name
特例:没有name属性的元素
固定套路: 获得倒数第二个按钮
form.elements[form.length-2]
方法:elem.focus() 让elem获得焦点
elem.blur() 让elem失去焦点