1、DOM: Document Object Model
早起 js 操作不同浏览器的 API 没有标准,有严重兼容性问题,后来 W3C 制定了统一的操作网页内容的 API 标准 DOM,使用 DOM API 操作网页内容,几乎 100%兼容所有浏览器,它具有查找, 修改(内容,属性,样式), 添加, 删除的功能
网页中一切内容在内存中都是以树形结构存储的,树只有一个根节document,它包含了所有网页内容,网页中每一项内容都是树上的一个节点对象,包括: 元素、文字、属性...,每个节点都是一个 node 类型的对象,node 也是所有节点的父类型
三大节点类型共有的属性: nodeType 、 nodeName 、 nodeValue
(1)、nodeType 节点类型
不同的类型的节点,能执行的操作是不一样的,nodeType 专门用于判断节点的类型
①、document(文档节点):9
②、element(元素节点):1
③、attribute(属性节点):2
④、Text(文本节点):3
(2)、nodeName 节点的名称
节点的名称专门用于鉴别元素的标签名,nodeName 返回的是全大写的标签名
①、document(文档节点):#document(始终是)
②、element(元素节点):全大写的标签名
③、attribute (属性节点):属性名
④、text (文本节点):#text(始终是)
(3)、 nodeValue 节点值
①、document(文档节点):undefined 或 null
②、element(元素节点):undefined 或 null
③、attribute(属性节点):属性值
④、text(文本节点):文本本身
2、DOM 查找有四种方法
(1)、不需要查找,可直接获得的元素:
html document.documentElement
head document.head
body document.body
form document.form
(2)、按节点间关系查找,节点树包含所有节点,分为元素和文本
①、父子
A、elem.parentNode 找 elem 的父节点
B、elem.childNodes 找 elem 的所有*直接*子节点,返回所有直接子节点组成的集合,类似于数组
C、elem.firstChild 找 elem 的第一个*直接*子节点
D、elem.lastChild 找 elem 的最后一个*直接*子节点
②、兄弟
A、elem.previousSibling 找 elem 的前一个兄弟
B、elem.nextSibling 找 elem 的下一个兄弟
按节点间关系查找的前提是已经获得了一个节点,用这个节点来查找周围临近的节
点,这种查找方式会连看不见的空字符也算文本节点,会对结果产生一定的干扰,解决
方法是按仅包含元素节点的树结构去查找
①、父子
A、elem.parentElement 找 elem 的父元素
B、elem.children 找 elem 的所有*直接*子元素,返回所有直接子元素组成的集合,类似于数组
C、elem.firstElementChild 第一个*直接*子元素
D、elem.lastElementChild 最后一个*直接*子元素
②、兄弟
A、elem.previousElementSibling 找 elem 的前一个兄弟元素
B、elem.nextElementSibling 找 elem 的下一个兄弟元素
注意: childNodes 和 children 返回的都是动态集合,凡是遍历动态集合,都要先缓存元素个数再遍历,这样不会导致反复查找 DOM 树
比如: for(var i=0,len= childNodes.length;i<len;i++)
(3)、按 HTML 查找(优势:范围可大可小,可设置条件)
①、按 id 查找: var elem=document.getElementById("id")
按 id 查找只能在 document 对象上调用,返回一个元素对象
②、按标签名查找: var elems=parent.getElementsByTagName("标签名");
按标签名查找可在任意父元素上,不但查找直接子元素,还查找所有后代元素,返回多个元素组成的集合
③、按 name 属性查找: var elems=document.getElementsByName("name")
按属性查找专门找表单中有 name 属性的表单元素,但是它只能在 document 上调用,它是返回多个元素组成的集合
④、按 class 属性查找:var elems=parent.getElementsByClassName("class")
按 class 属性查找可在任意父元素上调用,该属性不要求完整匹配,只要包含即可
它会返回多个元素组成的集合,但每次只能按一个条件查找,如果条件复杂,就无法一
句话获得想要的元素
(4). 按选择器查找
①. 只找一个元素
语法: var elem=parent.querySelector("selector");
②. 找多个元素
语法: var elems=parent.querySelectorAll("selector");
(5)、查找总结
①、首次查找
A、如果条件简单: 按 HTML 查找: id、标签、 className
B、如果条件复杂: 按选择器查找
②、已经获得一个元素,找周围相邻: 按节点间关系
(6)、HTML 查找与按选择器查找的区别?
①、使用的难易程度: 当条件复杂时:
按选择器查找——简单
按 HTML 查找——繁琐
②、返回值:
getElementsByTagName() 返回多个元素的*动态*集合
动态集合: 不实际存储对象的属性值,每次访问,都要重新查找 DOM 树
querySelectorAll() 返回多个元素的*非动态*集合
非动态集合: 实际存储对象的所有属性值,即使反复访问集合,也不会导致反复查找DOM 树
③、单次效率:
按 HTML 查找——效率高
按选择器查找——效率低
3、元素的内容
(1)、elem.innerHTML: 获取或设置元素开始标签到结束标签之间的原始 html 代码片段,实际运用的较多
(2)、elem.textContent: 获取或设置元素开始标签到结束标签之间的纯文本内容——IE9+,它可以去掉 html 标签,将转义字符翻译为正文
4、DOM 两大标准
(1)、核心 DOM: 操作一切结构化文档的 API(HTML,XML)
①、elem.attributes 集合: 保存了当前元素的所有属性节点
②、elem.getAttribute("属性名"):获取属性值
③、elem.setAttribute("属性名","值"):修改属性值
④、elem.hasAttribute("属性名"):判断是否包含属性
⑤、elem.removeAttribute("属性名"):移除属性
(2)、HTML DOM: 对部分常用 DOM API 的简化版本,HTML DOM 将标准属性都预定义在元素对象中
①、elem.属性名: 获取属性值
②、elem.属性名="值":修改属性值
③、elem.属性名===""(全空):判断是否包含属性
④、elem.属性名="":移除属性
特例: class 属性和 ES 标准中的 class 重名->DOM -> className
(3)、核心 DOM 与 HTML DOM 的区别
①、核心 DOM 无法操作三大状态属性(disabled 、selected、checked),只能用 HTML DOM,操作(elem.disabled 、elem.selected 、elem.checke),值都是 bool 类型 true/false
②、HTML DOM 不能操作自定义属性,比如: data-toggle="dropdown",只能用核心 DOM操作
5、修改 css 样式
(1)、仅获取/修改内联样式: elem.style.css 属性名
①、问题 1: css 属性名有的带-
解决: 所有 css 属性名都要去横线变驼峰
比如: background-color: backgroundColor、list-style-type: listStyleType
②、问题 2: 所有数值类型的属性值都是带单位的字符串
解决: 获取时,都要去单位,转数值,修改时,将单位拼回数,
③、问题 3: 仅能获得内联样式无法获得样式表中的样式
解决: 计算后的样式——最终应用到元素上的完整样式,分两步完成
A、获得完整样式对象 style
语法:var style=getComputedStyle(elem)
B、获得 style 对象中的 css 属性
语法: style.css 属性名
注意: style 对象中的样式都是只读
结论:获取样式:getComputedStyle,修改样式: elem.style.css 属性名
(2)、运行时修改样式表中的样式,分三步完成
①、获得样式表对象
语法:var sheet=document.styleSheets[i]
②、获得样式表对象中某个 CSSRule(一个选择器{})
语法: var rule=sheet.cssRules[i]
③、修改
语法:rule.style.css 属性名=值
6、添加
(1)、添加分三步来完成
①、创建空元素:
语法: var a=document.createElement("a");
结果:<a></a>
②、设置关键属性
语法:a.href="http://tmooc.cn"、a.innerHTML="go to tmooc";
结果:<a href="http://tmooc.cn">go to tmooc</a>
③、将元素添加到 DOM 树 3 种
A、末尾追加: parent.appendChild(child)
B、中间插入: parent.insertBefore(child, oldChild)
C、替换: parent.replaceChild(child, oldChild)
D、克隆:element.cloneNode()
(2)、尽量少的修改 DOM 树,每次修改 DOM 树,都会导致重新 layout,耗时较长,所以在操作 DOM 树时应遵循以下优化方案
①、如果同时添加父元素和子元素时,应该先在内存将子元素都添加到父元素中,再
将父元素一次性整体添加到 DOM 树,这样只会触发一次 layout
②、如果同时添加多个平级子元素,应使用文档片段,文档片段是内存中临时存储多个子元素的虚拟元素,使用文档片段分 3 步来完成.
A、创建文档片段
语法:var frag=document.createDocumentFragment():
B、将子元素临时添加到frag中
语法:frag.appendChild(child)
C、将frag添加到DOM树,frag不会成为页面元素,添加子元素后,frag自动释放
语法:parent.appendChild(frag)
7、删除
语法:parent.removeChild(child)
8、HTML DOM 常用对象:Select/option
(1)、Select 代表页面上的一个 select 元素
①、: select.value 当前选中项的 value,如果没有 value,就返回选中项的内容
②、select.options 保存 select 下所有 option 元素对象
类似: select.getElementsByTagName("option")
③、select.options.length 保存 select 下 option 的个数
清空 select 下所有 option: select.options.length=0;
select.length 等效于 select.options.length
清空 select 下所有 option: select.length=0 或者 select.innerHTML="";
④、select.selectedIndex 当前选中项的下标
⑤、onchange 当选中项发生改变时,配合 sclect.add(option)完成事件
⑥、select.add(option) 向 select 中添加一个 option
类似: select.appendChild(option),此命令不支持文档片段
⑦、select.remove(i) 移除 select 中 i 位置的一个 option
(2)、Option: 代表页面上的一个 option 元素
语法(创建): var opt=new Option(text,value);创建一个 option 对象,同时设置 opt 的内容为 text,设置 opt 的值为 value
类似: var opt=document.createElement("option");
opt.innerHTML=text;opt.value=value;
属性: .text 代替.innerHTML
.index 表示当前 option 在 select 下的下标位置
9、HTML DOM 常用对象:Table,Table 代表网页中一个 table 元素,它管着行分组
(1) 创建行分组
①、创建表头 var thead=table.createTHead();
②、创建主体 var tbody=table.create TBody();
③、创建表尾 var tfoot=table.create TFoot();
(2)、删除行分组
①、删除表头 table.deleteTHead();
②、删除表尾 table.deleteTFoot();
(3)、获取行分组
①、获取表头 table.tHead
②、获取主体 table.tBodies[i]
③、获取表尾 table.tFoot
(4)、行分组 Thead、 TBody 、Tfoot 控制行
①、添加行,在行分组中 i 位置插入一个新行, 中间插入行,原 i 位置的行向后顺移
A、表头添加行: var tr=thead.insertRow(i);
B、主体添加行:var tr=tbody. insertRow(i);
C、表尾添加行: var tr=tfoot.insertRow(i);
D、固定套路
a、末尾追加一个新行: thead|tbody|tfoot.insertRow();
b、开头插入: thead|tbody|tfoot.insertRow(0);
②、删除行,删除行分组中第 i 行,i 是当前行在行分组内的相对下标位置
语法: thead|tbody|tfoot.deleteRow(i)
删除行时,由于 i 无法自动获得,表格行较多时,手动难以指定,故以上方法较少用,其实在 tr 上都有一个属性 tr.rowIndex,用 tr.rowIndex 删除行,只能通过表格定
位下标来删除,以后凡是删除行都用它
语法: table.deleteRow(tr.rowIndex)
③、获取行: thead|tbody|tfoot.rows
(5)、行 tr 控制单元格 td
①、添加 td: var td=tr.insertCell(i); 省略 i 表示右侧末尾追加
insertCell 不支持添加 th,只能添加 td
②、删除 td: tr.deleteCell(i);
③、获取 td: tr.cells
10、HTML DOM 常用对象 form,form 代表页面上一个表单元素
(1)、获取表单: var form=document.forms[i/id]
(2)、获取所有表单元素的集合:form.elements (input、select、textarea 、button)
(3)、获得表单中表单元素的个数:form.length => form.elements.length
(4)、获得表单元素:var elem=form.elements[i/id/name]
如果表单元素有 name 属性:form.name
(5)、手动提交表单:form.submit();
(6)、任何方式提交表单之前自动触发:form.onsubmit,常用于在提交之前,验证所有表单元素的内容
(7)、让 elem 获得焦点:elem.focus();
(8)、让 elem 失去焦点:elem.blur();
11、HTML DOM 常用对象 Image,Image 代表页面上一个 img 元素
语法: var img=new Image();
12、过渡动画的两种实现方法
(1)、css 中: 添加 transition
(2)、js 中: 修改 css 属性值
不支持 transition: display 、zIndex
支持: width 、height 、opacity 、bottom、top、left、right
13、 BOM(Browser Object Model)
BOM 是专门操作浏览器窗口的 API 比如: alert 、prompt 、confirm,它存在两大问题
(1)、没有标准——兼容性问题
(2)、不可定制
window 对象的 2 个角色
(1)、代替 ES 中的 Global 充当全局作用域对象
(2)、封装所有 BOM 和 DOM 的 API
14、BOM 打开超链接的 4 种方法
(1)、在当前窗口打开,可后退
①、html: <a href="url" target="_self"></a>
②、js: /*window.*/open("url","_self")
(2)、在当前窗口打开,不可后退
①、js: location.replace("url");用新 url 代替 history 中当前 url,网页实现无法后退
(3)、在新窗口打开,可打开多个
①、html:<a href="url" target="_blank"></a>
②、js: open("url","_blank")
(4)、在新窗口打开,只能打开一个
①、html:<a href="url" target="自定义 name 属性值"></a>
②、js: open("url","自定义 name 属性值")
内存中每个窗口都有一个唯一的 name 属性来标示一个窗口,浏览器规定,相同 name属性的窗口只能打开一个,其实 html 中的 target 属性就是在设置新窗口的 name 属性值,如果 target 中使用自定义的窗口名,则只能打开一个,name 属性预定义两种:
①、_self: 默认使用当前窗口自己的 name 属性,新窗口覆盖当前窗口
②、_blank: 意为不指定窗口名,浏览器会随机生成不同的窗口名,每次打开新窗口
都随机生成不同的 name 并且可打开任意多个
15、定时器(2 种)
(1)、周期性定时器——让程序按照指定时间间隔,反复执行一项任务,分 3 步完成:
①、任务函数: 让定时器反复调用的函数
②、启动定时器: var timer=setInterval(任务函数,间隔的毫秒数)
③、停止定时器: clearInterval(timer),2 种:
A、用户手动停止定时器: 用按钮调用 clearInterval
B、自动停止定时器,在任务函数中,设定临界条件,如果达到临界条件就自动调用clearInterval
问题: timer 中的序号会残留在 timer 变量中
解决:停止定时器后,主动清空 timer=nul
(2)、一次性定时器——让程序先等待一段时间,再自动执行一次任务,执行一次后,定时器自动停止,分 3 步完成:
①、任务函数
②、启动: var timer=setTimeout(任务函数, 等待的毫秒数)
③、停止: clearTimeout(timer)
16、window.onload 页面(HTML CSS JS)加载后触发
<body><script>中的 js 很可能和 CSS 并行加载,甚至先与 css 中的 transition 执行,所以,只要一段代码必须在 css 加载后才能执行都要放在 window.onload 中
笔试: 定时器中的函数,只能在主程序所有程序执行后才能执行???
for(var i=0;i<3;i++){
setTimeout(function(){
console.log(i);
},0);
}
//结果: 3 3 3
//alert("Hello") 如果不点确定,则永远不输出 333
17、window 常用属性:history、location、document、navigator、screen、event
18、history: 保存当前窗口打开后,成功访问过的历史记录的栈,history 封装的非常严密,只能前进、后退、刷新
语法:history.go(n)
(1)、前进: go(1)
(2)、后退:go(-1)
(3)、刷新:go(0)
19、location: 专门保存当前窗口正在打开的 url 的对象
(1)、location.href 保存了完整的 url
在当前窗口打开: location.href=新 url
(2)、location.protocol: 协议
①、.host: 主机名+端口号
②、.hostname: 主机名
③、.port: 端口号
(3)、location.pathname: 相对路径
①、.hash: 锚点地址#xxx
②、.search: 表单提交后地址栏中的查询字符串
③、?变量名=值&变量名=值&...
Loction 的常见应用有以下几种:
(1)、替换 history 中当前 url,实现进制后退: location.replace("新 url")
(2)、在当前页面打开,可后退:
location.assign("新 url") => location.href="新 url"=> location="新 url"
(3)、刷新页面: location.reload(false/true);
(4)、笔试: false/true 的差别
浏览器本地是有缓存的,浏览器的缓存中会保存 css 图片等静态资源,每次请求时,首先查看缓存中是否有想要文件,没有想要文件或文件过期,才去服务器下载新文件
①、reload(false) 优先使用本地缓存的文件
②、reload(true) 强制去服务器下载新文件
20、event: 绑定事件
(1)、在 HTML 中绑定: <ANY.on 事件名="js 语句">
问题: 不符合内容与行为分离的原则——不便于维护
(2)、在 js 中动态绑定,2 种
①、一个事件只绑定一个处理函数
语法: elem.on 事件名=function(){ //this->elem }
解除绑定: elem.on 事件名=null;
问题:每个事件只能绑定一个处理函数
②、一个事件可同时绑定多个处理函数
语法:elem.addEventListener("事件名",function(){ //this->elem })
(3)、解除绑定: elem.removeEventListener("事件名","函数名");
如果一个事件处理函数可能被动态移除,则绑定时,不能使用匿名函数,必须使用有名称的函数
21、事件模型: DOM 标准分为 3 个阶段
(1)、捕获: 由外向内,记录各级父元素绑定的事件处理函数
(2)、目标触发: 首先执行目标元素上的事件处理函数
(3)、冒泡: 由内向外,反向执行捕获阶段记录的处理函数
22、事件对象─事件发生时自动创建的封装事件信息提供操作事件的 API 的对象
通常作为事件处理函数的第一个参数,默认自动传入!
语法: .on 事件名=function(e){
//e 会自动获得事件对象
}
阻止蔓延/冒泡: e.stopPropagation();
取消事件/阻止默认行为: e.preventDefault();
尽量少的添加事件监听,因为浏览器触发事件监听,是采用遍历查找的方式,添加的
监听越多,遍历的速度越慢,如果多个子元素都要绑定相同的事件,只要在父元素绑定
一次,所有子元素即可共用,这样绑定需要注意两点:
(1)、获得目标元素,不能用 this, 因为 this 指父元素,应该用 e.target,保存实际点击的目标元素
(2)、鉴别目标元素,先判断目标元素的 nodeName 或 className...只有目标元素符合要求时,才执行事件操作
事件坐标共 3 对:
(1)、相对于整个屏幕左上角的坐标: e.screenX|screenY
(2)、相对于文档显示区左上角的坐标:e.clientX|clientY
(3)、相对于当前元素左上角的坐标: e.offsetX|offsetY
23、页面滚动
(1)、事件: window.onscroll
(2)、获得页面滚动位置: document.body.scrollTop
24、屏幕操作相关命令
(1)、网页可见区域宽: document.body.clientWidth;
(2)、网页可见区域高: document.body.clientHeight;
(3)、网页可见区域宽: document.body.offsetWidth (包括边线的宽);
(4)、网页可见区域高: document.body.offsetHeight (包括边线的宽);
(5)、网页正文全文宽: document.body.scrollWidth;
(6)、网页正文全文高: document.body.scrollHeight;
(7)、网页被卷去的高: document.body.scrollTop;
(8)、网页被卷去的左: document.body.scrollLeft;
(9)、网页正文部分上: window.screenTop;
(10)、网页正文部分左: window.screenLeft;
(11)、屏幕分辨率的高: window.screen.height;
(12)、屏幕分辨率的宽: window.screen.width;
(13)、屏幕可用工作区高度: window.screen.availHeight;
(14)、屏幕可用工作区宽度:window.screen.availWidth