一、瀑布流
1.效果图、代码、草稿
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>瀑布流</title>
<style>
* {
padding: 0;
margin: 0;
}
html {
width: 100%;
height: 100%;
}
#container {
width: 656px;
height: 798px;
margin: 0 auto;
/* background-color: aqua; */
position: relative;
}
#container div {
position: absolute;
width: 100px;
}
#container div img {
width: 100px;
}
</style>
</head>
<body>
<div id="container">
</div>
<script>
let data = [
'd1A.png',
'd2B.png',
'd3C.png',
'd4A.png',
'd5C.png',
'd1A.png',
]
waterFull(3, 6)
function waterFull(rowNum, colNum) {
let topNum1 = 14;
let topNum2 = 14;
let topNum3 = 14;
for (let i = 0; i < rowNum; i++) {
console.log('aaaaaa')
let leftNum = 8;
for (let n = 0; n < colNum; n++) {
let odiv = document.createElement('div')
let mainBox = document.getElementById('container')
mainBox.appendChild(odiv)
//这个塞路径的方式不确定是否可行
odiv.innerHTML = `<img src="${data[n]}" class='imgA'>`
//分别确定盒子高度,使其参差不平
let photoArr = document.getElementsByClassName('imgA')
if (n === 0 || n === 3 || n === 5) {
photoArr[n].style.height = 250 + 'px'
odiv.style.top = topNum1 + 'px'
} else if (n === 1) {
photoArr[n].style.height = 200 + 'px'
odiv.style.top = topNum2 + 'px'
} else if (n === 2 || n === 4) {
photoArr[n].style.height = 150 + 'px'
odiv.style.top = topNum3 + 'px'
}
//给盒子赋上左定位
odiv.style.left = leftNum + 'px'
leftNum += 110
}
topNum1 = topNum1 + 264
topNum2 = topNum2 + 214
topNum3 = topNum3 + 164
}
}
isLoading = false
let a = 3
window.onscroll = function () {
// console.log("1111")
var listHeight = container.offsetHeight
var listTop = container.offsetTop
// console.log(listHeight+listTop)
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop
var windowHeight = document.documentElement.clientHeight
// console.log()
if (isLoading) return
if ((listHeight + listTop) - Math.round(windowHeight + scrollTop) < 50) {
console.log("到底了")
isLoading = true
//渲染下一页数据
setTimeout(function () {
waterFull(a, 6)
isLoading = false //下一次到底事件继续触发
}, 800)
a += 3
console.log(a)
}
}
</script>
</body>
2.思路分析
(1)本次流程特点:先进行分析,直接根据分析写代码,最后改bug
(2)瀑布流
①先做第一行的图片,来确定container的宽高,子盒子的宽高和间距,为了做出瀑布流的效果,就必须有的图片长,有的图片短
②这种多行多列,而且有相同创建逻辑的,一般都是使用2个for循环创建的,外循环控制行数,内循环控制列数,为了便于下次创建不同行不同列的瀑布流,封装成了函数——这也给下方懒加载功能的实现创造了便利
③进一步思考如何做出瀑布流的布局样式——可以使用定位,通过改变每个图片的left和top来实现瀑布流的样式,同时找出left和top的变化规律,塞入循环之中
(3)懒加载
使用了b站视频中现成的代码。但是其中无限加载的功能需要进行替换,只要不断增加函数的行数就可以实现无限加载
3.难点分析
主要难点在于如何实现瀑布流图片参差不平的效果,有2点要素
①改变height ②改变top——因为height改变后需要再次调整top
我这里是用一个if语句判断,使不同列的图片有不同的高,每一行,每一列的top值都不一样,都在有规律的增加,要分别使用3个变量进行自增,而且是每次内循环结束后再自增,初始值必须放在外循环之外,如果在外循环内,那在末尾的变量自增将失效。
二、放大镜
1.效果图、代码、草稿
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>放大镜</title>
<style>
* {
margin: 0;
padding: 0;
}
#left {
width: 600px;
height: 400px;
background-color: antiquewhite;
margin-left: 20px;
margin-top: 40px;
float: left;
position: relative;
background-image: url(c.jpg);
background-size: cover;
overflow: hidden;
}
#mouseBox {
width: 150px;
height: 100px;
background-color: rgba(14, 182, 78, 0.3);
position: absolute;
pointer-events: none;
}
#right {
width: 600px;
height: 400px;
/* background-color: antiquewhite; */
margin-left: 20px;
margin-top: 40px;
float: left;
position: relative;
/* display: none; */
overflow: hidden;
}
#right img {
position: absolute;
}
</style>
</head>
<body>
<!-- 左边的原图片盒子 -->
<div id="left" style="width: 600px;
height: 400px;">
<div id="mouseBox"></div>
</div>
<div id="right">
<img src="c.jpg" id="img">
</div>
<script>
left.onmouseover = function () {
this.firstElementChild.style.display = "block"
right.style.display = 'block'
}
left.onmouseleave = function () {
this.firstElementChild.style.display = "none"
right.style.display = 'none'
}
//下面做放大镜功能
function bigger(multiple) {
//获取放大后的图片宽高
let currentWidth = left.clientWidth
let currentHeight = left.clientHeight
let biggerWidth = currentWidth * multiple
let biggerHeight = currentHeight * multiple
//把宽高赋给图片
// let biggerImg = document.querySelector('#right img')
// console.log(biggerImg)
// biggerImg.setAttribute('style',`width:${biggerWidth};height:${biggerHeight}`)
img.style.width = biggerWidth + 'px';
img.style.height = biggerHeight + 'px';
left.onmousemove = function (evt) {
this.firstElementChild.style.left = evt.offsetX + -75 + "px"
this.firstElementChild.style.top = evt.offsetY + -50 + "px"
img.style.left = -(evt.offsetX - 75) * multiple + "px"
img.style.top = -(evt.offsetY - 50) * multiple + "px"
}
}
bigger(10)
</script>
</body>
2.思路分析
(1)鼠标跟随(放大镜的镜)
通过evt.offsetX/Y来获取鼠标距离父元素的距离,然后对镜子和放大的图片进行相应的位移即可,当然,不要忘记给镜子添加 pointer-events: none; 不然会产生闪动
(2)放大(放大镜的放大)
通过left.clientWidth来获得照片的初始宽高,乘以相应倍数之后放进变量储存起来,核心是计算放大图片的left和top
三、动态表格
1.效果图、代码
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* 弹窗 (background) */
.modal {
display: none;
/* 默认隐藏 */
position: fixed;
/* z-index: 1; */
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
/* 弹窗内容 */
.modal-content {
position: relative;
background-color: #fefefe;
cursor: pointer;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 0;
border: 1px solid #888;
width: 30%;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
-webkit-animation-name: animatetop;
-webkit-animation-duration: 0.4s;
animation-name: animatetop;
animation-duration: 0.4s
}
/* 添加动画 */
@-webkit-keyframes animatetop {
from {
top: -300px;
opacity: 0
}
to {
top: 50%;
opacity: 1
}
}
@keyframes animatetop {
from {
top: -300px;
opacity: 0
}
to {
top: 50%;
opacity: 1
}
}
/* 关闭按钮 */
.close {
color: white;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
.modal-header {
padding: 2px 16px;
background-color: #5cb85c;
color: white;
}
.modal-body {
padding: 2px 16px;
text-align: center;
}
fieldset {
text-align: center;
width: 80%;
position: relative;
left: 50%;
margin-left: -40%;
}
.modal-body div {
width: 80%;
display: inline-block;
}
td {
text-align: center;
}
</style>
</head>
<body>
<!-- 打开弹窗按钮 -->
<button id="myBtn">创建新记录</button>
<!-- 弹窗 -->
<div id="myModal" class="modal">
<!-- 弹窗内容 -->
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
<center>
<h2>创建新记录</h2>
</center>
</div>
<div class="modal-body">
<div>
学号
<input id="schoolNum" value="">
</div>
<div>
姓名
<input id="name" value="">
</div>
<div>
性别
<input id="sex" value="">
</div>
<div>
二级学院
<input id="secondColl" value="">
</div>
<div>
班级
<input id="class" value="">
</div>
<div>
专业
<input id="major" value="">
</div>
<div>
辅导员
<input id="counsellor" value="">
</div>
<br>
<button id="create">创建</button>
</div>
</div>
</div>
<center>
<table id="table" border="1" width="80%" height="80%" cellspacing="0" cellpadding="0">
</table>
</center>
<script>
//创建弹窗
//————————————————————————————————————————————————————————————
// 获取弹窗
var modal = document.getElementById('myModal');
// 打开弹窗的按钮对象
var btn = document.getElementById("myBtn");
// 获取 <span> 元素,用于关闭弹窗 that closes the modal
var span = document.getElementsByClassName("close")[0];
// 点击按钮打开弹窗
btn.onclick = function () {
modal.style.display = "block";
}
// 点击 <span> (x), 关闭弹窗
span.onclick = function () {
modal.style.display = "none";
}
create.onclick = function () {
modal.style.display = "none";
}
//——————————————————————————————————————————————————————————————
//创建tr>创建td>创建数据数组>创建删除按钮>依次插入节点(创建表格)>设置单元格属性
//——————————————————————————————————————————————————————————————
let data = [
{
schoolNum: 23210501,
name: '曹媛媛',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210502,
name: '丁薇',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210503,
name: '马婧',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210504,
name: '孟嘉',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210505,
name: '倪嘉琪',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210506,
name: '',
sex: '',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210507,
name: '王晓冉',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210508,
name: '王璇',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210509,
name: '王璇',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
{
schoolNum: 23210510,
name: '朱梦涵',
sex: '女',
secondColl: '计算机工程学院',
class: 232105,
major: '计算机科学与技术',
counsellor: '冯雪冰',
},
]
//封装函数
function createTable(data) {
let arrNum = Object.keys(data).length;//获取对象数
let oTable = document.getElementById('table')//获取table标签
for (let i = 0; i < arrNum; i++) { //根据对象数确定行数
let otr = document.createElement('tr'); //创建行
oTable.appendChild(otr) //把行插入到table下
for (let item in data[0]) { //对象特有的 for..in遍历
let otd = document.createElement('td'); //创建列
otr.appendChild(otd) //把列插入到行内
otd.innerHTML = data[i][item]//把对象中的key的value插入到单元格中,同时确保第一行的数据对应数组中第一个对象的数据
}
let otd2 = document.createElement('td'); //因为for in是对象的key有几个就循环几次,所以不能利用其来插入最后的删除单元格
otr.appendChild(otd2); //把“删除”单元格插入到对应的行中
otd2.innerHTML = `<button id="delateA">
删除
</button>`//这里本来是想用lastChild.innerHTML来插入按钮标签的,但是一直出问题,所以用了最为方便的``直接暴力插入
if ((i % 2) != 0) { //用来设置未操作时的单元格背景颜色 ,之所以用!=0,因为i的初始值是0,意味着i为偶数时对应的才是奇数行
otr.setAttribute('style', 'background-color:black;color:white;')
}
}
}
createTable(data) //调用函数来进行初始单元格设置
//删除按钮属性
let allBtn = document.getElementsByTagName('button') //必须用getElementsBy...的方式来获取伪数组,querySelector和ById获得的都是第一个符合条件的标签
for (let i = 2; i < allBtn.length; i++) { //给每个开始就有的“删除”按钮设置“删除”事件,因为开始的[0][1]按钮不是删除作用,所以设置i的初始值为2
allBtn[i].onclick = handler
}
function handler() {
this.parentNode.parentNode.remove() // 把删除按钮的爷爷删掉,也就是删除其所在的tr,因为内容都是嵌套在tr中,所以一起消失
let trLength = document.getElementsByTagName('tr')//删除某行之后,可能会导致原来的奇偶顺序打乱,所以要重新设置背景颜色
console.log(trLength[1])
for (let i = 0; i < trLength.length; i++) {
if ((i % 2) != 0) {
trLength[i].setAttribute('style', 'background-color:black;color:white;')
} else { //给偶数行设置完颜色后,也要给奇数行设置(覆盖掉错误的颜色)
trLength[i].setAttribute('style', 'background-color:white;color:black;')
}
}
}
//添加按钮属性
create.onclick = getValue
function getValue() {
let getValueA = document.getElementsByTagName('input')//获取弹窗表单中的所有input标签并返回伪数组
let oTable = document.getElementById('table') //再次获得table标签,用于后面插入
let otr = document.createElement('tr');
for (let i = 0; i < getValueA.length; i++) {
oTable.appendChild(otr)
let otd = document.createElement('td');
otr.appendChild(otd)
otd.innerHTML = getValueA[i].value
}
let otd2 = document.createElement('td');
otr.appendChild(otd2);
otd2.innerHTML = `<button id="delateA">
删除
</button>`
let o = 1;
let allBtn = document.getElementsByTagName('button')
for (let i = 1+o; i < allBtn.length; i++) {
allBtn[i].onclick = handler
}
o++
let trLength = document.getElementsByTagName('tr')
console.log(trLength[1])
for (let i = 0; i < trLength.length; i++) {
console.log(i)
if ((i % 2) != 0) {
trLength[i].setAttribute('style', 'background-color:black;color:white;')
} else {
trLength[i].setAttribute('style', 'background-color:white;color:black;')
}
}
}
create.onmouseout = loseValue
function loseValue() {
let getValueA = document.getElementsByTagName('input')
for (let i = 0; i < getValueA.length; i++) {
getValueA[i].setAttribute('value', '')
}
}
</script>
</body>
2.思路分析
(1)弹窗
通过添加和删除display:none;来弹出弹窗,同时给弹窗的盒子设置z-index使在弹窗存在时,只能点击弹窗中的内容
(2)表格
①初始表格
将数据以对象的形式放入数组之中,创建一个单元格就读取相应的数据填入其中(详见注释)
②删除按钮
在外循环末尾创建一个单元格,再把按钮插入其中(直接用innerHTML暴力插入),点击删除按钮会直接删除它的爷爷(也就是其所在的tr),然后将该行全部删除
③添加表格
获取input标签的伪数组,用getValueA[i].value来获取弹窗输入框中中输入的信息,经过循环,逐一插入到新行的单元格中
④黑白背景
在初始表格创建时设置一次背景,在删除行的时候设置一次,在添加行的时候设置一次,每次设置都要给奇数行和偶数行分别设置
3.难点分析
①使用for...in循环(对象特有的循环)来将数组的对象中的数据插入到相应的单元格中
②之所以偶数行的设置要用!=0因为在循环中i的初始值是0,所以奇数时才代表的偶数行
四、滚动弹幕
1.效果图、代码
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>滚动弹幕</title>
<style>
* {
padding: 0;
margin: 0;
}
#container {
overflow: hidden;
width: 1000px;
height: 600px;
background-color: antiquewhite;
position: relative;
margin: 0 auto;
top: 100px;
}
#main {
width: 1000px;
height: 90%;
background-color: aqua;
}
#bottom {
width: 1000px;
height: 10%;
text-align: center;
}
#inputA {
width: 400px;
height: 40px;
margin-top: 8px;
font-size: 22px;
padding-left: 6px;
border-radius: 6px;
font-family: '楷体';
}
#buttonA {
width: 40px;
height: 42px;
position: relative;
left: -4px;
top: -3px;
border-radius: 6px;
}
#main div {
/* position: absolute; */
border: 2px solid #aaaaaa;
border-radius: 10px;
width: 400px;
padding-left: 10px;
position: absolute;
right: -412px;
/* 412 */
}
@keyframes move {
from {
right: 0;
}
to {
right: calc(100% + 200px);
}
}
</style>
</head>
<body>
<div id="container">
<div id="main">
<!-- <div id="line1">这也太好看了吧!!!</div>
<div id="line2">真的吗兄弟,我真觉得一般般吧</div>
<div id="line3">兄弟们,这个我是真喜欢</div>
<div id="line4">南邮通达潘子轩实名开导</div>
<div id="line5">阿巴阿巴</div>
<div id="line6">这里竟然还有通达的?</div> -->
</div>
<div id="bottom">
<input type="text" value="发个友善的弹幕见证当下" id="inputA">
<button id="buttonA">发送</button>
</div>
</div>
<script>
//输入框的小功能——点击清除&自动恢复
let clearText = document.getElementById("inputA")
clearText.onclick = function () {
clearText.setAttribute('value', '')
}
clearText.onmouseleave = function () {
let timer = setTimeout(function () {
clearText.setAttribute('value', '发个友善的弹幕见证当下')
}, 2000)
}
//发送建功能——获取文字>储存文字>创建div>插入节点>插入文字>添加属性>添加动画
buttonA.onclick = handler
function handler() {
//获取文字
let inputValue = clearText.value;
//console.log(inputValue);
//创建div
let valueDiv = document.createElement('div');
let mainDiv = document.getElementById('main');
//插入节点
mainDiv.appendChild(valueDiv);
//插入文字
valueDiv.innerHTML = inputValue;
//设置随机top值
let topValue = getRnd2(0, 400);
//console.log(topValue)
valueDiv.style.top = topValue + 'px'
//设置动画
valueDiv.style.animation = "move " + (5 + Math.random() * 5) + "s linear infinite";
}
function getRnd2(min, max) {
if (min > max) {
console.error("参数有误")
return
}
return Math.floor(Math.random() * (max - min + 1)) + min
}
//溢出清除盒子,降低内存负荷
</script>
</body>
2.思路分析
先布置出弹幕的基本样式,然后获取input输入框中输入的value,将其赋到新创建的div中,当确定按钮点击后,随机设置div的top值,并给其绑定从右至左的动画
五、三级联动
1.效果图、代码
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>三级下拉</title>
</head>
<body>
<form>
省:
<select id="sheng">
</select>
市:
<select id="shi">
</select>
区:
<select id="qu">
</select>
</form>
<script>
//先创建装有省市区数据的数组,之后要导入到相应的option中
//省
let dataS = [
'江苏省',
'福建省',
]
//市
let dataS1 = [
// '南京市',
// '无锡市',
// '徐州市',
// '常州市',
'苏州市',
// '南通市',
// '连云港市',
// '淮安市',
// '盐城市',
'扬州市',
// '镇江市',
// '泰州市',
// '宿迁市'
]
let dataS2 = [
'福州市',
'厦门市'
// '泉州市',
// '漳州市',
// '莆田市',
// '宁德市',
// '龙岩市',
// '三明市',
// '南平市'
]
//区
let dataJ1 = [
'姑苏区',
'虎丘区',
'吴中区',
'相城区',
'吴江区',
]
let dataJ2 = [
'邗江区',
'广陵区',
'江都区',
]
let dataF1 = [
'辖鼓楼区',
'台江区',
'仓山区',
'晋安区',
'马尾区',
'长乐区'
]
let dataF2 = [
'思明区',
'海沧区',
'湖里区',
'集美区',
'同安区',
'翔安区'
]
//封装一个导入数据的函数
function dataImport(data, id) {
for (let i = 0; i < data.length; i++) {
let oOption = document.createElement("option");
id.appendChild(oOption)
oOption.innerHTML = data[i];
}
}
//调用函数导入省的信息
dataImport(dataS, sheng)
dataImport(dataS1,shi)
dataImport(dataJ1,qu)
let os = document.getElementsByTagName('select')
let idNum = 0;
//封装一个函数:判断value并导入相应的市和区的数据
function judgeImporter(index, id, data1, data2, data3) {
if (os[index].selectedIndex === 0) {
dataImport(data1, id)
idNum = 1;
} else if (os[index].selectedIndex === 1) {
dataImport(data2, id)
idNum = 2;
}
}
function judgeImporter2(index, id, data1, data2, data3) {
if (os[index].selectedIndex === 0) {
dataImport(data1, id)
} else if (os[index].selectedIndex === 1) {
dataImport(data2, id)
}
}
//封装一个函数:用来删除讨人厌的之前的数据
function dataDelate(id) {
let el = document.getElementById(id);
let children1 = el.childNodes;
for (var i = children1.length - 1; i >= 0; i--) {
el.removeChild(children1[i])
}
}
let proSel = document.getElementById('sheng')
proSel.onchange = function () {
dataDelate("shi")
judgeImporter(0, shi, dataS1, dataS2)
}
let citSel = document.getElementById('shi')
citSel.onchange = function () {
dataDelate("qu")
if (idNum === 1) {
judgeImporter2(1, qu, dataJ1, dataJ2)
} else if (idNum === 2) {
judgeImporter2(1, qu, dataF1, dataF2)
}
}
</script>
</body>
2.思路分析
分别将省、市、区的数据放入数组之中方便调用,先对表单进行初始化——默认value(江苏省,苏州市,虎丘区),假如要的碰巧就是这个那么就不需要改动,但是不是就会点击第一个省选项或是第二个市选项,这时候对前2个表单做改变检测即可,如果点击第一个,就要对第二个市和第三个区选项进行一次清除,然后再进行匹配相应的市和区并填充到第二、三表单中,如果点击第二个,保留第一个表单中的选项,对第三个区选项进行一次清除,再进行匹配导入即可
六、登录界面
1.效果图、代码
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
padding: 0;
margin: 0;
}
body,html {
width: 100%;
height: 100%;
}
section {
width: 100%;
height: 100%;
background-color: antiquewhite;
position: relative;
}
#container {
width: 800px;
height: 400px;
background-color: white;
position: absolute;
left: 50%;
margin-left: -400px;
top:50%;
margin-top: -200px;
display: flex;
}
#photoBox {
width: 50%;
height: 80%;
padding: 40px 20px;
text-align: center;
position: relative;
background-color: aqua;
}
#signBox {
width: 50%;
height: 80%;
padding: 40px 20px;
position: relative;
background-color: aqua;
}
#photoBox img {
position: absolute;
top:80px;
left: 80px;
width:367.5px;
height: 225px;
}
#abab {
position: absolute;
top:80px;
right: 80px;
height: 205px;
border: 1px solid black;
border-radius: 5%;
padding-top: 20px;
background-color: aquamarine;
text-align: left;
padding-left: 16px;
padding-right: 16px;
}
#niCheng {
width: 167px;
}
#name {
width: 168px;
}
#num {
width: 158px;
}
#phNum {
width: 152px;
}
#niCheng {
width: 167px;
}
#niCheng {
width: 167px;
}
#key2 {
width: 136px;
}
</style>
</head>
<body>
<section>
<div id="container">
<div id="photoBox">
<img src="a3.jpg">
</div>
<div id="signBox">
<form id="abab">
<div id="niChengBox">
昵称
<input id="niCheng" type="text" value="n<=10" class="aa">
</div>
<div id="nameBox">
姓名
<input id="name" type="text" value="n<=4" class="aa">
</div>
<div id="numBox">
QQ号
<input id="num" type="text" value="5<n<10" class="aa">
</div>
<div id="phNumBox">
手机号
<input id="phNum" type="text" value="n=11" class="aa">
</div>
<div id="youXiangBox">
邮箱
<input id="youXiang" type="text" class="aa">
</div>
<div id="keyBox">
密码
<input id="key" type="text" class="aa" value="字母和数字,8<n<16">
</div>
<div id="keyBox2">
确认密码
<input id="key2" type="text" class="aa" value="字母和数字,8<n<16">
</div>
<button id="confirm1" class="aa">
确认
</button>
<button id="reset" class="aa">
重置
</button>
</form>
</div>
</div>
</section>
<script>
let round = document.getElementsByClassName("aa")
for (let i = 0; i < round.length; i++) {
round[i].onclick = function () {
round[i].setAttribute("value", " ")
}
}
reset.onclick = function () {
let sure2 = confirm('请确定是否重置')
if (sure2 === true) {
for (let i = 0; i < round.length; i++) {
console.log(round[i].value)
round[i].value = ""
}
alert('请重新输入')
}else {
alert('已取消')
}
}
function isValid(str) { return /^\w+$/.test(str); }
confirm1.onclick = function () {
let sure1 = confirm('请确定是否提交')
if (sure1 === true) {
let data = []
for (let n = 0; n < round.length; n++) {
data[n] = round[n].value.trim().length;
}
console.log(data)
if (data[0] > 1 && data[0] <= 10
&& data[1] > 1 && data[1] <= 4
&& data[2] > 5 && data[2] <= 10
&& data[3] === 11
&& data[5] > 8 && data[5] <= 15
&& data[6] > 8 && data[6] <= 15) {
alert('已经成功提交')
if (isValid(data[5]) == false) {
alert('密码请输入英文和数字的组合')
}
if (data[5] != data[6]) {
alert('请确认两次密码输入一致')
}
} else {
alert('输入无效,请重新输入')
}
}
}
</script>
</body>
2.流程分析
①先做一个简单的登录界面
②放置input表单和提交重置按钮
③效果1:输入框中有相应提示,点击后消失——初始有value,然后用循环把所有input都绑上点击事件,点击后把value设置为""即可
④效果2:提交按钮的条件判断——长度判断因为要判断的过多,先把去空长度放进数组中,再调用判断
是否由英文字母和数字组成用正则表达式判断,这里直接封装了一个简单的判断函数
⑤效果3:点击重置按钮后删除所有表单中信息,并重新展示提示信息
七、验证码
1.效果图、代码
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
section {
width: 100%;
height: 200px;
}
#container {
width: 250px;
height: 200px;
background-color: aquamarine;
position: relative;
top: 100px;
left: 600px;
padding-top: 200px;
padding-left: 20px;
padding-right: 20px;
}
</style>
</head>
<body>
<section>
<div id="container">
<input id="shuRuKuang" type="text" value="请输入验证码" name="aaa">
<br>
<button id="yanZheng">获得验证码</button>
<br>
<button id="anNiu">提交</button>
</div>
</section>
<script>
let num = false;
yanZheng.onclick = function () {
alert('验证码为:0505')
let timerId = setTimeout(function () {
yanZheng.removeAttribute('disabled', 'disabled')
anNiu.removeAttribute('disabled', 'disabled')
}, 60000);
if (num === false) {
console.log('我被禁用了')
yanZheng.setAttribute('disabled', 'disabled')
anNiu.setAttribute('disabled', 'disabled')
}
var countDownDate = new Date().getTime() + 60000;
var x = setInterval(function () {
var now = new Date().getTime(); // 获取当前时间的毫秒数
var distance = countDownDate - now; // 计算剩余时间的毫秒数
var seconds = Math.floor((distance % (1000 * 60)) / 1000); // 计算剩余秒数
console.log(seconds); // 在控制台输出剩余秒数
yanZheng.innerHTML = `${seconds}秒`
if (distance < 0) { // 如果倒计时结束
clearInterval(x); // 清除定时器
yanZheng.innerHTML = '获得验证码'
}
}, 1000);
}
anNiu.onclick = function () {
let code = document.getElementById('shuRuKuang').value
console.log(code)
if (code === '0505') {
alert('输入成功')
}
}
</script>
</body>
2.流程分析
“发送验证码”按钮:包含发送验证码提示,点击后禁用,禁用时显示一分钟倒计时
①用了绑定点击事件,点击后alert和添加属性disabled即可实现前两个功能
②倒计时
先获取当前时间的毫秒数用作计算(直接new Date()给出的是详细时间,放在间隔定时器后会每1秒获取一次进行计算,达到实时更新的效果),然后用反引号和innerHTML把剩余的秒数实时的插入到盒子中显示,最后如果剩余秒数为0就清除定时器并还原内容到”获得验证码“
八、返回顶部
1.效果图、代码
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
div {
width: 100%;
height: 800px;
/* background-color: aquamarine; */
}
#btn {
position: fixed;
color: rgb(40, 39, 38);
bottom: 0px;
right: 0px;
}
.block {
display: none;
}
</style>
</head>
<body>
<div id="line1"></div>
<button id="btn" class="block">返回顶部</button>
<script>
isLoading = false
window.onscroll = function () {
// console.log("1111")
var listHeight = line1.offsetHeight
var listTop = line1.offsetTop
// console.log(listHeight+listTop)
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop
var windowHeight = document.documentElement.clientHeight
// console.log(list.offsetTop,list.offsetHeight,windowHeight,scrollTop)
// console.log()
if (isLoading) return//解决距离底部[0,50]区间内重复触发的问题
if ((listHeight + listTop) - Math.round(windowHeight + scrollTop) < 50) {
console.log("到底了")
isLoading = true
btn.removeAttribute('class', 'block')
} else {
btn.setAttribute('class', 'block')
}
}
btn.onclick = function () {
let aa = document.documentElement.scrollTop
console.log(aa)
let timer = setInterval(() => {
// 获取当前滚动高度
const top = document.body.scrollTop || document.documentElement.scrollTop;
// 设置滚动速度
const speed = top / 10;
if (document.body.scrollTop !== 0) {
document.body.scrollTop -= speed;
} else {
document.documentElement.scrollTop -= speed;
}
if (top >= 0 && top <=2) {
clearInterval(timer)
}
console.log(top)
}, 16)
}
</script>
</body>
2.流程分析
(1)到底出现返回顶部按钮
借用懒加载的到底判断,并给按钮添加display:block显示
(2)由快到慢返回顶部
设置一个speed,每次/10,越来越小,然后获取滚动条滚动距离,间隔定时器循环一次就-speed,造成由快到慢的效果
九、电子时钟
1.效果图、代码
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>刻度时钟</title>
<style>
* {
padding: 0;
margin: 0;
}
html {
width: 100%;
height: 100%;
}
#container {
margin: 0 auto;
width: 1200px;
height: 200px;
position: relative;
}
ul {
list-style: none;
}
/* 刻度时钟 */
.time-clock {
width: 200px;
height: 200px;
border: 3px solid rgb(21, 121, 141);
border-radius: 50%;
margin: 50px;
position: relative;
top: 100px;
left: 50%;
margin-left: -100px;
}
/* 表盘 */
.time-clock ul {
width: 100%;
height: 100%;
position: relative;
}
.time-clock ul li {
width: 2px;
height: 4px;
background: rgb(21, 121, 141);
position: absolute;
left: 50%;
top: 0;
transform-origin: center 100px;
}
#hour {
width: 4px;
height: 25px;
background: rgb(8, 126, 139);
position: absolute;
left: 50%;
top: 125px;
margin: -50px 0 0 -3px;
transform-origin: center bottom;
}
#minute {
width: 3px;
height: 40px;
background: rgb(8, 126, 139);
position: absolute;
left: 50%;
top: 140px;
margin: -80px 0 0 -2px;
transform-origin: center bottom;
}
#second {
width: 2px;
height: 60px;
background: red;
position: absolute;
left: 50%;
top: 160px;
margin: -120px 0 0 -1px;
transform-origin: center bottom;
}
#ball {
width: 8px;
height: 8px;
background: black;
position: absolute;
left: 50%;
top: 50%;
border-radius: 50%;
transform: translate(-50%, -50%);
}
#box {
position: absolute;
top: 400px;
left: 50%;
margin-left: -60px;
font-size: 30px;
text-align: center;
border: 2px solid #aaaaaa;
background-color: #aaaaaab9;
}
</style>
</head>
<body>
<div id="container">
<div id="box"></div>
<div class="time-clock">
<ul>
</ul>
<div id="hour"></div>
<div id="minute"></div>
<div id="second"></div>
<div id="ball"></div>
</div>
<script>
//刻度时钟
let oul = document.querySelector(`ul`);
let hour = document.querySelector(`#hour`);
let minute = document.querySelector(`#minute`);
let second = document.querySelector(`#second`);
for (var i = 0; i < 60; i++) {
var oli = document.createElement(`li`);
oli.style.transform = `rotate(` + i * 6 + `deg)`;
if (i % 5 == 0) {
oli.style.height = `9px`
}
oul.appendChild(oli);
}
//定时器
function run() {
var date = new Date();
var currentHour = date.getHours();
var currentMinute = date.getMinutes();
var currentSecond = date.getSeconds();
hour.style.transform = `rotate(` + (currentHour * 30 + currentMinute / 2) + `deg)`
minute.style.transform = `rotate(` + currentMinute * 6 + `deg)`
second.style.transform = `rotate(` + currentSecond * 6 + `deg)`
}
run();
setInterval(run, 1000);
setInterval(function () {
let currentDate = new Date();
let currentHour = currentDate.getHours()
if (currentHour <= 9)
currentHour = "0" + currentHour;
let currentMinute = currentDate.getMinutes()
if (currentMinute <= 9)
currentMinute = "0" + currentMinute;
let currentSecond = currentDate.getSeconds()
if (currentSecond <= 9)
currentSecond = "0" + currentSecond;
box.innerHTML = `${currentHour}:${currentMinute}:${currentSecond}`
}, 1000)
</script>
</body>
2.流程分析
(1)数字时钟:数字时钟很好做,获取当前时间的时、分、秒,再用反引号插入到盒子中即可
(2)刻度时钟:先做一个大盒子放在最外面,用border-radius=40%画出表框,刻度有小时刻度和分钟刻度,总共60个,所以做一个60次的循环,逢5就赋上更大的height制造小时刻度,最后用旋转来做表针
十、选项卡
1.效果图、代码
<style>
*{
padding: 0;
margin: 0;
}
ul {
list-style: none;
}
.header {
display: flex;
width: 500px;
}
.header li {
flex: 1;
height: 50px;
line-height: 50px;
text-align: center;
border:1px solid black;
}
.box {
position: relative;
}
.box li {
position: absolute;
left: 360px;
top: 0;
width: 1000px;
height: 500px;
background-color: antiquewhite;
display: none;
}
.header .active {
background-color: aquamarine;
}
.box .active {
display:block;
}
#photobox{
position: relative;
width:490px;
height: 300px;
left:-50px;
top:40px;
}
#photobox img{
width:490px;
height: 300px;
}
#textbox {
position: relative;
top:80px;
left: -10px;
font-size: 16px;
font-family: '微软雅黑';
text-align: center;
}
</style>
</head>
<body>
<center>
<ul class="header">
<li class="active">英雄联盟</li>
<li>王者荣耀</li>
<li>明日方舟</li>
<li>APEX Legend</li>
</ul>
<ul class="box">
<li class="active">
<div id="photobox">
<img src="a1.jpeg">
</div>
<div id="textbox">
<span>
《英雄联盟》(League of Legends,简称LOL)是由美国拳头游戏(Riot Games)开发、中国内地由腾讯游戏
<br>
代理运营的英雄对战MOBA竞技网游。游戏里拥有数百个个性英雄,并拥有排位系统、符文系统等特色系统。
</span>
</div>
</li>
<li>
<div id="photobox">
<img src="a2.png">
</div>
<div id="textbox">
<span>
王者荣耀》是由腾讯游戏天美工作室群开发并运营在Android、IOS平台上的MOBA类国产手游,于2015年11月26日
<br>
在Android、iOS平台上正式公测,游戏曾经使用名称有《英雄战迹》、《王者联盟》。《王者荣耀》的外服版本为
<br>
《传说对决》(Arena Of Valor)。
</span>
</div>
</li>
<li>
<div id="photobox">
<img src="a3.jpg">
</div>
<div id="textbox">
<span >
《明日方舟》是一款魔物主题的策略手游。
<br>
在游戏中,玩家将管理一艘满载“ 魔物干员”的方舟,为调查来源神秘的矿石灾难而踏上旅途。
</span>
</div>
</li>
<li>
<div id="photobox">
<img src="a4.jpg">
</div>
<div id="textbox">
<span>
Apex Legends is a free-to-play hero shooter game
<br>
where legendary competitors battle for glory, fame, and fortune on the fringes of the Frontier.
</span>
</div>
</li>
</ul>
</center>
<script>
//获取header的所有li,box的所有li
let oHeaderItems = document.querySelectorAll(".header li")
let oBoxItems = document.querySelectorAll(".box li")
//给header的所有li绑事件
for (let i = 0; i < oHeaderItems.length; i++) {
//给每个li通过自定义属性添加索引,便于确定点击的是哪一个li
oHeaderItems[i].dataset.index = i ;
oHeaderItems[i].onclick = handler
}
console.log(oHeaderItems)
function handler () {
//无论点击哪个li都把所有的激活样式全部清空,等后面再重新赋上去就行
for (var m = 0; m <oHeaderItems.length; m++) {
oHeaderItems[m].classList.remove("active")
oBoxItems[m].classList.remove("active")
}
//通过this来获取当前点击的对象的index索引,并赋值给变量记录下来
let index = this.dataset.index;
console.log(index)
oHeaderItems[index].classList.add("active")
oBoxItems[index].classList.add("active")
}
</script>
</body>
2.流程分析
核心是利用z-index覆盖,点击选项卡的时候清除所有的active标签,给当前所在的选项卡和对应的内容页赋上active
十一、轮播图
1.效果图、代码
2.流程分析
(1)轮播主体:在间隔定时器中不停地给图片设置z-index覆盖,i不断增加,如果到头了就使i归0重新开始
(2)悬浮暂停:鼠标进入关闭定时器并使两个左右切换按钮上浮
(3)左右切换:点击后清除所有的图片上浮属性和小圆点的active属性,往左或往右赋上上浮并相应的给小圆点赋active
(4)小圆点导航:和选项卡逻辑一致
3.自我批评
(1)轮播主体开始有很多bug,比如到最后页面时无法切回到第一张再次开始轮播,再比如无法做到丝滑轮播效果,虽然积极解决问题但是后者的问题还是没有达到解决,现在处于一种能用但是用的不舒服的情况
(2)没有封装函数,就是根据脑中的思路硬写,然后找bug解决bug,流程还不够清晰明了,代码看起来也很乱,没有头绪
(3)注释内容过少,重新看甚至自己都会想不清楚是怎么写的
十二、随机点名
1.效果图、代码
2.流程分析
核心就是随机点名:先获取需要进行点名的人选,放入一个数组中,然后用Math.random()*data.length获得一个随机数,再用反引号插入盒子,整体放入一个定时器,不停获取,不停展示,达到滚动展示的效果
十三、总结
1.不足
(1)开始的demo页面不够美观
(2)代码不够简洁,不够语义化(注释少,封装函数少,变量名称过于随意)
(3)对JS主要常用代码不够熟悉,在前几个demo编写时想一个小功能就要回看一下笔记,浪费时间
(4)逻辑不够清晰,很少能快速分析出功能并给出解决方案
2.收获
(1)12次实战练习后对常用的代码——元素属性获取,标签获取,对象、数组的操作等代码更加熟悉
(2)分析问题能力增强:确定了个人分析流程——先分析功能,再以画图加笔记的形式给出主体的代码框架,然后根据草稿搭建主体框架,接着填充细小的,优化用户体验的功能,最后试运行找bug并解决bug
(3)总结了常见的找bug的办法:①控制台报红的英文内容推断 ②console.log输出提示字符或者变量进行类断点测试 ③查看元素,观察元素的标签、内容、属性是否被添加或者是添加错误 ④上网搜索寻求帮助和启发