JavaScript中的DOM学习
1、DOM
- DOM -> Document Object Model ,文本对象模型
- DOM以树状结构表示整个HTML文档
2、延迟执行(window.onload)
window.onload
:用于在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻执行某个方法。
window.onload = function(){
// 希望执行的操作,如访问元素节点等
}
3、节点与操作节点
3.1、访问元素节点的常用方法
document.getElementById()
:通过id
获取元素document.getElementsByTagName()
:通过标签名
获取元素数组
document.getElementsByClassName()
:通过类名
获取元素数组
document.querySelector()
:通过选择器
获取元素document.querySelectorAll()
:通过选择器
获取元素数组
3.1.1、getElementById
<p id = "kun">喝茶吗?坤哥🍵</p>
var k = document.getElementById('kun')
3.1.2、getElementsByTagName和getElementsByClassName
<div id="box1">
<p>乌</p><p>鸡</p><p>哥</p>
</div>
<div id="box2">
<p>只</p><p>因</p><p>你</p><p>太</p><p>美</p>
</div>
var p = document.getElementsByTagName('p')
console.log(p) // 获取了8个P节点
// 若只需要box2中的p节点
var box2 = document.getElementById('box2')
var ps_box2 = box2.getElementsByTagName('p')
console.log(ps_box2)
3.1.3、querySelector和querySelectorAll
<div id="box2">
<p>只</p><p>因</p><p>你</p><p>太</p><p>美</p>
</div>
var p1 = document.querySelector("#box2 p")
console.log(p1) // <p>只</p> , 只获取符合的第一个
var p2 = document.querySelectorAll("#box2 p")
console.log(p2) // 获取5个p节点的数组
方法 | 兼容性 |
---|---|
getElementById | IE6 |
getElementsByTagName | IE6 |
getElementsByClassName | IE9 |
querySelector | IE8部分兼容、IE9完全兼容 |
querySelectorAll | IE8部分兼容、IE9完全兼容 |
3.2、节点的关系
节点 = 元素节点 + 文本节点(包括空白文本节点)
3.2.1、排除文本节点的干扰
关系 | 考虑所有节点 | 只考虑元素节点(IE9兼容) |
---|---|---|
子节点 | childNodes | children |
父节点 | parentNode | parentNode |
第一个子节点 | firstChild | first |
最后一个子节点 | lastChild | last |
前一个兄弟节点 | previousSibling | previous |
后一个兄弟节点 | nextSibling | next |
3.2.2、示例
<div id="box">
<p>只</p>
<p id="para">因</p>
<p>你</p><p>太</p><p>美</p>
</div>
var b = document.getElementById('box')
var p = document.getElementById('para')
console.log(b.childNodes) // NodeList(9) [text, p, text, p#para, text, p, p, p, text] div标签与p标签中间的空白换行也算是节点(空白文本节点)
console.log(b.children) // HTMLCollection(5) [p, p#para, p, p, p]
console.log(p.previousSibling) // #text 前一个兄弟节点为空白文本节点
console.log(p.previousElementSibling) // <p>只</p>
3.2.3、常用的节点函数(兼容IE6)
- 获取所有的子元素节点(兼容IE6)
function getChildren(node){
var arr = []
for (var i = 0 ;i < node.childNodes.length;i++){
if (node.childNodes[i].NodeType == 1){
arr.psuh(node.childNodes[i])
}
}
return arr
}
- 获取前一个元素兄弟节点(兼容IE6)
function getElementPreSibling(node){
var o = node
// 确保node节点有前一个节点
while (o.previousSibling != null){
if (o.previousSibling.NodeType == 1){
return o.previousSibling
}
// 让o称为它的前一个节点
o = o.previousSibling
}
}
- 获取所有的元素兄弟节点(兼容IE6)
function getAllElementSibling(node){
var o = node
var pre = []
var next = []
while (o.previousSibling != null){
if (o.previousSibling.NodeType == 1){
pre.unshift(o.previousSibling)
}
// 让o称为它的前一个节点
o = o.previousSibling
}
o = node
while (o.nextSibling != null){
if (o.nextSibling.NodeType == 1){
next.push(o.nextSibling)
}
// 让o称为它的前一个节点
o = o.nextSibling
}
// 合并数组
return pre.concat(next)
}
3.3、改变节点中的内容
innerHTML
属性:以HTML语法设置节点中的内容innerText
属性:以纯文本的形式设置节点中的内容
<div id="box">
<p>只</p><p id="para">因</p><p>你</p><p>太</p><p>美</p>
</div>
<script>
var b = document.getElementById('box')
b.innerHTML = "<p>坤</p>"
// b.innerText = "坤"
</script>
注意:inner会把新内容
覆盖
旧内容,而不是插入。
3.4、改变元素节点中的css样式
节点.style.属性
= '属性值' -- 属性值的写法与css中的写法保持一致
<div id="box" wid>坤</div>
var b = document.getElementById('box')
b.style.color = 'red'
3.5、改变元素节点中的HTML属性(标准W3C属性)
节点.属性
= '属性值'
<img id="box" src="" alt="坤哥没了">
var b = document.getElementById('box')
b.src = 'url'
3.6、改变元素节点中的HTML属性(非标准W3C属性)
节点.setAttribute('属性','属性值')
:设置属性节点.getAttribute('属性')
:获取属性
<div id="box"></div>
var b = document.getElementById('box')
b.setAttribute('data-n',10)
var d = b.getAttribute('data-n')
console.log(d) // 10
3.7、创建节点
document.createElement()
方法创建一个指定的tagName
的HTML元素- 新创建的节点是“孤儿节点”,意味着并没有挂载到DOM树上,无法看见它
// 创建一个div节点
var oDiv = document.createElement('div')
3.8、插入节点
appendChild()
:将某个节点插入到父节点的内部,成为它的最后一个子节点insertBefore()
:将某个节点插入到父节点的内部,成为某个兄弟节点的前节点
父节点.appendChild(某个节点)
父节点.insertBefore(某个节点,兄弟节点)
3.9、移动节点
原理
:一个节点不能同时位于DOM树的两个位置。故可以使用appendChild()
和insertBefore()
来进行移动
新父节点.appendChild(某个节点)
新父节点.insertBefore(某个节点,兄弟节点)
3.10、删除节点
removeChild()
:从DOM树中删除一个子节点- 节点不能删除自己,只能由父节点进行删除
父节点.removeChild(删除节点)
3.11、克隆节点(深浅克隆)
- 克隆出的节点是
孤儿节点
- 浅克隆:
cloneNode()
- 深克隆:
cloneNode(true)
var 孤儿节点 = 克隆节点.cloneNode()
var 孤儿节点 = 克隆节点.cloneNode(true)
4、事件
- 用户与网页的交互动作称为
事件
。
4.1、onxxx
事件监听
- 常见的
鼠标事件
监听 - 常见的
键盘事件
监听 - 常见的
表单事件
监听
4.1.1、常见的鼠标事件监听
事件名 | 事件描述 |
---|---|
| 鼠标单击某个对象 |
| 鼠标双击某个对象 |
| 鼠标按键在某个对象上被按下 |
| 鼠标按键在某个对象上被松开 |
| 鼠标按键在某个对象上移动 |
| 鼠标进入某个对象(不冒泡) |
| 鼠标进入某个对象(冒泡) |
| 鼠标离开某个对象 |
| 鼠标滚动事件 |
<div id = "box"></div>
var oBox = document.getElementById('box')
oBox.onclick = function(){
console.log('我是onclick')
}
4.1.2、常见的键盘事件监听
事件名 | 事件描述 |
---|---|
| 键盘按键被按下(无法识别系统按钮,如:箭头键) |
| 键盘按键被按下(优于onkeypress) |
| 键盘按键被松开 |
<input type="text" id="nameField">
var nameField = document.getElementById('nameField')
// 当按下`shift`等按键时,无效 -- 无法识别
nameField.onkeypress = function(){
console.log('onkeypress')
}
4.1.3、常见的表单事件监听
事件名 | 事件描述 |
---|---|
| 输入框输入完内容后,输入框失焦后触发 |
| 输入框中输入时就会触发(IE9以下不支持) |
| 某元素获得焦点(点击/tab) |
| 某元素失去焦点 |
| 表单被提交 |
| 表单被重置 |
<form id="myform">
<p>
姓名:
<input type="text" name="nameField">
</p>
</form>
var myform = document.getElementById('myform')
var nameField = myform.nameField;
nameField.onchange = function(){
console.log("onchange")
}
nameField.oninput = function(){
console.log("input")
}
4.2、addEventListener()
监听
节点.addEventListener('事件名',function(){},布尔值)
事件名
:不加on布尔值
:true监听捕获阶段,false监听冒泡阶段
var myform = document.getElementById('myform')
myform.addEventListener('click',function(){
console.log("click")
},true)
4.3、事件传播
- 事件传播的顺序:先从外到内,再从内到外。(捕获阶段 --> 冒泡阶段)
4.4、事件监听之间的区别
- DOM0级事件监听:只能监听冒泡阶段,如
onxxx
监听
<div id="box1">
<div id ="box2">
<div id="box3">
</div>
</div>
</div>
box1.onclick = function(){
console.log("这个是onclick box1")
}
box2.onclick = function(){
console.log("这个是onclick box2")
}
box3.onclick = function(){
console.log("这个是onclick box3 -- 先写的")
}
box3.onclick = function(){
console.log("这个是onclick box3 -- 后写的")
}
// 从最里层的box3开始冒泡
这个是onclick box3 -- 后写的
这个是onclick box2
这个是onclick box1
注意:当给元素设置两个或者多个相同的同名事件,DOM0级后写的事件会覆盖先写的
- DOM2级事件监听:可以监听捕获阶段和冒泡阶段,如
addEventListener()
监听
<div id="box1">
<div id ="box2">
<div id="box3">
</div>
</div>
</div>
box1.addEventListener('click',function(){
console.log("这个是click box1")
},true)
box2.addEventListener('click',function(){
console.log("这个是click box2")
},true)
box3.addEventListener('click',function(){
console.log("这个是click box3 -- 先写的")
},true)
box3.addEventListener('click',function(){
console.log("这个是click box3 -- 后写的")
},true)
// 从最外层的box1开始捕获
这个是onclick box1
这个是onclick box2
这个是click box3 -- 先写的
这个是click box3 -- 后写的
注意:当给元素设置两个或者多个相同的同名事件,DOM2级会按照顺序执行
4.5、事件对象
- 事件处理函数提供一个封装本次事件细节的对象参数,该对象称为事件对象
节点.onmousemove = function(e){}
4.5.1、鼠标位置
属性 | 属性描述 |
---|---|
|
|
| 相对于 |
| 相对于 |
| 相对于 |
| 相对于 |
| 相对于 |
| 鼠标滚动方向,下滚动为正值,上滚动为负值 |
- 演示例子:
4.5.2、charCode和keyCode属性
charCode属性
:通常用于onkeypress
事件中,表示用户输入字符的“字符码”
keyCode属性
:通常用于onkeydown
和onkeyup
属性中, 表示拥护按下按键的“键码”
charCode字符码:
字符 | 字符码 |
---|---|
|
|
|
|
|
|
keyCode字符码:
字符 | 字符码 |
---|---|
|
|
|
|
|
|
|
|
|
|
- 演示例子:
4.5.3、preventDefault()方法
- 阻止事件产生的默认动作 -- 如:“键盘输入a是不显示,a显示在输入框中为默认动作”
4.5.4、stopPropagation()方法
- 阻止事件后续传播 -- 如:“在捕获阶段,阻止内层的事件捕获,需要在外层调用该方法来进行事件传播阻止”
<div id="box1">
<div id ="box2">
</div>
</div>
box1.addEventListener('click',function(e){
e.stopPropagation()
console.log("这个是click box1")
},true)
box2.addEventListener('click',function(){
console.log("这个是click box2")
},true)
这个是onclick box1
4.6、事件委托
事件委托
是指:通过冒泡机制
,将目标元素事件委托给祖先元素(不仅仅指父元素)事件委托
通常结合e.target
属性使用 | 属性 | 属性描述 | | --- | --- | |
|target
事件的真正发出者
| |
|currentTarget
始终是监听事件者
|
事件委托使用的场景和优点
- 需要为大量元素添加事件监听时,减少事件监听内存开销
- 新元素需要上树并添加事件监听时
事件委托需要注意的事项
- 无
冒泡机制
的事件,不能使用事件委托 - 例如:
onmouseenter
天生不冒泡,onmouseover
冒泡,不可为onmouseenter
添加事件委托 - 最内层元素不能再有额外的内层元素,如下示例分析:
<ul>
// 这时这个`span`实际才是最内层元素,当点击`谁啊?`时,整个`li`变红,但点击`额外内层元素`标签时,只有`span`标签变红
<li><span>额外内层元素</span>谁啊?</li>
</ul>
4.7、定时器和延时器
定时器
:每固定间隔,重复调用函数延时器
:固定间隔后,只调用一次函数
4.7.1、定时器
setInterval(function(){},间隔时间)
// 每一秒打印一次 -- 我是鸡哥定时器
setInterval(function(){
console.log("我是鸡哥定时器")
},1000)
setInterval(function(){},间隔时间,参数...)
// 每一秒打印一次 -- 30
setInterval(function(a,b){
console.log(a+b)
},1000,10,20)
清除定时器
-->clearInterval(定时器变量)
// 不会执行打印
var timer = setInterval(function(){
console.log("我是鸡哥定时器")
},1000)
clearInterval(timer)
4.7.2、延时器
setTimeout(function(){},间隔时间)
// 每一秒打印一次 -- 我是鸡哥定时器
setTimeout(function(){
console.log("我是鸡哥定时器")
},1000)
清除延时器
-->clearTimeout(延时器变量)
// 不会执行打印
var timer = setTimeout(function(){
console.log("我是鸡哥定时器")
},1000)
clearTimeout(timer)
定时器与延时器操作实例: