前言
DOM是Javascript操作HTML的常用且最直观的方式,熟练掌握DOM操作及相关事件就可以自己编写一个简单的动态交互页面了,首先先来看看DOM吧~
DOM简介
- DOM全称document object model
-
文档
整个HTML文件就是一个文档
-
对象
HTML文件中的每个节点都在JS中是一个对象
-
模型
JS使用模型来表示各个对象之间的关系
-
JS中有宿主对象document,它是window对象的属性,也就是文档对象本身
DOM中的对象又称为节点对象,共有4种常用节点分别为:
nodeName nodeType nodeValue 文档节点 #document 9 null 元素节点 标签名 1 null 属性节点 属性名 2 属性值 文本节点 #text 3 文本内容
document对象
-
常用属性:
-
body
该属性封装的是body元素对象的引用
-
documentElement
属性值为HTML元素对象
-
all
属性值为当前页面中的所有元素节点的数组
这个属性值本身为
undefined
,它的typeof
值也为undefined
-
URL
获取当前页面的url
-
domain
获取当前页面的域名部分
-
referrer
获取是哪个页面链接跳转到当前页面,没有则返回空字符串
-
-
常用方法:
ps: 这些方法对于普通的DOM对象同样适用,只是DOM范围为普通DOM对象内部元素而非全局元素
-
基本语法:
document.方法();
-
getElementById()
-
通过ID值获取对应的节点
-
IE8中不区分大小写
-
IE7中会匹配表单元素的name值
-
-
getElementsByTagName()
-
通过标签名获取一组节点
-
返回值是一个
HTMLCollection
,即使只获取到一个节点时,也封装成HTMLCollection
返回
-
-
getElementsByName()
-
通过
name
的值获取一组节点 -
返回值是一个
nodelist
,与数组相似,可以使用forEach
方法,即使只获取到一个节点时,也封装成nodelist
返回
-
-
getElementsByClassName()
-
通过
class
属性获取一组节点 -
返回值是一个
HTMLCollection
,即使只获取到一个节点时,也封装成HTMLCollection
返回 -
仅支持IE8以上
-
-
querySlector()
- 通过CSS选择器返回一个节点,当能匹配多个节点时,返回满足匹配的第一个节点 仅支持IE7以上
-
querySlectorAll()
-
通过CSS选择器返回一组节点
-
返回值是一个
nodelist
,与数组相似,可以使用forEach
方法,即使只获取到一个节点时,也封装成nodelist
返回 -
仅支持IE7以上
-
HTMLCollection 支持通过索引或者字符串来查找需要的节点,在后台则分别使用它的
item()
与namedItem()
方法来获取NodeList 与
HTMLCollection
类似的一个node集合两者的值都是动态的,每次去取这些值的属性都会引发一次文档查询,因此需要尽量减少直接访问
-
事件
此处简单提及事件,详细内容会在下一篇博文中讲解
当用户与浏览器进行任何交互时都会产生相应的事件
JS可以通过给事件绑定响应函数来使事件触发时执行相应的响应函数,响应函数会在执行事件时才会被执行,因此响应函数内部使用的变量可能会与预期不一致
var div = document.getElementByTagName("test");
for(var i = 0; i < div.length; i++){
div[i].onclick = function(){
console.log(i);
}
}
//结果永远会是div的长度的值
-
绑定事件有两种方法:
-
第一种:
<button id="btn" onclick="alert('a')">btn</button>
结构与行为耦合,不要使用
-
第二种:
<button id="btn">btn</button> <script> var btn = document.getElementById("btn"); btn.onclick = function(){ alert("a"); } </script>
常用方式
-
文档加载顺序
浏览器加载HTML文件时是自上向下加载,因此当js代码写在文档之前时可能出现无法获取到节点的情况
使用window.onload
绑定响应函数可以使响应函数内的代码都在整个文档加载完成之后执行
或者将JS代码写在HTML文档的最后来保证文档先于JS代码加载
元素节点
元素节点拥有一个方法和属性来获取它们内部的节点,除了元素的class
属性以外,所有的属性都可以以元素节点对象属性的方式获取,class
的值需要用className
属性来获取,因为class
是js中的保留字
- 常用方法:
-
getElementById()
通过ID值获取当前元素节点内部对应的节点
-
getElementsByTagName()
用来获取当前元素节点内部的特定标签的元素节点
返回值是一个
HTMLCollection
,与数组相似,不能使用forEach
方法,即使只获取到一个节点时,也封装成HTMLCollection
返回 -
getElementsByName()
通过name的值获取当前元素节点内部的一组节点
返回值是一个
nodelist
,与数组相似,可以使用forEach
方法,即使只获取到一个节点时,也封装成nodelist
返回 -
getElementsByClassName()
通过
class
属性获取当前元素节点内部的一组节点返回值是一个
HTMLCollection
,与数组相似,不能使用forEach
方法,即使只获取到一个节点时,也封装成HTMLCollection
返回仅支持IE8以上
-
querySlector()
通过CSS选择器返回当前元素节点内部的一个节点,当能匹配多个节点时,返回满足匹配的第一个节点
仅支持IE7以上
-
querySlectorAll()
通过CSS选择器返回当前元素节点内部的一组节点
返回值是一个
nodelist
,与数组相似,可以使用forEach
方法,即使只获取到一个节点时,也封装成nodelist
返回仅支持IE7以上
-
hasChildNodes()
返回当前节点内部是否拥有1个或多个子节点
-
-
常用属性:
-
innerHTML
获取当前元素节点中的HTML代码
-
innerText
获取当前元素节点中的文本
-
childNodes
获取当前元素节点中所有的子节点并以数组形式返回,按照ES标准如果当前元素节点中有空格时也会被包括在内
IE8及以下浏览器没有实现这一标准,因此只会获取到内部的元素节点
-
children
获取当前元素节点中的所有子元素节点
-
firstChild
获取当前元素节点中的第一个子节点
-
firstElementChild
获取当前元素节点中的第一个子元素节点, 仅支持IE8以上
-
lastChild
获取当前元素节点中的最后一个子节点
-
lastElementChild
获取当前元素节点中的最后一个子元素节点, 仅支持IE8以上
-
parentNode
获取当前元素节点的父节点
-
previousSibling
获取当前元素节点的前一个兄弟节点
-
previousElementSibling
获取当前元素节点的前一个兄弟元素节点, 仅支持IE8以上
-
nextSibling
获取当前元素节点的后一个兄弟节点
-
nextElementSibling
获取当前元素节点的后一个兄弟元素节点, 仅支持IE8以上
-
ps:当响应函数给某个节点绑定时,这个响应函数中的this
就是这个节点
DOM的增删改
-
常用方法:
-
createElement()
新建一个元素节点,参数是元素节点的标签名
-
语法:
document.createElement(“标签名”);
-
-
createTextNode()
新建一个文本节点,参数是文本节点的内容
-
语法:
document.createTextNode(“文本内容”);
-
-
appendChild()
向一个父节点中添加一个子节点
-
语法:
父节点.appendChild(子节点);
-
-
insertBefore()
向一个子节点前添加一个新的节点
-
语法:
父节点.insertBefore(新节点, 原节点);
-
-
removeChild()
移除一个子节点
-
语法:
父节点.removeChild(子节点);
-
-
replaceChild()
将原节点替换成新节点
-
语法:
父节点.replaceChild(新节点, 原节点);
-
-
cloneNode()
返回值是一个节点的拷贝, 接受一个布尔值作为参数, true则为深度拷贝, false为浅拷贝
-
语法:
节点.cloneNode(boolean)
-
-
normalize()
返回一个经过一定处理后的节点, 这个过程会合并调用此方法的节点中的相邻文本节点并删除空白的文本节点
-
splitText(offset)
将一个文本节点按照偏移量参数分割成两个兄弟节点, 返回值是后一个文本节点, 此方法只可被用于文本节点
-
-
由于许多方法都要通过父节点调用对应的方法来操作节点,因此可使用以下代码来对节点进行直接操作而不需要获取它的父节点
子节点.parentNode.removeChild(子节点);
-
对DOM的增删改操作可以用父节点的
innerHTML
的字符串操作来代替,由于是整体修改所以这个父节点包括它的子节点绑定的函数都会失效代码1:
<html> <head> </head> <body> <div> <input type="checkbox" id="checkallbox"/>AAAAA </div> </body> </html> <script> var input = document.createElement("input"); var checkallbox = document.getElementById("checkallbox"); input.type="checkbox"; checkallbox.parentNode.insertBefore(input, checkallbox); </script>
代码2
<html> <head> </head> <body> <div> <input type="checkbox" id="checkallbox"/>AAAAA </div> </body> </html> <script> var div = document.getElementByTagName("div")[0]; div = "<input type='checkbox'/>" + div.innterHTML; </script>
两部分代码功能基本相同,唯一区别是如果input标签绑定了响应函数,在代码2中会失效
DOM的遍历
-
document.createNodeIterator(root, whatToShow, filter, entityReferenceExpansion)
返回值是一个
NodeIterator
对象-
参数一: 指定开始遍历的根节点,只能遍历body标签中的节点,其他标签会被跳过
-
参数二: 标识要访问标签元素的哪些节点,它是一个数字代码,语义化信息保存在
NodeFilter
类型中NodeFilter.SHOW_ALL 显示所有节点 NodeFilter.SHOW_ELEMENT 显示元素节点 NodeFilter.SHOW_ATTRIBUTE 显示属性节点 NodeFilter.SHOW_TEXT 显示文本节点 NodeFilter.SHOW_DOCUMENT 显示文档节点 NodeFilter.SHOW_COMMENT 显示注释节点
-
参数三: 是一个
NodeFilter
对象,或者一个返回接受或者拒绝特定标签的函数-
NodeFilter
对象是一个只有acceptNode
方法的对象,该方法是一个迭代回调函数,有一个node参数,迭代器中有几个节点则会被执行几次 -
返回值应是
NodeFilter.FILTER_ACCEPT
或者NodeFilter.FILTER_SKIP
-
通过返回值来判断是否能够访问当前回调函数中的标签元素
var filter = { acceptNode: function(node){ return node.tagName.toLowerCase() == "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP } }
- 这个参数也可以是一个与
acceptNode
方法相似的函数
function acceptNode(node){ return node.tagName.toLowerCase() == "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP }
-
-
参数四: 标识是否要扩展实体引用,在html中没有意义
NodeIterator
有两个方法nextNode()
与previousNode()
来访问迭代器中的节点刚创建好的迭代器一开始是指向一个标识根节点的指针,第一次调用
nextNode
会指向第一个子节点当越界时返回值为
null
支持IE9+和其他浏览器
-
-
document.createTreeWalker(root, whatToShow, filter, entityReferenceExpansion)
返回一个
TreeWalker
对象可以实现在经过过滤的root节点中任意跳跃,非常灵活
参数与上一个函数一致,唯一有区别的在于第三个参数的
Nodefilter
函数增加一个有效的返回值NodeFilter.FILTER_REJECT
此时迭代器会跳过这个节点及其后代元素而不是像
NodeFilter.FILTER_SKIP
那样单纯跳过当前节点,仍然进行深度遍历在
createNodeIterator
的第三个参数中返回NodeFilter.FILTER_REJECT
作用与NodeFilter.FILTER_SKIP
相同-
相较于
NodeIterator
对象,TreeWalker
对象多出一些重要方法:parentNode()
firstChild()
lastChild()
nextSibling()
previousSibling()
通过这些方法可以实现在
TreeWalker
对象中任意跳转到目标元素 W3C标准DOM2,但是IE不支持
-
DOM范围
可以通过范围来选择文档中的一个区域而不必考虑节点的界限(选择在后台完成,对用户是不可见的)
-
范围创建语句
document.createRange()
返回值为range对象
-
以下属性可以获取到当前范围在文档中的位置信息
-
startContainer
包含范围起点的节点,即起点的父节点
-
startOffset
范围起点在
startContainer
中的偏移量如果起点是文本节点,注释节点,cdata节点则值是范围起点节点距离
startContainer
需要跳过的字符串如果起点是元素节点,则值是范围起点节点在
startContainer
中的子节点索引 -
endContainer
包含范围终点的节点,即终点的父节点
-
endOffset
范围终点在
startContainer
中的位置值计算规则与
startOffset
相同, 表示索引时通常是起始位置加1 -
commonAncestorContainer
起点节点与终点节点共同的最近的那个祖先节点
-
-
通过以下方法可以进行选择范围操作
-
selectNode()
选中传入参数的那个节点及内部节点
-
selectNodeContents()
选中传入参数的那个节点的内部节点
-
setStartBefore(refnode)
将范围起点设置为refnode之前,refnode就成为了范围选区的第一个节点
startContainer
变成refnode的父节点,startOffset
的值变成refnode在父节点中的索引 -
setStartAfter(refnode)
将范围起点设置为refnode之后,refnode就成为了范围选区外的节点,它的下一个节点是范围选区内第一个节点
startContainer
变成refnode的父节点,startOffset
的值变成refnode在父节点中的索引加1 -
setEndBefore(refnode)
将范围终点设置为refnode之前,refnode就成为了范围选区外的节点,它的上一个节点是范围选区内最后一个节点
startContainer
变成refnode的父节点,endOffset
的值变成refnode在父节点中的索引 -
setEndAfter(refnode)
将范围终点设置为refnode之后,refnode就成为了范围选区 内的最后一个节点
startContainer
变成refnode的父节点,endOffset
的值变成refnode在父节点中的索引加1 -
setStart()
传入两个参数,第一个参数是参照节点,第二个参数是偏移量
参照节点会被当做
startContainer
,偏移量会变成startOffset
-
setEnd()
传入两个参数,第一个参数是参照节点,第二个参数是偏移量
参照节点会被当做
endContainer
,偏移量会变成endOffset
ps: 当改变范围起点到范围终点之后时, 范围终点会自动变成起点的偏移值, 同理改变范围终点到范围起点时, 范围起点也会自动变成终点的偏移值
-
-
创建范围之后可以使用以下方法对选中内容进行操作
-
deleteContents()
将选中的内容从文档中删除
-
extractContents()
将选中的内容从文档中提取出来并作为返回值返回
-
cloneContents()
拷贝一份选中的内容并作为返回值返回
-
insertNode()
将一个节点插入到范围的最开始处
-
surroundContents()
将节点移动到范围之外,刚好包裹此范围,并且删除节点内原有的内容
-
collapse()
可以将选中的范围进行折叠,使范围起点与终点都指向同一个位置
传入一个布尔值作参数,true表示折叠到原范围的起点,false表示折叠到原范围的终点
-
compareBoundaryPoints()
-
传入两个参数
第一个参数是需要比较哪两个点的一个常量,值可以是
Range.START_TO_START
,Range.START_TO_END,Range.END_TO_START
,Range.END_TO_END
第二个参数是需要比较的范围,如果调用这个方法的范围与传入的范围的需要比较的点是前者在前则返回-1,相同返回0,前者在后则返回1
-
-
detach()
在使用完范围后最好使用此方法来使范围从文档中分离,然后再删除引用,方便垃圾回收机制清理
-
IE8及以下的文本范围
-
创建文本范围
在
input
,button
,textarea
,body
等几个元素可以使用createTextRange
来创建文本节点 -
选取文本范围
-
findText()
传入一个字符串参数,如果找到了对应的范围则返回值为true否则为false同时让范围包围这段文本
-
moveToElementText()
传入一个DOM节点,让范围包含这个节点的文本信息与标签信息,与selectNode作用类似
-
move()
将范围折叠到指定位置
-
moveStart()
将范围的起点移动到指定位置
-
moveEnd()
将范围的终点移动到指定位置
-
expand()
将部分文本的范围变成完整文本的范围
-
这些方法都传入两个参数
第一个参数是移动单位
第二个参数是移动单位的数量
-
移动单位可以是以下字符串
character:逐个字符移动
word:逐个单词移动
sentence: 逐个句子(一系列以叹号问号句号结尾的字符)移动
textedit:移动到当前选区开始或结束的位置
-
-
修改范围内的内容
有
text
属性与pasteHTML()
方法前者改变文本,后者可以包括标签
-
折叠范围
与其他浏览器相同使用
collapse()
来折叠范围但是没有
collapsed
属性来判断是否折叠,但是可以使用boundingWidth
属性来判断范围宽度是否为0 -
比较范围
-
compareEndPoints()
用法与作用同
compareBoundaryPoints
类似第一个参数是字符串,表示需要比较哪两个点如:
StartToEnd
等格式第二个参数是待比较的范围
-
-
复制范围
-
duplicate()
作用与
cloneContents()
相同
-
操作样式
在css的属性中如果有用 - 来连接的情况,在style需要使用驼峰命名法来修改属性名从而设置或者读取对应的样式
- 获取节点样式的属性或方法
-
style
-
语法:
节点对象.style.属性值
通过这个属性能够修改和读取到节点的内联样式,当节点没有内联样式时获取到的是空字符串
如果在其他地方的css样式中设置了
!important
,那么通过js操作这个属性会失效 -
-
currentStyle
此属性只能在IE浏览器中使用并且是只读的,当未给某个节点设置长度值时会返回auto
-
语法:
节点对象.currentStyle.属性值
-
-
getComputedStyle()
此方法是
window
的方法,可以直接使用,而且也是只读的在获取width属性时当未设置具体样式的情况下,会返回节点对象实际呈现的结果而不是auto
-
语法:
getComputedStyle(节点对象, 伪元素(常设置为null))[样式名]
此方法支持IE8以上以及其他浏览器
-
-
获取节点对象相关位置的常用属性:
-
clientHeight
获取元素的视图高度,包括元素的内容高度与内边距高度,返回值是一个数字,只读, 在根标签的此属性就是指代视口大小,与此规则无关
-
clientwidth
获取元素的视图宽度,包括元素的内容宽度与内边距宽度,返回值是一个数字,只读, 在根标签的此属性就是指代视口大小,与此规则无关
-
offsetHeight
获取元素的真实高度,包括元素的内容高度,内边距高度与边框高度,当被父元素隐藏或者滚动时不会影响这个值,返回值是一个数字,只读,在IE11以下浏览器中的根标签的此属性就是指代视口大小,与此规则无关
-
offsetWidth
获取元素的真实宽度,包括元素的内容宽度,内边距宽度与边框宽度,当被父元素隐藏或者滚动时不会影响这个值,返回值是一个数字,只读,在IE11以下浏览器中的根标签的此属性就是指代视口大小,与此规则无关
-
offsetParent
获取当前元素的定位父元素,即设置了
position
的最近父元素,当所有父元素都没有设置时,返回body
元素, 但是在除了火狐的浏览器中当position
设置为fixed
,返回null
在IE7包括IE7时,
position
为absolute
与relative
时返回html
,body
与html
间的margin
清除后可看做body
. 在IE7以下时当祖先元素开启hasLayout
返回最近开启的元素PS: hasLayout相关的内容还是比较多的,现在只需有个概念,在进阶内容中会详细讲述
-
offsetTop
获取当前元素相对于定位父元素的上侧偏移量(相对于
offsetParent
的内边距),即使父元素自身有偏移量也不改变这个值的大小,返回值是一个数字,只读 -
offsetLeft
获取当前元素相对于定位父元素的左侧偏移量(相对于
offsetParent
的内边距),即使父元素自身有偏移量也不改变这个值的大小,返回值是一个数字,只读 -
scrollHeight
获取当前元素的真实高度,包括元素的内容高度与内边距高度,当被父元素隐藏或者滚动时不会影响这个值,返回值是一个数字,只读
-
scrollWidth
获取当前元素的真实宽度,包括元素的内容宽度与内边距宽度,当被父元素隐藏或者滚动时不会影响这个值,返回值是一个数字,只读
-
scrollTop
获取当前元素的右侧滚动条滚动的像素,返回值是一个数字,只读
-
scrollLeft
获取当前元素的下侧滚动条滚动的像素,返回值是一个数字,只读
公式:
scrollHeight - scrollTop = clientHeight
可以证明滚动条滚动到最底端-
getBoundingClientRect()
这是DOM中唯一能够直接获取节点相对于视图位置的方法,且兼容性相当的好,需要熟练掌握,此处有个小案例来帮助学习这个方法
获取一个元素四个角的相对位置与高宽且这些值是基于border-box的,也就是说返回的width, height属性都包括了节点的content, padding, border的高宽
在IE7及以下没有
width
与height
属性获取节点的绝对位置可以直接让相对位置加上滚动的像素来得到
BOM
-
-
Window
代表浏览器窗口并且保存浏览器的全局对象
window.open
当弹窗被浏览器内置工具屏蔽时会返回null
,被工具屏蔽会报错 -
Navigator
代表浏览器信息
由于历史原因大部分属性没有意义,只剩下
userAgent
可以判断浏览器类型/chrome/i.test(navigator.userAgent) /firefox/i.test(navigator.userAgent)
IE11的
userAgent
中没有IE信息必须使用"ActiveXObject" in window
来进行判断edge浏览器仍然可以使用
userAgent
中有无edge字符串来判断 -
Location
代表浏览器地址栏信息
如果直接打印
location
,则能获取到当前网址栏的信息修改
location
属性为一个完整路径或相对路径则页面会直接跳转-
assign()
直接跳转到某个页面,与直接修改
location
一样 -
reload()
用于重新加载当前页面,与刷新按钮一样,传递
true
作为参数时会强制清空缓存 -
replace()
与
assign
类似但是它不会生成历史记录,无法回退
-
-
History
代表历史记录,只能前进后退
-
length
当次访问的历史次数,关闭浏览器时清零
-
back()
回到上一个页面
-
forward()
前往下一个页面
-
go()
使用整数作为参数来指定需要跳转到的页面
-
-
Screen
代表用户当前屏幕
这些属性都保存在window
中,可以直接使用