1. DOM
什么是
***DOM Tree
查找
按节点间关系查找
遍历
查找API
按html查找(了解)
按Selector查找
1. 什么是: Document Object Model
专门操作HTML内容的API
原生js: ES(核心语法)
+DOM(操作网页内容的API) 3天
+BOM(操作浏览器窗口的API) 2天
DOM: W3C 专门操作HTML内容的API标准
DOM分为:
核心DOM: 能够操作所有结构化文档(HTML, XML)
万能, 繁琐
HTML DOM: 专门操作HTML内容的API
对部分常用API的简化
简单, 不是万能!
真正开发: 先用简单的!如果简单的实现不了,再用核心DOM补充
2. ***DOM Tree:
HTML中每个内容(元素,属性,文本)都是一个节点对象(Node)
所有节点对象都以树形结构存储在一起
document对象是整棵树的根节点
所有Node对象都是document的子代节点
今后,实现任何功能前,必须在头脑中构建出一个树形结构
节点对象: Node 是所有节点对象的父类型
三大属性:
nodeType: 节点类型 number
专门区分节点的类型:
document: 9
element: 1
attribute: 2
text: 3
问题: 无法进一步区分元素的标签名
nodeName: 节点名
专门区分节点的名称:
document: #document
element: 标签名(全大写)
attribute: 属性名
text: #text
nodeValue: 节点值(了解)
document: null
element: null
attribute: 属性值
text: 文本的内容
***DOM操作: 都要先找到元素,再修改元素
四大操作: 查找,修改,添加,删除
3. ***查找:
1. 三个元素不用找,可直接获得:
<html> document.documentElement
<head> document.head
<body> document.body
2. 节点间关系: 2大类:
何时: 只要已经获得了一个节点,找周围节点时
节点树: 包含所有节点(元素,文本)的完整的树结构
1. 父子关系: elem.parentNode 返回一个父节点对象
elem.childNodes 返回子节点对象的集合
elem.firstChild 返回第一个子节点对象
elem.lastChild 返回最后一个子节点对象
2. 兄弟关系:
elem.previousSibling : 返回当前节点的前一个兄弟节点
elem.nextSibling : 返回当前节点的下一个兄弟节点:
问题: 不但包含有用的元素节点,还包含看不见的换行/空格干扰
解决: 今后只要仅关心元素节点时,就可用元素树
元素树: 仅包含元素节点的树结构
1. 父子关系: elem.parentElement 返回一个父元素对象
elem.children 返回子元素对象的集合
elem.firstElementChild 返回第一个子元素对象
elem.lastElementChild 返回最后一个子元素对象
2. 兄弟关系:
返回当前节点的前一个兄弟元素
elem.previousElementSibling :
返回当前节点的下一个兄弟元素:
elem.nextElementSibling :
***elem.childNodes和elem.children返回的都是动态集合:
动态集合(live collection): 不实际存储元素和属性值
每次访问集合都重新查找DOM树
遍历动态结合:
错误: for(var i=0;i<children.length;i++)
后果,循环了几次,就重复查找了几次DOM树
解决: for(var i=0,len=children.length; i<len; i++)
仅在循环开始时查找一次DOM树,将值另存为在len中
3. 递归遍历: 查找指定父节点下所有子代节点——鄙视题 手写
如何: 2步:
1. 先遍历所有直接子节点
2. 在遍历直接子节点时,对每个子节点调用和父节点完全相同的方法。
算法: 深度优先遍历: 每次都优先遍历子节点
所有子节点遍历完,才返回遍历兄弟节点
递归的效率: 极低。
解决: 绝大多数递归都可用循环代替
遍历API: 每次仅遍历下一个节点,可用循环反复执行(了解)
1. 节点迭代器:
如何: 2步:
1. 创建迭代器对象:
var iterator=document.createNodeIterator(
parent,NodeFilter.SHOW_ALL, null, false
.SHOW_ELEMENT
);
2. var 原先的节点对象=
iterator.nextNode(); 跳到下一个节点
如果返回null,说明到头了
内置的就是深度优先遍历算法。
4. 按HTML查找: 在整个页面或指定父元素下,查找属性或标签符合要求得元素对象。
何时: 只要按基本的条件有选择的查找元素时
4种:
1. 按id查找:
var elem=document.getElementById("id");
强调: 只能用document调用!
仅返回一个元素!
2. 按标签名查找:
var elems=parent.getElementsByTagName("标签名");
强调: 任何父元素都能调用!
返回父元素下所有同名标签的元素的集合!
即使仅返回一个元素,也会放在集合中
如果想取出唯一的一个元素,必须加[0]
不仅查找直接子节点,而且查找所有子代节点
3. 按name属性查找: (了解)
var elems=document.getElementsByName("name"); 4. 按class属性查找: (了解) (兼容性问题)
var elems=parent.getElementsByClassName("class");
练习:
关于this:
在事件处理函数中this->可自动获得事件绑定的元素对象
何时: 只要处理函数的逻辑中必须当前元素才能正常执行时
反之: 如果函数的逻辑中不需要当前元素也能正常执行时
就不用加this!
DOM操作的固定套路:
绑定事件->找元素->修改/计算
正课:
1. ***查找
按选择器查找
2. 修改:
1. ***查找:
按选择器查找:
1. 如果只找一个:
var elem=parent.querySelector("selector");
2. 如果找多个:
var elems=parent.querySelectorAll("selector");
返回非动态集合(non-live collection):
实际存储完整数据,反复访问,也不用重新查找DOM树
优: 不会造成反复查找
鄙视题: getElementsXXXX vs querySelector
1. 返回值:
getElements返回动态集合:
优: 首次查找效率高
缺: 可能造成反复查找DOM树
querySelector返回非动态集合:
优: 包含完整属性,无需反复查找DOM树
缺: 首次查找效率低
2. 易用性:
querySelector更灵活, 且更简单
getElements每次只能查找一种结果,无法设置查找条件
更繁琐
总结: jQuery中:
如果一次查找就能找到元素时,首选getElements
如果查找条件复杂时,就用querySelector
2. 修改:
内容:
1. .innerHTML: 获取或设置元素开始标签到结束标签之间的html代码片段。
2. .textContent: 获取或设置元素开始标签到结束标签之间的纯文本内容。
2件事: 1. 去掉了html标签
2. 将转义字符转换为正文
何时: 如果希望去掉内容中的标签,仅获取文本内容时
.textContent有兼容性问题: IE8不兼容
IE8: .innerText
属性:
样式:
正课:
1. 修改:
属性:
样式:
1. 属性:
html标准属性:
核心DOM: 万能
1. 获取属性:
1. 获得属性节点对象: (了解)
var attrNode=elem.attributes[i/属性名];
elem.getAttributeNode("属性名");
attrNode.value
2. 直接获得属性值:
var value=elem.getAttribute("属性名");
2. 设置属性:
elem.setAttribute("属性名",新值);
3. 判断是否包含指定属性:
var bool=elem.hasAttribute("属性名")
4. 移除属性:
elem.removeAttribute("属性名");
HTML DOM: 仅对部分的简化
elem.属性名
强调: html中的class属性和ES中的class属性冲突
html中的class要改名为className
特例: disabled selected checked
不能用核心DOM操作!只能用html DOM打点操作
attribute vs property
attribute指出现在开始标签中的属性
property保存在内存中的对象中的属性
核心DOM只能操作出现在页面上的attribute属性
无法操作未出现在页面上的内存中的property属性
HTML DOM可访问内存中的property属性
自定义属性: 2种:
1. 只能用核心DOM访问,不能用html访问
2. HTML5: ——兼容问题
所有自定义属性: 属性名必须: data-属性名
访问时: elem.dataset.属性名
何时: 1. 在客户端网页中临时缓存部分业务数据
2. 代替id和class作为查找元素的条件
2. 样式:
内联: elem.style.css属性名
强调: 所有css属性都要去横线变驼峰
background-color -> backgroundColor
list-style-type -> listStyleType
问题1: 只能获得/设置内联样式
无法获取最终应用到元素上的完整样式
解决:
如果设置一个元素的样式: elem.style.css属性名
因为: 优先级最高!不影响其他元素
如果获取一个元素的样式: 不用style
getComputedStyle(elem).css属性名
问题2: elem.style.css属性名一句话只能设置一个样式
如果需要同时设置一个元素的多个css属性,代码繁琐
解决: 今后只要操作一个元素的样式,都用class属性批量操作
特例: 精确控制动画效果时,需要操作单个css属性
内部/外部样式表:
补: .innerHTML只能获得封闭标签的内容
表单元素的值只能用.value
正课:
1. 添加/删除元素
2. ***HTML DOM常用对象
select/option table/行分组/tr/td form
1. 添加/删除元素:
添加: 3步:
1. 创建新的空元素:
var a=document.createElement("a");
相当于: <a></a>
2. 设置元素的关键属性:
a.href="http://tmooc.cn"
a.innerHTML="go to tmooc";
相当于: <a href="http://tmooc.cn">go to tmooc</a>
3. 将元素添加到DOM树上:
3种:
1. 末尾追加: parent.appendChild(a)
2. 插入在一个现有元素之前:
parent.insertBefore(a,oldElem)
3. 替换现有元素: parent.replaceChild(a,oldElem)
***页面加载过程:
html->DOM Tree
↓
Render Tree->***layout->paint
↑
css->CSSRules
结论: 只要修改DOM树的内容,都会导致重新layout
解决: 优化: 尽量少的操作DOM树
1. 如果同时添加父元素和子元素
先再内存中将子元素添加到父元素,再将父元素一次性添加到DOM树
正课:
1. ***HTML DOM常用对象:
Form
2. BOM
1. ***HTML DOM常用对象:
Form: 代表一个<form元素
获取: var form=document.forms[i/id/name];
属性: .length: 相当于form.elements.length
方法: .submit() : 专用于手动提交表单
问题: 用户可能按回车,自动提交
解决: 表单提交的最后一关是一个事件
form.onsubmit(): 当表单正式提交前自动触发
获取*表单*元素:
var elem=form.elements[i/id/name];
更简化: 如果表单元素有name属性:
form.name
方法: .focus() : 让当前表单元素获得焦点
正课:
1. ***BOM
window
打开关闭窗口
窗口大小和位置
定时器
1. ***BOM:
什么是: Browser Object Model
专门操作浏览器窗口的API —— 没有标准
DHTML对象模型:
window 2个角色:
1. 代替global作为全局作用域对象
所有全局函数和全局变量都是window的成员
2. 封装所有DOM和BOM的API
包括:
history 当前窗口打开后成功访问的历史记录
location 当前窗口正在打开的URL
document DOM树的根节点,包含所有网页内容
screen 当前显示设备的信息
navigator 浏览器的配置信息(版本,名称,插件,cookie)
event 定义了浏览器中所有事件
window: 指代当前浏览器窗口对象
打开和关闭窗口
打开: open("url","name")
关闭: close();
打开一个连接,共有几种方式:
1. 在当前窗口打开, 可后退:
html: <a href="url" target="_self">
js: open("url","_self");
2. 在当前窗口打开, 禁止后退:
js: location.replace("url")
在当前窗口打开新url,用新url替换history中原有url
原理:
history: 保存当前窗口打开后成功访问过的url的历史记录栈
3. 在新窗口打开,可打开多个:
html: <a href="url" target="_blank">
js: open("url","_blank");
4. 在新窗口打开,只能打开一个:
html:<a href="url" target="name">
js: open("url","name");
每个窗口都有一个唯一的name属性
name: 在内存中唯一标识一个窗口的名称
规定: 同时只能打开一个相同name的窗口
预定义: _self : 和当前窗口使用相同的name
_blank: 不指定自定义name,由浏览器随机分配
窗口大小和窗口位置: (了解)
窗口大小:
1. 完整窗口大小: window.outerWidth/outerHeight
***2. 文档显示区大小: window.innerWidth/innerHeight
调整: 2个机会:
1. 在打开窗口时,就规定大小:
var config="left=?,top=?,width=?,height=?";
open("url","name",config)
2. 打开窗口后再调整——被禁用
window.resizeTo(width,height)
window.resizeBy(width增量,height增量)
窗口位置: 窗口左上角距屏幕左上角的位置
window.screenLeft|screenX
window.screenTop|screenY
调整:
moveTo(left,top)
moveBy(left的增量,top的增量)
*****定时器:
什么是: 让程序按照指定时间执行一项任务
何时:
1. 让程序每隔一段时间间隔反复执行一项任务——周期性
2. 让程序等待一段时间后自动执行一次任务——一次性
如何:
周期性定时器: 3件事:
1. 任务函数task
2. 启动定时器: 将task放入定时器中,设定时间间隔
timer=setInterval(task, ms);
timer是唯一标识一个定时器实例的序号
强调: task不能加(),因为是传递给定时器去回调执行
3. 停止定时器:
clearInterval(timer);
问题: 定时器往往需要自动停止
解决: 在*任务函数*中判断临界值
如果未达到临界值,继续执行任务
否则 停止定时器
一次性定时器: 先等待,再执行一次,然后自动释放
3件事:
1. 任务函数task
2. 启动定时器: 将task放入定时器中,设定等待时间
timer=setTimeout(task, ms);
3. 停止定时器: (其实是在执行之前,停止等待)
clearTimeout(timer);
定时器原理:
定时器中的回调函数,必须在主程序所有语句执行完最后才能开始执行!
正课:
1. 添加/删除元素
2. ***HTML DOM对象
select/option table/行分组/tr/td form
1. 添加/删除元素
优: 尽量少的操作DOM树
如何:
1. 如果同时添加父元素和子元素时,都要先将子元素添加到父元素,再将父元素整体添加到DOM树一次即可
2. 如果同时添加多个平级元素时,都要用文档片段
文档片段: DocumentFragment
内存中临时存放多个平级元素的虚拟的临时父元素
何时: 如果同时添加多个平级元素时,都要先将平级元素添加到文档片段中。再将文档片段一次性添加到DOM树
如何:
1. 创建文档片段:
var frag=document.createDocumentFragment();
2. 将子元素添加到frag中: frag.appendChild(child)
3. 将frag添加到DOM树: parent.appendChild(frag)
frag将子元素添加到DOM树后,自动释放。
删除元素: 1. 先找到要删除的元素对象elem
2. parent.removeChild(elem)
elem.parentNode.removeChild(elem);
2. ***HTML DOM常用对象:
Image Select/Option Table/... Form
1. Image: 代表一个<img元素
唯一的简化: var img=new Image();
2. Select: 代表一个<select元素
属性:
.selectedIndex: 获得select中当前选中项的下标
.options: 获得select中所有的option元素对象
.options.length: option元素的个数
=0可清除所有option
其实select.length等效于select.options.length
最简单的清除所有option的办法: select.length=0;
.value: 当前select元素选中项的值
2种情况:
1. 如果选中项有value属性,则使用选中项的value属性
2. 如果选中项没有value属性,则使用text作为value
方法:
.add(option) => .appendChild
.remove(i) => select.removeChild(select.options[i])
事件:
.onchange: 当选中项发生改变时
Option: 代表一个<option元素
创建:
var opt=new Option(text,value)
属性:
.index: 当前option在select中的下标
.value:
.text => .innerHTML
selected:
其实向select中添加一个option的最简单写法:
sel.add(new Option(text,value));
3. Table: 代表<table元素
table
.createTHead()=>thead
.deleteTHead()
.tHead
.insertRow(i) => tr //在i位置插入一个新行
省略i, 默认表示末尾追加
.deleteRow(i);//删除i位置的行
.rows //获得thead中的所有行对象
.rows[i]=>tr
属性: .rowIndex 获得当前tr相对于整个table的行下标
.insertCell(i) => td //i位置添加一个新td
省略i, 默认表示末尾追加
.deleteCell(i)
.cells //获得行中的所有格
.cells[i] => td
.createTBody() tbody
.tBodies/tBody
.createTFoot() tfoot
.deleteTFoot()
.tFoot
删除行:
2种: 1. 用行分组.deleteRow(i)
i : 指的是行在当前分组内的下标位置
2. 用table.deleteRow(i)
i: 指的是行在整个table中的下标位置
——tr.rowIndex
只要用rowIndex删除行,必须用table.deleteRow(...)
补: 三类对话框: 都不用
alert("警告");
var bool=confirm("确认提示");
点确定->true
点取消->false
var input=prompt("输入提示")
正课:
1. *****定时器
动画
2. screen
3. history
4. ***location
5. ***navigator
6. ****event
1. *****定时器
动画:
练习:
获取长度属性值时: 获得都是带单位的字符串,不能直接计算
都要先去单位(parseFloat),再计算
设置长度属性值时: 在数值结尾拼单位(px)
2. screen对象:
获得屏幕的大小:
完整大小: screen.width/height
何时: 用屏幕大小鉴别设备种类: ——鄙视题
wide desktop - lg : 1200+
pc - md: 992+
pad - sm: 768+
phone - xs: 480+
可用大小: screen.availWidth/availHeight
去掉任务栏之后的剩余大小
3. history: 保存当前窗口打开后成功访问过的url的历史记录栈
前进: history.go(1)
后退: history.go(-1)
刷新: history.go(0)
其实: history.go(n)
4. ***location: 封装当前窗口正在打开的url对象
属性:
.href: 获取或设置当前网页打开的url
何时: 在当前页面打开新链接时
简写: location.href=url -> location=url
.protocol: 协议
.host: 主机名+端口号
.hostname: 主机名
.port: 端口号
.pathname: 网页的相对路径
.hash: #锚点
.search: ?查询字符串
鄙视: .search -> 参数组成的对象:
?uname=zhangdong&upwd=123456
["uname=zhangdong", "upwd=123456"]
{uname:zhangdong, upwd:123456}
正课:
1. ***location
2. ***navigator
3. ****event
1. ***location:
属性:
方法:
location.assign("url") => location.href="url"
location="url"
location.reload(false/true): 重新从服务器加载url
force
默认false: 优先从服务器端缓存中获取文件
如果改成true, 强制从服务器硬盘获取新文件
location.replace(url): 在当前窗口打开新url,禁止后退
2. ***navigator: 封装浏览器配置信息的对象
navigator.cookieEnabled: 是否启用cookie
cookie: 在客户端本地持久存储一个数据的文件
如果禁用无法保存搜索关键词,或记住密码
navigator.plugins: 封装所有插件对象的集合
判断浏览器的名称和版本:
navigator.userAgent: 保存浏览器内核,名称,版本号的字符串
3. ****event
事件: 浏览器自己触发的或用户手动触发的页面(元素)状态的改变。
浏览器在事件发生时,都会自动调用事件处理函数
事件处理函数: 当节点发生事件时,自动调用的函数
其实就是节点的onxxx属性
事件处理函数的值,都是一个函数对象
事件发生时: 节点对象.onxxx();
事件处理函数中的this: .前的节点对象
何时: 只要希望一个元素/节点,能够响应某个事件时
在发生某个事件时,能自动执行事件处理函数
如何: 3种方式:
1. 在html元素开始标签中:
html: <ANY on事件名="js语句(this)"
js: function 函数(ANY){ ... }
问题: 1. 无法为动态生成的元素绑定事件
2. 事件绑定分散在页面各个角落,不便于维护
2. 在js中绑定事件: 2种方式:
1. ANY.on事件名=function(){
this->ANY
}
事件发生时,自动执行: ANY.onXXX();
问题: 每个ANY的一个事件处理函数,只能绑定一个函数对象。
解决:
2. ANY.addEventListener("事件名",fn)
ANY.removeEventListener("事件名",fn)
如果只是添加事件监听,可用匿名函数
如果可能移除某个处理函数,就必须用有名的函数绑定
IE8: ANY.attachEvent("on事件名",fn)
其实: addEventListener("事件名",fn,capture)
capture: 是否在捕获阶段就提前触发
默认false: 所有事件处理函数都在冒泡阶段反向触发
true: 该事件会在捕获阶段就提前触发!
***事件周期:
DOM标准: 3阶段
1. 捕获(capture): 由外向内依次记录各级元素绑定的相同事件处理函数
2. 目标出发: 执行实际触发事件的元素上的处理函数
3. 冒泡: 由目标元素向外,出发各级父元素上绑定的相同事件处理函数
目标元素(target): 实际触发事件的元素
IE8: 2个阶段: 没有捕获阶段
事件对象: event: 在事件发生时,自动创建的封装所有事件信息的对象
event对象提供了操控事件的方法: 阻止事件, 取消冒泡....
何时: 1. 为了获得事件相关的数据
2. 操控事件
如何:
获取: DOM标准: 事件对象,默认作为事件处理函数的第一个参数传入:
IE8: 事件对象自动保存在全局变量event中
兼容: function eventHandler(e){
e=e||window.event;
}
取消冒泡: e.stopPropagation();
利用冒泡:
优化: 尽量少的添加EventListener
为什么: 每个EventListener都是一个对象
浏览器触发事件时,会轮询每个EventListener对象
添加的EventListener越多,页面响应速度越低
解决: 当多个平级子元素绑定相同的事件处理函数时
其实,只要在父元素绑定一次,所有子元素共用即可!
难题: 1. 如何获得目标元素:
this->父元素 X
DOM: e.target ->目标元素
IE8: e.srcElement
兼容: var target=e.target||e.srcElement;
2. 鉴别目标元素是否想要:
判断元素的名称或属性