DOM(Document Object Model),最初是为XML设计。
节点属性
HTML标签成树形结构,在内存中以文档对象映射的形式存放:
<!DOCTYPE html>
<html land='zh-Hans'>
<head>
<meta>
<title>
</head>
<body>
</body>
</html>
- Node:Element(元素),Text(文本),Document(html标签,html是document的子节点),Comment(注释);
- 页面中的节点通过上面的构造函数创建出对象,提供一系列操作节点的API(DOM API)
- 其中Node也是派生自Object。
DOM API
一般是以下单词相互组合:
child / children / parent
node
first / last
next / previous
sibling / siblings
type
value / text / content
inner / outer
element
例如:
childNodes // 获取子节点包括标签和回车键(文本)
children // 获取子标签
firstElementChild // 第一个元素儿子
previousSibling // 上一个兄弟节点
previousElementSibling // 上一个元素兄弟节点
nodeName // body节点名称(注意除了svg是小写以外别的标签都是大写)
nodeType // 节点类型编号
textContent // 获取元素的文本内容(包括<script>、<style>等)
innerHTML // 修改HTML标签(不安全,比如用户注入<script>)
innerText // 获取元素文本内容(不包括<script>、<style>等,不包括display:none的,由于受CSS影响,会触发重排:如通过innerText修改节点属性导子节点丢失)
'textContent' in document.head ? document.body.textContent: document.body.innerText
...
关于DOM API,学习DocumentFragment优化
节点方法
如果一个属性是函数,那么这个属性也叫做方法(即函数属性),常用方法:
appendChild() // 添加子节点
cloneNode() // 克隆节点,默认是浅拷贝(true为深拷贝)
contains() // 是否包含系欸但
hasChildNodes() // 是否有子节点
insertBefore() // 在前面插入节点
isEqualNode() // 是否相等节点(元素完全一样)
isSameNode() // 是否相同节点,等价于“===”
removeChild() // 移除子节点(在内存中,不出现在页面)
replaceChild() // 替换子节点
normalize() // 常规化(如合并两个孩子的文本内容)
...
Document API
属性
body
characterSet
childElementCount
children
doctype
documentElement
domain
fullscreen
head
hidden
images
links
location
onxxxxxxxxx // 事件监听
origin
plugins
readyState
referrer
scripts
scrollingElement
styleSheets
title
visibilityState
方法
close
createDocumentFragment
createElement
createTextNode
execCommand // 执行交互命令(开发富文本编辑器)
exitFullscreen
getElementById
getElementsByClassName
getElementsByName
getElementsByTagName
getSelection
hasFocus
open
querySelector // 选择器(jQuery)
querySelectorAll // 选择器,返回伪数组(instance of Array)
registerElement
write
writeln
其中document.close在异步操作中很危险:
<script>
document.write(1)
document.write(2)
setTimeout(() ={document.write(3)}, 1000)
// 写入1、2后document已执行close
// 1s后重新执行open,write会覆盖之前写入的内容
</script>
Element API
...
事件模型
绑定事件的方式
<button id=X, onclick='print()'>A</button>
<!-- 或 -->
<button id=X, onclick='print.call()'>A</button>
<!-- onclick后面跟的是要执行的代码,不是js函数 -->
<script>
X.onclick = print // 指定函数(不是调用)
</script>
事件监听队列
<script>
// 第二次定义onclick,会把第一次覆盖
xxx.onclick = function({
concole.log('1')
})
xxx.onclick = function({
concole.log('2')
})
// 队列会按事件设置的顺序执行
f1 = function(){
concole.log('ok')
// xxx.removeEventListener('click', f1) // 执行一次即从队列中移除,只能执行一次
}
xxx.addEventListener('click', f1)
</script>
事件捕获和冒泡
<div>
<div id='grand'>
<div id='parent'>
<div id='son'>
</div>
</div>
</div>
</div>
<script>
grand.addEventListener('click', function f1(){
console.log('grand')
})
parent.addEventListener('click', function f2(){
console.log('parent')
})
son.addEventListener('click', function f3(){
console.log('son')
})
// 1、点击son所在区域,parent和grand都会触发事件
// 2、事件执行顺序默认为fn3,fn2,fn1(addEventListener的第三个参数默认为false,称为冒泡;设置true则逆序执行,称为捕获)
// 3、如果同一个元素上既定义了捕获也定义了冒泡,则按定义的顺序执行(不会覆盖)
</script>
点击别处关闭浮层
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{border: 1px solid red;}
.wrapper{position: relative;display: inline-block;}
.popover{border: 1px solid red;position: absolute;left: 100%;top: 0;white-space: nowrap;padding: 10px;margin-left: 10px;background: white;display: none;}
.popover::before{position: absolute; right: 100%;top: 5px;border: 10px solid transparent;border-right-color: red;content: '';}
.popover::after{position: absolute; right: 100%; top: 5px; border: 10px solid transparent; border-right-color: white; content: ''; margin-right: -1px;}
</style>
</head>
<body>
<div id="wrapper" class="wrapper">
<button id="clickMe">点我</button>
<div id="popover" class="popover">
<input type="checkbox">浮层
</div>
</div>
</body>
</html>
方法1:
<script>
clickMe.addEventListener('click', function(e){
console.log('show')
popover.style.display = 'block' // 点击按钮出现浮层
})
wrapper.addEventListener('click', function(e){
// 点击出现浮层的按钮时时事件会冒泡,最后触发了document的隐藏事件
// 需要在中间层添加停止传播,则事件会在外层div被阻隔
e.stopPropagation()
})
document.addEventListener('click', function(){
console.log('none')
popover.style.display = 'none' // 点击最外面隐藏浮层(注意不能设置body,body的高度取决于布局,没有元素的区域不生效)
})
</script>
方法2(使用jQuery):
<script>
$(clickMe).on('click', function() {
$(popover).show()
// 点击的时候这个事件会马上被加到处理队列中,这个过程优先于冒泡阶段
// 顺序:内层点击事件触发 -> 定义外层点击事件 -> 冒泡到外层 -> 外层点击事件触发
// $(document).one('click', function() {
// $(popover).hide()
// })
setTimeout(function() {
$(document).one('click', function() {
$(popover).hide()
})
}, 0) // 此处定义的document点击事件会在冒泡阶段结束后才生效
})
</script>
图解事件捕获和冒泡:
<html>
<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<meta charset="utf-8">
<title></title>
<style>
*{margin:0;padding:0;box-sizing:border-box;}
.red.active {background: red;}
.blue.active {background: blue;}
.green.active {background: green;}
.yellow.active {background: yellow;}
.orange.active {background: orange;}
.purple.active {background: purple;}
div {border: 1px solid black;padding: 20px;transition: all 0.5s;display: flex; flex: 1;border-radius: 50%;background: white;}
.red{width: 500px;height: 500px;}
</style>
</head>
<body>
<div class="red">
<div class="blue">
<div class="green">
<div class="yellow">
<div class="orange">
<div class="purple">
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let divs = $('div').get()
let n = 0
for (let i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', () => {
setTimeout(() => {
divs[i].classList.add('active')
}, n * 500) // 点击0.5s后添加active
n += 1
}, true) // 设置为true,表示从外层到内层,事件捕获
}
for (let i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', () => {
setTimeout(() => {
divs[i].classList.remove('active')
}, n * 500)
n += 1
})
}
</script>
</body>
</html>