优快云话题挑战赛第2期
参赛话题:学习笔记
Web APIs基础学习三
在上一章中主要学习了关于 事件监听 的处理问题,感兴趣的小伙伴可以回顾一下 Web前端系列技术之Web APIs基础(从基础开始)② 中所介绍的内容;
在学习完事件监听之后,使用事件监听处理对应的DOM对象是项目开发过程中实现用户交互必不可少的操作,例如 动态创建用户评论区 ;当然这里所要实现的功能就不仅仅是要使用到事件侦听操作了,还需要动态的创建实现评论区所用到的标签元素以及评论时间,那么这里就需要学到关于 ==DOM节点操作==以及 时间对象 的知识;
那么今天主要介绍的内容就是:
一、节点操作
二、时间对象
三、重绘与重排(回流)
四、评论区案例
文章目录
一、节点操作
基础理解: 此处所指的节点指的是 DOM节点;在之前学习到过 DOM树 ;那么结合着树的结构,可以知道在树中的每一个点位都被称为 节点 ,同样,这里的DOM树种所包含的每一个内容便都可以称之为 DOM节点,在对于这些节点的操作过程种,主要也就分为增、删、改、查四种操作方式,
一般情况下,增、删、查是用到最多的;
1. DOM节点
节点与节点之间也会有所不同,主要划分的节点类型为三类,当然也有其他的,因为项目开发的过程中并不常用,所以这里就不做过多的赘述了:
- 元素节点(重点): 所有的标签,比如
body
、div
等,其中html
属于根节点; - 属性节点: 所有的属性 比如 href、id等
- 文本节点: 所有的文本
- 其他
⭐注意:元素节点是一个极其重要的节点内容,它可以更好的让我们理清标签元素之间的关系;
2. 查找节点
所谓的查找节点操作,主要是依赖于节点之间的关系实现查找,在DOM节点的关系中,被划分成了三类:父节点、子节点 和 兄弟节点,以便于查找到节点内容;
2.1 父节点查找
基础语法结构:
子元素.parentNode
⭐注意:
parentNode
是属性,而非方法- 返回 最近一级的父节点 找不到返回为
null
2.2 子节点查查找
在子节点查找中,主要分为两种方法;
基础语法结构:
//方法一(了解);
父元素.childNodes
//方法二(重点):
父元素.children
⭐注意:
childNodes
获得所有子节点、包括文本节点(空格、换行)、注释节点等;children
仅获得所有元素节点,返回的还是一个伪数组(重点);
2.3. 兄弟节点查找
在兄弟节点查找中,主要分为两种情况:
基础语法结构:
//下一个兄弟节点
元素.nextElementSibling
//上一个兄弟节点
元素.previousElementSibling
⭐注意:
nextElementSibling
和previousElementSibling
都属于属性;
3. 增加节点
在项目的开发的过程中,很多功能都需要实现标签的动态添加,例如,评论区功能 ;不过,在实现增加节点的过程中,往往分为两步开发:
① 创建一个新的节点;
② 把创建的新的节点放入到指定的元素内部;
3.1 创建节点
基础概念: 即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点;
基础语法结构:
//属于一个方法
document.createElement('标签名')
⭐注意:这里需要写的是标签名,因为要创建的是标签
3.2 追加节点
基础概念: 如果需要在界面有所呈现,那就需要将创建好的标签插入到某个父元素中,这里又分为两种情况:
① 插入到父元素的最后一个子元素:
父元素.appendChild(要插入的元素)
② 插入到父元素中某个子元素的前面:
父元素.insertBefore(要插入的元素, 在哪个元素的前面)
⭐注意:此处该如何定义元素的位置呢?利用父查子生成的伪数组查询下标的方式插入;
父元素.children[i]
3.3 克隆节点
特殊情况下,新增节点也可以通过克隆的方式进行添加,主要操作如下:
- 复制一个原有的节点
- 把复制的节点放入到指定的元素内部
基础语法结构:
//克隆一个已有的元素节点
元素.cloneNode(布尔值)
⭐注意:
cloneNode
会克隆出一个跟原标签一样的元素,括号内传入布尔值:
- 若为true,则代表克隆时会包含后代节点一起克隆
- 若为false,则代表克隆时不包含后代节点
- 默认为false
4. 删除节点
若一个节点在页面中已不需要时,可以删除它,在JavaScript 原生DOM操作 中,要删除元素必须通过父元素删除;
基础语法结构:
父元素.removeChild(要删除的元素)
⭐注意:
- 如不存在父子关系则删除不成功;
- 删除节点 和 隐藏节点(
display:none
) 有区别的: 隐藏节点还是存在的,但是删除,则从html中删除节点;
二、时间对象
基础概念: 用来表示时间的对象,通常用来得到当前系统时间;但是在创建时间对象的时候,需要进行 实例化;
基本语法结构:
//获取当前时间
let date = new Date()
//获取指定时间
let date = new Date(2022-09-17)
效果如下:
⭐注意:在创建时间对象的时候,需要用到
new
关键字来进行实例化操作,这里涉及到了构造函数的用法,后期会详细介绍的
1. 时间对象方法
由于时间对象所返回的数据并不能直接被使用,所以需要转换为实际开发中常用的格式,具体如表所示:
方法 | 作用 | 说明 |
---|---|---|
getFullYear() | 获得年份 | 获取四位年份 |
getMonth() | 获得月份 | 取值为 0 ~ 11 |
getDate() | 获取月份中的每一天 | 不同月份取值也不相同 |
getDay() | 获取星期 | 取值为 0 ~ 6 |
getHours() | 获取小时 | 取值为 0 ~ 23 |
getMinutes() | 获取分钟 | 取值为 0 ~ 59 |
getSeconds() | 获取秒 | 取值为 0 ~ 59 |
代码样例如下:
//获取元素
let div = document.querySelector('div')
// 防止留白
getime()
//获取当前时间
function getime() {
let dt = new Date()
let year = dt.getFullYear()
let month = dt.getMonth()
let date = dt.getDate()
let hours = dt.getHours()
let minute = dt.getMinutes()
let second = dt.getSeconds()
let arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
let day = dt.getDay()
//补零
hours<10?hours+='0':hours
minute<10?minute+='0':minute
second<10?second+='0':second
// 设置内容
div.innerHTML = `现在的北京时间${year}年${month}月${date}日${hours}点${minute}分${second}秒 ${arr[day]}`
}
// 定时器
setInterval(getime, 1000)
具体效果:
⭐注意: 当然,在项目开发的过程中,显示时间的功能还是有很多需要注意的地方:
- 在获取月份的过程中,由于
getMonth()
的取值范围为0~11
,所以在开发的过程中,需要将该函数方法进行优化 =>getMonth()+1
- 页面所要呈现的时间是动态跳动的,所要需要将获取的时间对象写进定时器中,并设定每隔1s自动刷新一次
- 在调用定时器的时候,页面调用的第一秒会产生留白,那么主要原因的就是定时器延迟了一秒才开始运行,那么最好的解决方法就是根据程序的执行顺序,先去调用一次时间函数,再紧接着使用定时器
2. 时间戳
什么是时间戳?是指 1970年01月01日00时00分00秒起 至 现在 的 总毫秒数 ,它是一种特殊的计量时间的方式;
时间戳的作用:常用于编写倒计时功能;
获取时间戳的三种方式:
- 使用
getTime()
方法
//1. 实例化
let date = new Date()
//2. 获取时间戳
console.log(date.getTime())
- 简写
+new Date()
(更常用)
console.log(+new Date())
- 使用
Date.now()
console.log(Date.now())
⭐注意:
+new Date()
的使用方法中,前面的加号相当于一个 正号,用来转换成数字型的Date.now()
的使用方法中,无需实例化,但是只能得到当前的时间戳, 而前面两种是可以返回指定时间的时间戳
3. 时间转换公式(拓展)
因为通过时间戳得到是总毫秒数,所以要想呈现出正常的时间格式,就需要先转换为秒再计算;
- 总秒数 = 总毫秒数 / 1000
时间转换:
- 计算天数:d = parseInt(总秒数/ 60/60 /24);
- 计算小时:h = parseInt(总秒数/ 60/60 %24);
- 计算分数:m = parseInt(总秒数 /60 %60 );
- 计算当前秒数:s = parseInt(总秒数%60);
三、重绘与重排(回流)
在了解 重绘和重排(回流) 之前,需要先了解浏览器是如何进行界面渲染的,如下图所示:
- 解析(Parser)HTML,生成DOM树(DOM Tree) ;
- 同时解析(Parser) CSS,生成样式规则 (Style Rules);
- 根据DOM树和样式规则,生成渲染树(Render Tree);
- 进行布局 Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置,大小);
- 进行绘制 Painting(重绘):根据计算和获取的信息进行整个页面的绘制;
- Display: 展示在页面上;
1. 重绘
由于节点(元素)的 样式 的 改变 并 不影响 它在文档流中的 位置 和 文档布局 时(比如:color
、background-color
、outline
等), 称为 重绘。
2. 重排(回流)
当 Render Tree 中部分或者全部元素的 尺寸 、结构 、布局 等发生改变时,浏览器就会重新渲染部分或全部文档的过程称为 重排(回流)。
⭐注意:
- outline不会撑大盒子,所以设置outline是会导致重绘的;
- 重绘不一定引起回流,而回流一定会引起重绘
3. 场景举例
会导致回流(重排)的操作:
- 页面的首次刷新;
- 浏览器的窗口大小发生改变;
- 元素的大小或位置发生改变;
- 改变字体的大小;
- 内容的变化(如:input框的输入,图片的大小);
- 激活css伪类 (如::hover) 脚本操作DOM(添加或者删除可见的DOM元素);
简单理解:影响到布局了,就会有重排(回流)
四、评论区案例
具体代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>微博发布</title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.w {
width: 900px;
margin: 0 auto;
}
.controls textarea {
width: 878px;
height: 100px;
resize: none;
border-radius: 10px;
outline: none;
padding-left: 20px;
padding-top: 10px;
font-size: 18px;
}
.controls {
overflow: hidden;
}
.controls div {
float: right;
}
.controls div span {
color: #666;
}
.controls div .useCount {
color: red;
}
.controls div button {
width: 100px;
outline: none;
border: none;
background: rgb(0, 132, 255);
height: 30px;
cursor: pointer;
color: #fff;
font: bold 14px '宋体';
transition: all 0.5s;
}
.controls div button:hover {
background: rgb(0, 225, 255);
}
.controls div button:disabled {
background: rgba(0, 225, 255, 0.5);
}
.contentList {
margin-top: 50px;
}
.contentList li {
padding: 20px 0;
border-bottom: 1px dashed #ccc;
position: relative;
}
.contentList li .info {
position: relative;
}
.contentList li .info span {
position: absolute;
top: 15px;
left: 100px;
font: bold 16px '宋体';
}
.contentList li .info p {
position: absolute;
top: 40px;
left: 100px;
color: #aaa;
font-size: 12px;
}
.contentList img {
width: 80px;
border-radius: 50%;
}
.contentList li .content {
padding-left: 100px;
color: #666;
word-break: break-all;
}
.contentList li .the_del {
position: absolute;
right: 0;
top: 0;
font-size: 28px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="w">
<div class="controls">
<img src="./images/9.6/tip.png" alt="" /><br />
<!-- maxlength 可以用来限制表单输入的内容长度 -->
<textarea placeholder="说点什么吧..." id="area" cols="30" rows="10" maxlength="200"></textarea>
<div>
<span class="useCount" id="useCount">0</span>
<span>/</span>
<span>200</span>
<button id="send">发布</button>
</div>
</div>
<!-- 内容列表 -->
<div class="contentList">
<ul id="list"></ul>
</div>
</div>
<script>
// 模拟数据
let dataArr = [
{ uname: '司马懿', imgSrc: './images/9.5/01.jpg' },
{ uname: '女娲', imgSrc: './images/9.5/02.jpg' },
{ uname: '百里守约', imgSrc: './images/9.5/03.jpg' },
{ uname: '亚瑟', imgSrc: './images/9.5/04.jpg' },
{ uname: '虞姬', imgSrc: './images/9.5/05.jpg' },
{ uname: '张良', imgSrc: './images/9.5/06.jpg' },
{ uname: '安其拉', imgSrc: './images/9.5/07.jpg' },
{ uname: '李白', imgSrc: './images/9.5/08.jpg' },
{ uname: '阿珂', imgSrc: './images/9.5/09.jpg' },
{ uname: '墨子', imgSrc: './images/9.5/10.jpg' },
{ uname: '鲁班', imgSrc: './images/9.5/11.jpg' },
{ uname: '嬴政', imgSrc: './images/9.5/12.jpg' },
{ uname: '孙膑', imgSrc: './images/9.5/13.jpg' },
{ uname: '周瑜', imgSrc: './images/9.5/14.jpg' },
{ uname: '老夫子', imgSrc: './images/9.5/15.jpg' },
{ uname: '狄仁杰', imgSrc: './images/9.5/16.jpg' },
{ uname: '扁鹊', imgSrc: './images/9.5/17.jpg' },
{ uname: '马可波罗', imgSrc: './images/9.5/18.jpg' },
{ uname: '露娜', imgSrc: './images/9.5/19.jpg' },
{ uname: '孙悟空', imgSrc: './images/9.5/20.jpg' },
{ uname: '黄忠', imgSrc: './images/9.5/21.jpg' },
{ uname: '百里玄策', imgSrc: './images/9.5/22.jpg' },
]
// 需求1:检测用户输入字数
// 1. 注册input事件
// 2. 将文本的内容的长度赋值给对应的数值
// 3. 表单的maxlength属性可以直接限制在200个数之间
let textarea = document.querySelector('textarea')
let useCount = document.querySelector('.useCount')
// 发布按钮
let send = document.querySelector('#send')
let ul = document.querySelector('#list')
textarea.addEventListener('input', function () {
useCount.innerHTML = this.value.length
})
// 需求2: 输入不能为空
// 点击button之后判断
// 判断如果内容为空,则提示不能输入为空, 并且直接return 不能为空
// 防止输入无意义空格, 使用字符串.trim()去掉首尾空格
// 并将表单的value值设置为空字符串
// 同时下面红色为设置为0
send.addEventListener('click', function () {
if (textarea.value.trim() === '') {
textarea.value = ''
useCount.innerHTML = 0
return alert('内容不能为空')
}
// 随机数
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
let random = getRandom(0, dataArr.length - 1)
// 需求3: 新增留言 写到send 的里面
// 创建一个小li,然后里面通过innerHTML追加数据
let li = document.createElement('li')
// 随机获取数据数组里面的内容, 替换newNode的图片和名字以及留言内容
//img图片中,动态添加链接的时候,就不要写/>中的/了,因为会改变图片地址
li.innerHTML = `
<div class="info">
<img class="userpic" src=${dataArr[random].imgSrc}>
<span class="username">${dataArr[random].uname}</span>
<p class="send-time"> ${new Date().toLocaleString()} </p>
</div>
<div class="content">${textarea.value}</div>
<span class="the_del">X</span>`
// 需求4:删除留言 放到追加的前面
// 在事件处理函数里面获取点击按钮, 注册点击事件
// 必须在事件里面获取, 外面获取不到
// 删除对应的元素(通过this获取对应的那条需要删除的元素)
// 放到追加进ul的前面,这样创建元素的同时顺便绑定了事件,方便后续删除留言
// 使用 li.querySelector()
let del = li.querySelector('.the_del')
del.addEventListener('click', function () {
// 删除操作 点击的是X 删除的小li 父元素.removeChild(子元素)
ul.removeChild(li)
})
// 利用时间对象将时间动态化new Date().toLocaleString()
// 追加给 ul 用 父元素.insertBefore(子元素, 那个元素的前面)
ul.insertBefore(li, ul.children[0])
// 需求5:重置
// 将表单域内容重置为空
// 将userCount里面的内容重置为0
textarea.value = ''
useCount.innerHTML = 0
})
</script>
</body>
</html>
具体效果:
总结
今天是继续学习Web APIs的第三天,内容不多,但练习极为重要,今天所总结出来的所有知识,希望对大家有用,同时也希望这篇文章可以有一个好的展现量和得到更多人的支持,谢谢每一位浏览文章的人,要相信小柴码文,必是好文,欢迎各位 点赞+收藏+关注 啦! ! !
以上就是所要介绍的Web APIs基础学习的第三节内容,后续即将更新前端开发的学习目标。感谢关注和支持,让我们一起成长!
有兴趣可回顾一下JavaScript基础学习的文章内容,再结合之前所介绍的CSS基础学习以及HTML基础学习,大脑里的内容会更加丰富而充实的,毕竟综合性复习和学习是更会加深印象的哟!!!