DOM节点操作 重绘与回流
1. DOM节点
- DOM节点: DOM树里每一个内容都称之为节点
1. 节点类型:
- 元素节点 • 所有的标签 比如 body、 div • html 是根节点
- 属性节点 • 所有的属性 比如 href
- 文本节点 • 所有的文本
- 重点记住元素节点, 可以更好的让我们理清标签元素之间的关系
1. 查找节点
1.查找父节点:
- parentNode 属性, 返回最近一级的父节点 找不到返回为null
<div class="box">
<div class="box1">你好</div>
</div>
// 1. parentNode 查找父节点
let num = document.querySelector('.box1')
num.parentNode.style.display = 'none'
- 关闭二维码案例:
<div class="box">
<img src="images/1.png">
<i class="box1">x</i>
</div>
//点击关闭按钮, 关闭的是二维码的盒子, 还要获取erweima盒子
//点击关闭按钮, 直接关闭它的爸爸,就无需获取erweima元素了
let num = document.querySelector('.box1')
num.addEventListener('click', function () {
this.parentNode.style.display = 'none'
})
- 关闭多个二维码案例
<div class="box1">
<span class="box2"></span>
</div>
//多个二维码,点击谁,谁关闭
let num1 = document.querySelectorAll('.box2')
// 绑定多个事件给box3
for (let num2 = 0; num2 < num1.length; num2 ) {
num1[num2].addEventListener('click', function () {
// 关闭当前二维码 点击谁 就关闭父元素
this.parentNode.style.display = 'none'
})
}
2. 查找子节点
- childNodes: 获得所有子节点、包括文本节点(空格、换行)、注释节点等
- children[重点]: 仅获得所有元素节点, 返回的还是一个伪数组
// 1. 查找子节点 children 伪元素
let num1 = document.querySelector('button')
let num2 = document.querySelector('ul')
num1.addEventListener('click', function () {
for (let num3 = 0; num3 < num2.children.length; num3 ) {
num2.children[num3].style.color = 'green'
}
})
3. 兄弟关系查找
1. 下一个兄弟节点
- nextElementSibling 属性
let num1 = document.querySelector('.btn')
let num2 = document.querySelector('.two')
num1.addEventListener('click', function () {
// 1.查找下一个节点
num2.nextElementSibling.style.color = 'red'
2. 上一个兄弟节点
- previousElementSibling 属性
// 2 查找上一个节点
num2.previousElementSibling.style.color = 'pink'
2. 增加节点
1. 创建节点
- 即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点
let num2 = document.createElement('li')
num2.innerHTML = '我是创建新节点/追加节点'
2. 追加节点
- 要想在界面看到, 还得插入到某个父元素中
1. 插入到父元素的最后一个子元素:
代码语言:javascript复制let num1 = document.querySelector('ul')
num1.appendChild(num2)
2. 插入到父元素中某个子元素的前面:
代码语言:javascript复制num1.insertBefore(num2, num1.children[0])
3. 学成在线案例渲染
代码语言:javascript复制let data = [
{
src: 'images/course01.png',
title: 'Think PHP 5.0 博客系统实战项目演练',
num: 1125
}
let num2 = document.querySelector('ul')
for (let num1 = 0; num1 < data.length; num1 ) {
let num3 = document.createElement('li')
num3.innerHTML = `
<img src=${data[num1].src} alt="">
<h4>
${data[num1].title}
</h4>
<div class="info">
<span>高级</span> • <span> ${data[num1].num}</span>人在学习
</div>
`
num2.appendChild(num3)
}
3. 克隆节点
- cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值
- 若为true,则代表克隆时会包含后代节点一起克隆
- 若为false,则代表克隆时不包含后代节点, 默认为false
let num1 = document.querySelector('ul')
// 括号默认为false 则不克隆后代节点
// 若是true 则克隆后代
let num2 = num1.cloneNode(true)
document.body.appendChild(num2)
4. 删除节点
- 若一个节点在页面中已不需要时,可以删除它
- 在 JavaScript 原生DOM操作中,要删除元素必须通过父元素删除
- 如不存在父子关系则删除不成功
- 删除节点和隐藏节点(display:none) 有区别的: 隐藏节点还是存在的,但是删除,则从html中删除节点
let num3 = document.querySelector('button')
let num4 = document.querySelector('ol')
num3.addEventListener('click', function () {
// 语法: 父元素.removeChild(子元素)
num4.removeChild(num4.children[0])
})
3. 时间对象
- 时间对象:用来表示时间的对象, 作用:可以得到当前系统时间
1. 实例化
- 在代码中发现了 new 关键字时,一般将这个操作称为实例化
- 创建一个时间对象并获取时间
// 小括号为空 可获得当前时间
let num1 = new Date()
document.write(num1)
// 小括号写时间 可返回指定时间
let num2 = new Date('2023-3-31 00:00')
document.write(num2)
2. 时间对象方法
- 因为时间对象返回的数据我们不能直接使用,所以需要转换为实际开发中常用的格式
方法 | 作用 | 说明 |
---|---|---|
getFullYear() | 获得年份 | 获取四位年份 |
getMonth() | 获得月份 | 取值为0~11 |
getDate() | 获得月份中的每一天 | 不同月份取值也不同 |
getDay() | 获取星期 | 取值为0~6 |
getHours() | 获取小时 | 取值为0~23 |
getMinutes() | 获取分钟 | 取值为0~59 |
getSeconds() | 获取秒 | 取值为0~59 |
// 年月日
console.log(num1.getFullYear())
console.log(num1.getMonth() 1)
console.log(num1.getDate())
console.log(num1.getDay())
// 时分秒
console.log(num1.getHours())
console.log(num1.getMinutes())
console.log(num1.getSeconds())
页面显示时间:
代码语言:javascript复制// 1. 显示时间案例
fn() // 先调用函数 省去1秒空白期
setInterval(fn, 1000)
function fn() {
// 实例化时间对象 写到定时器里才行
let arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
let num3 = new Date()
let n1 = num3.getFullYear()
let n2 = num3.getMonth() 1
let n3 = num3.getDate()
let n4 = num3.getDay()
let n5 = num3.getHours()
let n6 = num3.getMinutes()
let n7 = num3.getSeconds()
let n8 = document.querySelector('.box')
n8.innerHTML = `今天是${n1}年${n2}月${n3}日 ${arr[n4]} ${n5}:${n6}:${n7}`
}
3. 时间戳
- 时间戳: 是指1970年1月1日0时0分0秒起至现在的毫秒数, 它是一种特殊的计量时间的方式
- 时间对象里面的方法转换实际所用
- 重点记住 new Date() 因为可以返回当前时间戳或者指定的时间戳
1. 三种方式获取时间戳:
代码语言:javascript复制// 无需实例化
// 但是只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳
// 利用将来时间减 现在时间戳 = 剩余时间毫秒
// 1.getTime() 方法
let num1 = new Date()
document.write(num1.getTime())
// 2.new Date()
document.write( new Date()) // 当前时间戳
document.write( new Date('2023-4-1 00:00:00')) // 指定时间
// 3.Date.now 只能得到当前
document.write(Date.now())
2. 毕业倒计时效果
代码语言:javascript复制fn()
setInterval(fn, 1000)
function fn() {
// 1.获取当前时间戳
let n1 = new Date()
// 2.获取指定时间戳
let n2 = new Date('2023-4-1 00:00:00')
// 3.计算剩余毫秒 / 1000 = 剩余秒数
let n3 = (n2 - n1) / 1000
// console.log(n3)
// 4.转换时分秒
let h = parseInt(n3 / 60 / 60 % 24)
h = h < 10 ? '0' h : h
let m = parseInt(n3 / 60 % 60)
m = m < 10 ? '0' m : m
let s = parseInt(n3 % 60)
s = s < 10 ? '0' s : s
// console.log(h, m, s)
let m1 = document.querySelector('#hour')
let m2 = document.querySelector('#minutes')
let m3 = document.querySelector('#scond')
m1.innerHTML = h
m2.innerHTML = m
m3.innerHTML = s
let m4 = document.querySelector('.next')
let m5 = document.querySelector('.tips')
let m6 = new Date()
let m7 = m6.getFullYear()
let m8 = m6.getMonth() 1
let m9 = m6.getDate()
let m10 = m6.getHours()
let m11 = m6.getMinutes()
let m12 = m6.getSeconds()
m4.innerHTML = `今天是${m7}年${m8}月${m9}日`
m5.innerHTML = `现在是${m10}:${m11}:${m12}`
}
4. 重绘和回流
浏览器是如何进行界面渲染的:
- 解析(Parser)HTML,生成DOM树(DOM Tree)
- 同时解析(Parser) CSS,生成样式规则 (Style Rules)
- 根据DOM树和样式规则,生成渲染树(Render Tree)
- 进行布局 Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置,大小)
- 进行绘制 Painting(重绘): 根据计算和获取的信息进行整个页面的绘制
- Display: 展示在页面上
1. 回流(重排)
- 当 Render Tree 中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染部分或全部文档的过 程称为 回流
2. 重绘
- 由于节点(元素)的样式的改变并不影响它在文档流中的位置和文档布局时(比如:color、background-color、 outline等), 称为重绘
3. 重绘不一定引起回流, 而回流一定会引起重绘
4. 重绘和回流(重排) 会导致回流的操作:
- 页面的首次刷新
- 浏览器的窗口大小发生改变
- 元素的大小或位置发生改变
- 改变字体的大小
- 内容的变化(如:input框的输入,图片的大小)
- 激活css伪类(如::hover)
- 脚本操作DOM(添加或者删除可见的DOM元素)
- 简单理解影响到布局了,就会有回流
let s = document.body.style
s.padding = '1px' // 重排 重绘
s.border = '1px solid red' // 重排 重绘
s.color = 'red' // 重绘
s.backgroundColor = '#000' // 重绘
s.fontSize = '14px' // 重排 重绘
5. 发布微博案例
代码语言:javascript复制let data = [
{ uname: '鲁班', imgSrc: './images/9.5/01.jpg' },
{ uname: '李白', imgSrc: './images/9.5/02.jpg' }
]
// 1. 检测用户输入字数
let text = document.querySelector('textarea')
let useCount = document.querySelector('.useCount')
let ul = document.querySelector('#list')
text.addEventListener('input', function () {
useCount.innerHTML = this.value.length
})
// 2. 发布内容不能为空
// 点击button后判断 内容为空 则提示不能输入为空 并直接return 不能为空
// 使用字符串.trim()去掉首尾空格
// 并将表单value值设置为空字符串 同时字数设置为0
let send = document.querySelector('#send')
send.addEventListener('click', function () {
if (text.value.trim() === '') {
text.value = ''
useCount.innerHTML = 0
return alert('内容不能为空')
}
// 4. 发布随机数
function fn(min, max) {
return Math.floor(Math.random() * (max - min min) min)
}
let fn1 = fn(0, data.length)
// 3. 新增留言
// 创建小li 通过innerHTML追加数据
// 随机获取数据数组内容 替换图片名字及留言内容
// 利用时间对象将时间动态化 new Date().toLocaleString()
let li = document.createElement('li')
li.innerHTML = `
<div class="info">
<img class="userpic" src=${data[fn1].imgSrc} />
<span class="username">${data[fn1].uname}</span>
<p class="send-time">发布于 ${new Date().toLocaleString()}</p>
</div>
<div class="content">${text.value}</div>
<span class="the_del">X</span>
`
// 5. 删除留言操作 放到追加的前面
// 在事件处理函数里获取点击按钮 注册点击事件
// 易错点: 必须在事件里获取 外面获取不到
// 放到追加ul的前面 创建元素同时顺便绑定了事件
let del = li.querySelector('.the_del')
del.addEventListener('click', function () {
ul.removeChild(li)
})
// 追加给ul 用父元素.insertBefore(子元素, 元素前面)
ul.insertBefore(li, ul.children[0])
// 6. 重置表单域内容为空
text.value = ''
useCount.innerHTML = 0
})
本节单词:
- parentNode
- children
- nextElementSibling
- previousElementSibling
- createElement
- appendChild
- insertBefore
- cloneNode
- removeChild
- new Date
- getTime
- now
- FullYear
- Month
- Day
- Hours
- Minutes
- Seconds
- trim
- toLocaleString