css操作

文字居中显示:
text-align:center;

布局纵向排列变成左右排列(盒子居左居右):
display:flex;

DOM操作

神奇滤镜–DOM,display

勾选顶部复选框时,显示对应的滤镜面板;取消勾选时隐藏。多个面板叠加时,后选的面板显示在最上层
querySelector('[data-filter-name="X"]'):精确匹配特定属性值的元素
显示控制原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
filterTrigger.forEach((trigger) => {
// TODO:待补充代码
console.log(trigger);
trigger.addEventListener("click", () => {
const dataFilterName = trigger.getAttribute("data-filter-name");

filters.forEach((item) => {
if (item.getAttribute("data-filter-name") == dataFilterName) {
item.hidden=!item.hidden
updateZIndex(item)
}
});
});
});

图片

景深操作

元素的filter样式属性用于指定一个或多个滤镜效果,从而改变元素的外观。
blur():模糊元素。参数值表示模糊程度,单位为像素。
brightness():调整元素的亮度。参数值表示亮度的百分比。
contrast():调整元素的对比度。参数值表示对比度的百分比。
grayscale():将元素转换为灰度图像。参数值表示灰度的百分比。
hue-rotate():调整元素的色相。参数值表示旋转角度,单位为度数。
invert():反转元素的颜色。参数值表示反转的百分比。
opacity():调整元素的不透明度。参数值表示不透明度的百分比。
saturate():调整元素的饱和度。参数值表示饱和度的百分比。
sepia():将元素转换为褐色图像。参数值表示褐色的百分比。

1
2
document.querySelector(".img1").style.filter="blur(0px)"; 
document.querySelector(".img2").style.filter="blur(0px)";

动画

动一下的动画变得无限循环

animation-iteration-count: infinite;
解决:

1
2
3
animation: a3 0.8s steps(8);
变为
animation: a3 0.8s steps(8) infinite;

鼠标经过打开扇子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#box:hover div:first-child{
transform: rotate(-60deg);
}
/*上面这一行也可以是
#box:hover div:nth-child(1){
transform: rotate(-60deg);
}
*/
#box:hover div:nth-child(2){
transform: rotate(-50deg);
}
#box:hover div:nth-child(3){
transform: rotate(-40deg);
}
#box:hover div:nth-child(4){
transform: rotate(-30deg);
}
#box:hover div:nth-child(5){
transform: rotate(-20deg);
}
#box:hover div:nth-child(6){
transform: rotate(-10deg);
}
#box:hover div:nth-child(7){
transform: rotate(10deg);
}
#box:hover div:nth-child(8){
transform: rotate(20deg);
}
#box:hover div:nth-child(9){
transform: rotate(30deg);
}
#box:hover div:nth-child(10){
transform: rotate(40deg);
}
#box:hover div:nth-child(11){
transform: rotate(50deg);
}
#box:hover div:nth-child(12){
transform: rotate(60deg);
}

flex:

单个子项位置align-self
先后顺序order:3
主轴方向flex-direction
换不换行flex-wrap

元素周期表–var,cal计算

已知:元素周期表(class=table)中一共有 18 列(class=list)元素,请使用 flex布局,使元素周期表中的 18 列元素横向排列。请使用 var、calc 函数设置第2、13~17列元素(class=interval1)的 margin-top 为 1 个元素的高度,设置第3~12列元素(class=interval3)的 margin-top 为 3 个元素的高度。
一个元素的高度已定义变量如下:

1
2
3
:root {
--interval: 66px;
}

css 变量 以及 calc 计算 使用案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
:root {
--main-color: #3498db; /* 定义--main-color变量 */
--padding-size: 16px; /* 定义--padding-size变量 */
}

.button {
/*
使用定义的--main-color 变量 等同于 color:#3498db
*/
color: var(--main-color);
/*
使用calc计算出变量 --padding-size 的一半。计算结果为8px
*/
padding: calc(var(--padding-size) / 2);
}

作答:

1
2
3
4
5
6
7
8
9
10
11
12
.container .table {
display: flex;
}

.container .table .interval1 {
margin-top: var(--interval);
}

.container .table .interval3 {
margin-top: calc(var(--interval) * 3);
}

名片修改–居中练习

名片整体页面居中;名片左部分的各子项居中
练习要求

1
2
3
4
5
body,.user-card{
display: flex;
justify-content: center;
align-items: center;
}

蔬菜–控制每个子项位置

练习要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#box1,
#box2,
#box3{
display: flex;
}
#box1 {
justify-content: center;
align-items:center;
}
#box2 {
justify-content: space-between;
}
#box2 .item:nth-child(2) {
align-self: end;
}

#box3 {
justify-content: space-between;
}
#box3 .item:nth-child(2) {
align-self: center;
}
#box3 .item:nth-child(3) {
align-self: end;
}

grid

电影院排座位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>电影院排座位</title>
<link rel="stylesheet" href="./css/style.css" />
</head>
<body>
<div class="container">
<!-- 屏幕区域 -->
<div class="screen">阿凡达2</div>
<!-- 座位区域 -->
<div class="seat-area">
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
</div>
</div>
</body>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
* {
box-sizing: border-box;
}

body {
background-color: #242333;
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
}

.container {
perspective: 1000px;
width: 470px;
}

.screen {
background-color: #fff;
height: 70px;
width: 100%;
transform: rotateX(-45deg);
box-shadow: 0 3px 10px rgba(255, 255, 255, 0.7);
color: #242333;
text-align: center;
line-height: 70px;
font-size: 30px;
}

.seat {
background-color: #444451;
height: 40px;
width: 45px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}

/* TODO:待补充代码 */
.seat-area {
margin-top: 50px;
display: grid;
gap: 10px;
grid-template-columns: repeat(2, 45px 65px 45px 45px);
}
/*或者下面的*/
.seat-area{
margin-top:50px;
display: flex;
flex-wrap: wrap;
gap:10px;
}
.seat-area .seat:nth-of-type(8n+2){
margin-right:20px
}
.seat-area .seat:nth-of-type(8n+6){
margin-right:20px
}

页面

页面表单

页面整体布局,要求如图
练习要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.content-container{
margin-top: 70px;
}
.content{
height: 600px;
width: 450px;
background-color: rgba(0, 0, 0, .45);
margin: 0 auto;
border-radius: 10px;
text-align: center;
}
.content img{
width: 200px;
height: 200px;
border-radius: 50%;
margin-top: -20%;
}
.content h2{
font-size: 45px;
font-weight: 800;
margin-bottom: 40px;
}
.content button{
width: 80px;
height: 30px;
border-color: #041c32;
background-color: #2d4263;
color: white;
margin-top: 15px;
}
.content a{
text-decoration: none;
color: white;
}
.text{
margin-top: 15px;
}
.content input{
text-align: center;
width: 300px;
height: 40px;
font-size: 20px;
border-radius: 5px;
margin: 10px;
}

js操作

数组

提取对象数组的Key返回一个数组并去重

请在 TODO 2 处补全代码,实现函数 extractUniquePoints 的封装,该函数接收一个二维数组 data 作为参数(即为目标 1 中获取到的 rawData 变量的值),并把二维数组 data 中的 points 属性值提取出来并去重,最终存储在一个一维数组中返回。最终返回的数据结构如下:
["DOM 操作","ElementPlus","Vue.js"]
两种解答方法:

1
2
3
4
5
6
7
8
9
10
11
12
function extractUniquePoints(data) {
// TODO 2 请在下面补充代码
const arr2=[]
data.forEach(item=>{
for(const key of item.points){
arr2.push(key)
}
})
return [...new Set(arr2)]
// TODO 2 END
}

第二种,用flat(),但是这个我不是很懂,这样的话怎么就只取key了呢,value不会也进去吗

1
2
3
4
5
function extractUniquePoints(data) {
// TODO 2 请在下面补充代码
return [...new Set(data.flat(1).map(item=>item.points).flat(1))]
// TODO 2 END
}

知识点

  • 数组升序arr.sort((a, b) => a - b);
  • 数组降序arr.sort((a, b) => b - a);

    为什么要用比较函数?
    若数组是 [10, 2],默认排序会得到 [10, 2](因为字符串 ‘10’ 的 Unicode 码点比 ‘2’ 小),但比较函数 a - b 会正确排序为 [2, 10]
    arr.slice(start, end):截取从 start 到 end - 1 的元素(不包含 end)
    result.push(arr.slice(i, i + sliceNum));push() 方法:将子数组添加到 result 的末尾

在编程中,cb 是 回调函数(Callback Function) 的常见缩写。它是一个函数,作为参数传递给另一个函数,并在某个特定条件满足时被调用

  • 将对象转化成数组Object.entries(params)
    1
    2
    const params = { name: 'Alice', age: 25 };
    Object.entries(params) // → [['name', 'Alice'], ['age', 25]]

小狼人–重写filter回调函数

1
2
3
4
5
6
7
8
Array.prototype.myarray = function (cb) {
// TODO:待补充代码
const newArr = []
for (const value of this) {
if(cb(value)) newArr.push(value)
}
return newArr
};
1
2
3
4
5
6
7
8
9
10
11
12
13
// 在 myarray.js 文件中实现
Array.prototype.myarray = function (cb) {
const result = []; // 存储符合条件的元素
// 遍历数组的每个元素
for (let i = 0; i < this.length; i++) {
const currentElement = this[i];
// 调用回调函数,传入元素、索引、原数组
if (cb(currentElement, i, this)) {
result.push(currentElement);
}
}
return result;
};

this 的指向:在 Array.prototype.myarray 中,this 指向调用该方法的数组(如 cardList)。
回调函数参数:filter 的回调函数接受三个参数:(element, index, array),需保持一致性

更好的解法:

1
2
3
4
5
6
7
8
9
10
11
12
// 返回条件为真的新数组
Array.prototype.myarray = function (cb) {
// TODO:待补充代码
let result = [];
this.forEach(item => {
if (cb(item)) {
result.push(item);
}
});
return result
};

解密数组–回溯函数

从1到max中选择count个不同的数,并按升序排列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function getPossiblePasswords(max, count) {
const result = [];
if (count === 0 || max < count) return result; // 处理无法生成组合的情况

function backtrack(start, path) {
if (path.length === count) {
result.push([...path]); // 将当前组合存入结果
return;
}
// 从当前数字的下一个开始选择,避免重复
for (let i = start; i <= max; i++) {
path.push(i); // 添加当前数字到路径
backtrack(i + 1, path); // 递归选择下一个更大的数字
path.pop(); // 回溯,移除最后一个数字以尝试其他可能性
}
}

backtrack(1, []); // 从数字1开始递归
//console.log(result);
return result;
}

分割数组

一个数组,将它分割成指定长度的若干份;升序排序

具体要求:
将待分割的(一维)数组升序排序。
将排序后的数组从下标为 0 的元素开始,按照从 id=sliceNum 的输入框中获取到的数值去分割,并将分割好的数据存入一个新数组中。如:输入框中值为 n,将原数组按每 n 个一组分割,不足 n 个的数据为一组。
将得到的新数组返回(即 return 一个二维数组)

1
2
3
4
const splitArray = (oldArr, num) => {
// TODO:请补充代码实现功能
};
modul

解答

1
2
3
4
5
6
7
8
9
10
11
const splitArray = (oldArr, num) => {
// 步骤1:排序数组
oldArr.sort((a, b) => a - b);

const result = [];
for (let i = 0; i < oldArr.length; i += num) {
// 步骤2:每次截取 num 个元素(不足则取剩余所有)
result.push(oldArr.slice(i, i + num));
}
return result;
};

垃圾分类–对象数组分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const waste = ref([{ type: "厨余垃圾", txt: "剩菜剩饭", dis: [10, 30, 100, 33] },
{ type: "厨余垃圾", txt: "骨头", dis: [185, 28, 50, 33] },
{ type: "厨余垃圾", txt: "菜根菜叶", dis: [550, 169, 100, 33] },
{ type: "可回收垃圾", txt: "废纸", dis: [20, 100, 50, 33] },
{ type: "可回收垃圾", txt: "塑料", dis: [8, 170, 50, 33] },
{ type: "可回收垃圾", txt: "玻璃", dis: [155, 140, 50, 33] },
{ type: "可回收垃圾", txt: "金属", dis: [400, 150, 50, 33] },
{ type: "可回收垃圾", txt: "布料", dis: [105, 60, 50, 33] },
{ type: "其他垃圾", txt: "砖瓦陶瓷", dis: [520, 120, 100, 33] },
{ type: "其他垃圾", txt: "渣土", dis: [600, 30, 50, 33] },
{ type: "其他垃圾", txt: "卫生间废纸", dis: [185, 100, 125, 33] },
{ type: "有害垃圾", txt: "废电池", dis: [350, 100, 75, 33] },
{ type: "有害垃圾", txt: "废日光灯管", dis: [500, 60, 125, 33] },
{ type: "有害垃圾", txt: "废水银温度计", dis: [305, 35, 150, 33] },
{ type: "有害垃圾", txt: "过期药品", dis: [255, 167, 100, 33] },
]);

let food_waste = ref([]); //厨余垃圾
let recyclable_waste = ref([]); //可回收垃圾
let other_waste = ref([]); //其他垃圾
let harmful_waste = ref([]); //有害垃圾
window.food_waste = food_waste; //厨余垃圾

waste.value 是一个数组,其中每个元素是一个对象,包含以下属性:type,txt,dis。

1
2
3
waste.value.forEach(res => {
// 在这里处理每个垃圾对象 res
});

forEach 是 JavaScript 中用于遍历数组的方法。
res 是当前遍历到的数组元素(即一个垃圾对象)。
forEach 不会返回新数组,它只是对数组中的每个元素执行回调函数。

let food_waste = ref([]);
ref([]) 是 Vue 3 中定义响应式数据的方式,表示一个初始值为空数组的响应式变量。
food_waste.value 是实际存储数据的地方。由于 ref 返回的是一个对象,必须通过 .value 访问或修改数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const garbagesorting = () => {
// TODO:待补充代码
console.log(waste.value);
waste.value.forEach(res=>{
if (res.type=="厨余垃圾") {
food_waste.value.push(res.txt)
}
if (res.type=="可回收垃圾") {
recyclable_waste.value.push(res.txt)
}
if (res.type=="其他垃圾") {
other_waste.value.push(res.txt)
}
if (res.type=="有害垃圾") {
harmful_waste.value.push(res.txt)
}
})
};

帛书碎片–数组去重

1
2
3
4
5
6
7
8
9
function collectPuzzle(...puzzles) {
const newPuz = [];
for (const k of puzzles) {
k.forEach(item => {
if (!newPuz.includes(item)) newPuz.push(item);
});
}
return newPuz;
}

我的作答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function collectPuzzle(...puzzles) {
// TODO:在这里写入具体的实现逻辑
// 对所有的拼图进行收集,获取不同拼图类型的结果,并返回
console.log(...puzzles)
const res=[]
for(const k of puzzles){
//console.log(k)
k.forEach(element => {
if(!res.includes(element)){
res.push(element)
}
});
}
//console.log(res)
return res
}

偏门作答(学习新知识)

flat给数组降维,默认是flat(1)就是降维一次,本来传的是个二维数组变成一维数组了,然后Set可以自动去重再生成一个去重后的新数组

1
return [...new Set(puzzles.flat())]

字符串拼接

分享链接–对象转数组+url拼接

思路是把对象转化成指定要求的数组拼接起来,两种方法:

  1. 第一种,使用Object.keys(对象)
    返回对象的key组成的数组arr1
    再用arr1.map()遍历key,返回要的格式

  2. 第二种,使用Object.entries(对象)
    返回对象的key和value组成的数组arr2
    再用arr2.map()遍历key和value,返回要的格式
    拼接字符串也是两种方法:

  3. 模板字符串
    `${key}=${params[key]}`

  4. 直接拼
    key+"="+params[key]

第一种方法代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function appendParamsToURL(url, params) {
//console.log("params", params)
//console.log("Object.keys(params)", Object.keys(params))
const nparams = Object.keys(params).map((p)=>`${p}=${params[p]}`).join("&")
//也可以var arr = Object.keys(params).map((p)=>p+"="+params[p]).join("&")
//console.log("map", nparams)
if(url.includes("?")){
return `${url}&${nparams}`}
else {
return `${url}?${nparams}`
}
//或者 if (url.includes("?")) return url + "&" + params;
// return url + "?" + params;
//再或者
//return url.includes('?')
// ? `${url}&${queryString}`
// : `${url}?${queryString}`;
}

第二种Object.entries()方法代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function appendParamsToURL(url, params) {
var nparams = Object.entries(params)
console.log(nparams)
var arr =nparams.map(([key,value])=>key+"="+value).join("&")

console.log(arr)
if(url.includes("?")){
string = url+"&"+arr
return string
}
else{
string = url+"?"+arr
return string
}
//下面这样写更好
//string = url.includes("?") ? url+"&"+arr :url+"?"+arr
// return string
}

将对象转为查询字符串

params = Object.keys(params).map((p) => `${p}=${params[p]}`).join("&");

功能:将 params 对象转换为 URL 查询字符串(如 name=Bob&age=25)。
关键步骤分解:
Object.keys(params):
获取 params 对象的所有键(属性名),形成一个数组。
例如:{ name: ‘Bob’, age: 25 } → [‘name’, ‘age’]。
map((p) => …):
遍历键数组中的每个键 p(如 ‘name’)。
将键和对应的值拼接为 key=value 的字符串。
例如:p = ‘name’ → params[p] 是 ‘Bob’ → 结果为 “name=Bob”。
.join(‘&’):
将所有 key=value 字符串用 & 连接,形成最终的查询字符串。
例如:[“name=Bob”, “age=25”] → “name=Bob&age=25”

水印生成–innerHtml拼接元素

两种解答方式

1
2
3
4
5
6
7
8
9
10
11
12
13
for(let i = 0; i < count; i++) {
let Aspan = document.createElement('span');
Aspan.innerHTML = text;
Aspan.style.color = color;
Aspan.style.opacity = opacity;
Aspan.style.transform = `rotate(${deg}deg)`;
container.appendChild(Aspan);
}
//或者
for(var i=0;i<count;i++){
container.innerHTML+= `<span style="color:${color};transform:rotate(${deg}deg);opacity:${opacity}">${text}</span>`
}

方法一:
创建元素:通过 document.createElement(‘span’) 创建一个新的 <span> 元素。
设置属性:使用 .innerHTML 来设置 <span> 的文本内容,.style 属性来逐个设置样式(颜色、透明度、旋转角度)。
追加到DOM:使用 .appendChild() 方法将新创建的 <span> 元素添加到指定的容器(container)中
方法二:
字符串拼接:在循环内部,构建一个包含所需<span>元素及其样式的字符串。
追加HTML:使用 += 操作符将这个字符串追加到 container.innerHTML 中,这意味着每次迭代都会修改容器的HTML内容

欢迎语拼接

注意第二种获取内容.value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//第一种
function generate() {
subject = document.getElementById("subject").value;
event1 = document.getElementById("event1").value;
event2 = document.getElementById("event2").value;
if (subject.length==0 || event1.length==0 || event2.length==0){
return;
}
result = "欢迎用户"+subject+"在"+event2+"学习"+event1+"课程!";
document.getElementById("result").value = result;
}

//第二种
function generate() {
subject = document.getElementById("subject");
event1 = document.getElementById("event1");
event2 = document.getElementById("event2");
if (subject.length==0 || event1.length==0 || event2.length==0){
return;
}
result = `欢迎用户${subject.value}${event2.value}学习${event1.value}课程!`;
document.getElementById("result").value = result;
}

正则表达式

添加千分位

原代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<!-- 控制浏览器缓存 -->
<meta http-equiv="Cache-Control" content="no-store" />
<title>你能看出有多少位吗?</title>
<link rel="stylesheet" href="https://labfile.oss.aliyuncs.com/courses/9203/style05.css">
</head>
<body>
<div class="wrap">
<form action="" class="edit-cash">
<p>消费总额</p>
<div class="shuru">
<span>&yen;</span>
<div id="input-box"></div>
</div>
<p>可询问工作人员应缴费用总额</p>
</form>
<input type="submit" value="支付" class="submit" />
</div>
<div class="layer"></div>
<div class="form">
<form action="" method="post">
<input type="text" placeholder="姓名" />
<input type="text" placeholder="联系电话" />
<input type="submit" value="提交" class="infor-sub" />
</form>
</div>
<div class="layer-content">
<div class="form-edit clearfix">
<div class="num num1">1</div>
<div class="num num2">2</div>
<div class="num num3">3</div>
<div class="num num4">4</div>
<div class="num num5">5</div>
<div class="num num6">6</div>
<div class="num num7">7</div>
<div class="num num8">8</div>
<div class="num num9">9</div>
<div class="num num0">0</div>
<div id="remove">删除</div>
</div>
</div>
<script src="https://labfile.oss.aliyuncs.com/courses/9203/jquery.min.js"></script>
<script>
$(function(){

$('.infor-sub').click(function(e){
$('.layer').hide();
$('.form').hide();
e.preventDefault(); //阻止表单提交
})

$('.shuru').click(function(e){
$('.layer-content').animate({
bottom: 0
}, 200)
e.stopPropagation();
})
$('.wrap').click(function(){
$('.layer-content').animate({
bottom: '-200px'
}, 200)
})

$('.form-edit .num').click(function(){
var oDiv = document.getElementById("input-box");
oDiv.innerHTML += this.innerHTML;
})
$('#remove').click(function(){
var oDiv = document.getElementById("input-box");
var oDivHtml = oDiv.innerHTML;
oDiv.innerHTML = oDivHtml.substring(0,oDivHtml.length-1);
})
})
</script>
</body>
</html>

作答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
/>
<!-- 控制浏览器缓存 -->
<meta http-equiv="Cache-Control" content="no-store" />
<title>你能看出有多少位吗?</title>
<link
rel="stylesheet"
href="https://labfile.oss.aliyuncs.com/courses/9203/style05.css"
/>
</head>
<body>
<div class="wrap">
<form action="" class="edit-cash">
<p>消费总额</p>
<div class="shuru">
<span>&yen;</span>
<div id="input-box"></div>
</div>
<p>可询问工作人员应缴费用总额</p>
</form>
<input type="submit" value="支付" class="submit" />
</div>
<div class="layer"></div>
<div class="form">
<form action="" method="post">
<input type="text" placeholder="姓名" />
<input type="text" placeholder="联系电话" />
<input type="submit" value="提交" class="infor-sub" />
</form>
</div>
<div class="layer-content">
<div class="form-edit clearfix">
<div class="num num1">1</div>
<div class="num num2">2</div>
<div class="num num3">3</div>
<div class="num num4">4</div>
<div class="num num5">5</div>
<div class="num num6">6</div>
<div class="num num7">7</div>
<div class="num num8">8</div>
<div class="num num9">9</div>
<div class="num num0">0</div>
<div id="remove">删除</div>
</div>
</div>
<script src="https://labfile.oss.aliyuncs.com/courses/9203/jquery.min.js"></script>
<script>
$(function () {
$(".infor-sub").click(function (e) {
$(".layer").hide();
$(".form").hide();
e.preventDefault(); //阻止表单提交
});

$(".shuru").click(function (e) {
$(".layer-content").animate(
{
bottom: 0,
},
200
);
e.stopPropagation();
});
$(".wrap").click(function () {
$(".layer-content").animate(
{
bottom: "-200px",
},
200
);
});
//修改部分
let num = ""; // 存储原始数字(不带逗号)

// 数字输入处理
$(".form-edit .num").click(function () {
num += this.innerHTML;
updateDisplay();
});

// 删除处理
$("#remove").click(function () {
if (num.length > 0) {
num = num.slice(0, -1);
updateDisplay();
}
});

// 统一的显示更新函数
function updateDisplay() {
const formatted = formatNumber(num);
$("#input-box").text(formatted);
}

// 优化的千分位格式化函数
function formatNumber(str) {
return str
.replace(/\D/g, "") // 去除非数字
.replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
}
});
</script>
</body>
</html>

讲解

num = num.slice(0, -1);始终删除最后一个数字字符
if (num.length > 0) { ... } 防止空字符串操作
return str.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');反向遍历的正则表达式
(\d):匹配单个数字并捕获
(?=(\d{3})+(?!\d)):后面跟着3的倍数位数的位置
$1,:在匹配位置插入逗号
添加千分位

验证密码强度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>验证密码强度</title>
<link href="https://labfile.oss.aliyuncs.com/courses/9203/bootstrap.min.css" rel="stylesheet">
<style>
.bg {
overflow: hidden;
background-color: beige;
}
</style>
</head>
<body>
<section class="bg">
<div class="container col-sm-12">
<div class="col-sm-4 col-sm-offset-4" autocomplete="off">
<fieldset>
<legend style="color: #0e0e0e">注 册</legend>
<div class="form-group">
<label for="passwordField">Password</label>
<input type="password" name="passwordField" id="passwordField" class="form-control" required='required' />
</div>
</fieldset>
<div>
<button type="button" class="btn btn-primary btn-validate">验证</button>
<span>&nbsp;&nbsp;&nbsp;强度等级:</span>
<span class="result">未验证</span>
</div>
</div>
</div>
<div class="container col-sm-12">
<br/>
<div class="col-sm-12 col-sm-offset-4">
<p><strong>你填写的密码的强度等级按如下划分:</strong></p>
<dl>
<dt>低:</dt>
<ol><li>密码必须大于 8 个字符</li>
</ol>
<dt>中(在满足低强度要求的前提下,需要满足以下需求):</dt>
<dd>
<ol>
<li>至少需要一个小写字母</li>
<li>至少需要一个数字</li>
</ol>
</dd>
<dt>高(在满足中强度要求的前提下,需要满足以下需求):</dt>
<dd>
<ol>
<li>至少需要一个大写字母</li>
<li>至少需要一个(除数字和字母外的)特殊字符</li>
</ol>
</dd>
</dl>
</div>
</div>
</section>

<script src="https://labfile.oss.aliyuncs.com/courses/9203/jquery.min.js"></script>
<script>
// 请在这里补充代码,实现密码强度的验证

</script>
</body>
</html>

作答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
document.querySelector('button').addEventListener('click',function(){
let str = document.querySelector('input').value
console.log(str)
// 低级强度验证
if(str.length>8){
document.querySelector('.result').innerHTML='低'
// 中级强度验证
const regaz = /[a-z]/g //查找小写字母,执行全局匹配
const regnum = /[0-9]/g //查找数字,行全局匹配
if(str.match(regaz)&&str.match(regnum)){
document.querySelector('.result').innerHTML='中'
// 高级强度验证
const regdaz = /[A-Z]/g //查找大写字母,执行全局匹配
const regzf = /[^0-9a-zA-Z]/g //查找除数字和字母的字符,执行全局匹配
if(str.match(regdaz)&&str.match(regzf)){
console.log('111')
document.querySelector('.result').innerHTML='高'
}
}
}else{
document.querySelector('.result').innerHTML='无效'
}
})

说明

是否使用 /g 标志其实并不影响结果,因为你在使用 .match() 方法时,只关心是否存在匹配项(即结果是否为 null),而不是具体的匹配内容或数量。因此,在这种情况下,可以省略 /g 标志以简化

多表单校验

要求:

完善 index.html TODO 部分,实现多表单校验功能。
两个表单校验规则(rules)如下:
姓名:必填。规则:只能输入汉字。提示:请输入姓名,只能输入汉字。
性别:必填。提示:请选择性别。
年龄:必填。提示:请输入年龄。
是否参加过编程比赛:必填。提示:请选择是否参加过编程比赛。
是否有过创业经历:必填。提示:请选择是否有过创业经历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const rules = reactive({
name: {
required: true,
validator: (rule, value, callback) => {
if (value === '') {
callback('请输入姓名')
} else if (/[^\u4e00-\u9fa5]/g.test(value)) {
callback('只能输入汉字')
} else {
callback()
}
}
},
sex: {
required: true,
message: '请选择性别'
},
age: {
required: true,
message: '请输入年龄'
},
isCompetition: {
required: true,
message: '请选择是否参加过编程比赛'
},
isEntrepreneurship: {
required: true,
message: '请选择是否有过创业经历'
}
})

封装函数

找回链接之旅–可重置的单次执行函数(难懂)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function resetableOnce(fn) {
let flag = true;
let result;
function runOnce(...arr) {
console.log(...arr); // 打印传入的参数
if (flag) {
result = fn(...arr); // 第一次执行时调用 fn,并保存结果
flag = false; // 标记已执行过
return result;
} else {
return result; // 后续调用直接返回之前的结果
}
}
function reset() {
result = undefined; // 重置结果
flag = true; // 重置标志
}
return { runOnce, reset }; // 返回包含两个方法的对象
}

360动画展示–管道模式–异步函数封装

等待每一个动画执行完毕之后,再执行下一个动画,并把前一个动画的返回值initialValue返回,以保持动画的衔接。

1
2
3
4
5
6
7
const pipeline = async (initialValue, sequence) => {
for (let fn of sequence) {
// 将 fn 的执行结果包装为 Promise,并等待其完成
initialValue = await Promise.resolve(fn(initialValue));
}
return initialValue;
};

initialValue 就是原材料。
sequence 是一个函数数组,每个函数代表一个加工步骤。
pipeline 函数的作用就是按顺序执行这些步骤,每一步的输出作为下一步的输入。

知识点

Promise是处理异步操作的对象,有三种状态:pending(进行中)、fulfilled(已完成)、rejecte(已失败)
异步操作(如网络请求、定时器)通常通过回调函数来处理结果。但回调函数会导致回调地狱(Callback Hell),代码结构混乱,难以维护

async:
修饰函数,表示该函数返回一个 Promise。
函数内部可以使用 await 关键字。
await:
只能在 async 函数内部使用。
暂停函数执行,等待 Promise 完成(fulfilled),并获取其结果。
如果 Promise 被 reject,会抛出错误,可以用 try/catch 捕获

布局切换–添加移除类名

实现被点击的模式元素(class=layout-option)处于激活状态,即添加一个类名(active),其他(class=layout-option)移除激活状态,即移除类名(active)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

const layoutOptions = document.querySelectorAll('.layout-option'); // 三个模式元素

// 显示模式
switching.addEventListener('click', function () {
mode.style.display = 'flex'; // 设置显示为flex布局
});

// 遍历选项
layoutOptions.forEach(function (option) {
// 经典模式,浏览模式,工具模式点击事件
option.addEventListener('click', function () {
// TODO:待补充代码
layoutOptions.forEach(item => {
item.classList.remove('active')
})
this.classList.add("active")

选项卡功能

它通过点击不同的div元素来切换显示对应的内容区域
原Html代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>选项卡切换</title>
<link rel="stylesheet" type="text/css" href="./css/index.css" />
</head>
<body>
<div id="main">
<div class="tabs">
<div class="active">选项一</div>
<div >选项二</div>
<div >选项三</div>
<div >选项四</div>
</div>
<div id="content">
<div id="one" class="active">
<p>
爱情要完结的时候自会完结,到时候,你不想画上句号也不行。爱情,原来是含笑饮毒酒。
</p>
<img src="./imgs/1.jpeg" />
</div>
<div id="two">
<p>
在这个光怪陆离的人间,没有谁可以将日子过的行云流水。但我始终相信,走过平湖山雨,岁月山河,那些历尽劫数,尝遍百味的人,会更加生动而干净。
</p>
<img src="./imgs/2.jpeg" />
</div>
<div id="three">
<p>
对于三十岁以后的人来说,十年八年不过是指缝间的事,而对于年轻人而言,三年五年就可以是一生一世。
</p>
<img src="./imgs/3.jpeg" />
</div>
<div id="four">
<p>
我要你知道,在这个世界上总有一个人是等着你的,不管在什么时候,不管在什么地方,反正你知道,总有这么个人。
</p>
<img src="./imgs/4.jpeg" />
</div>
</div>
</div>
</body>
<script src="./js/index.js" type="text/javascript" charset="utf-8"></script>
</html>

要补充的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function init() {
// 获取所有带有'.tabs>div'选择器的div元素,这些是选项卡按钮
var divs = document.querySelectorAll('.tabs>div');
// 获取所有在'#content>div'下的div元素,这些是每个选项卡对应的内容
var imgs = document.querySelectorAll('#content>div');

// 遍历所有的选项卡按钮
for(let i = 0; i < divs.length; i++) {
// 给每个选项卡按钮添加点击事件监听器
divs[i].onclick = function() {
// 先移除所有选项卡按钮和内容的'active'类
for(let a = 0; a < divs.length; a++) {
divs[a].classList.remove('active');
imgs[a].classList.remove('active');
}
// 给当前点击的选项卡按钮和对应的内容添加'active'类
divs[i].classList.add('active');
imgs[i].classList.add('active');
}
}
}
init();

注意

在这段代码中,classList.remove(‘active’)用来确保每次点击时先清除之前设置的激活状态,然后再用classList.add(‘active’)来设置新的激活状态。
当你调用classList.remove()并传递一个参数如’active’时,你实际上是在告诉JavaScript:“请从这个元素的class属性中移除名为active的类。”
工作原理:此方法接受一个或多个类名(不带.前缀)作为参数,并尝试从元素的class列表中删除这些类。如果该类存在于元素上,则它将被移除;如果不存在,则什么也不会发生。
classList方法期望的是纯粹的类名字符串,而不包含任何CSS选择器符号(如.代表类选择器)

选择器 实时与静态更新:

document.querySelectorAll(‘.tabs>div’) 和 document.querySelectorAll(‘#content>div’):
这两个调用使用的是CSS选择器语法来选择元素。querySelectorAll方法返回一个静态的NodeList,包含所有匹配给定选择器的元素。
.tabs>div表示选择所有直接位于具有.tabs类的元素下的div元素;#content>div同理,但它是基于ID选择器#content。

使用getElementsByClassName(‘.tabs’)是不正确的,因为getElementsByClassName接受一个参数,即一个或多个类名(不带.),并且它会返回一个实时更新的HTMLCollection。因此,正确的方式应该是document.getElementsByClassName(‘tabs’),但这只会返回拥有tabs类的所有元素,而不能用于直接选择.tabs下的子div元素
选择器方法对比表
实时与静态集合:
HTMLCollection(动态)会随DOM变化自动更新
NodeList(静态)是选择时的快照
getElementsByClassName和getElementsByTagName返回的是实时的HTMLCollection,这意味着如果文档中的元素发生了变化,这个集合也会随之更新。相反,querySelectorAll返回的是静态的NodeList,即使文档发生变化,这个列表也不会改变。
性能考量:对于简单的选择操作,getElementsByClassName和getElementsByTagName可能会比querySelectorAll更快,尤其是在处理大量元素时。然而,querySelectorAll提供了更大的灵活性,特别是在处理复杂的选择器时。
综上所述,如果你想根据类名和父子关系精确选择元素,querySelectorAll是最适合的选择之一。

优化方向

1
2
3
4
5
6
// 事件委托优化示例(减少事件绑定次数)
document.querySelector('.tabs').addEventListener('click', function(e) {
if(e.target.tagName === 'DIV') {
// 处理逻辑...
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function init() {
// 最佳元素获取方式
const tabsContainer = document.querySelector('.tabs'); // 使用CSS选择器获取容器
const content = document.getElementById('content'); // ID获取是最快的方式

// 事件委托绑定
tabsContainer.addEventListener('click', function(e) {
// 事件目标过滤
const clickedTab = e.target.closest('.tabs > div');
//e.target:实际点击的原始元素
if (!clickedTab) return;

// 获取索引(优化版)
const index = Array.prototype.indexOf.call(
tabsContainer.children,
clickedTab
);

// 类名操作(现代写法)
const prevActiveTab = tabsContainer.querySelector('.active');
const prevActiveContent = content.querySelector('.active');

// 执行切换
if (prevActiveTab) prevActiveTab.classList.remove('active');
if (prevActiveContent) prevActiveContent.classList.remove('active');
clickedTab.classList.add('active');
content.children[index].classList.add('active');
});
}

新年贺卡(逻辑+随机数)

选择器方法对比表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="index.css">
</head>

<body>
<div class="card-name">新年贺卡</div>
<div class="card" id="card">
<p id="greeting-display"></p>
</div>
<button id="btn">书写贺卡</button>

<script src="./index.js"></script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
document.addEventListener('DOMContentLoaded', function () {
const greetingDisplay = document.getElementById("greeting-display")
const btn = document.getElementById("btn")
// 点击开始书写按钮
btn.addEventListener("click", () => {
show(greetingDisplay)
})
})

const greetings = [
"新年快乐!",
"接受我新春的祝愿,祝你平安幸福",
"祝你新年快乐,洋洋得意!",
"新的一年,新的开始;心的祝福,新的起点!",
"新年好!祝新年心情好,身体好,一切顺心!",
]

// 随机数函数 从 greetings 随机取一个值并返回
function writeGreeting() {
// TODO 带补充代码
// 步骤1:生成随机索引(0到数组长度-1)
const randomIndex = Math.floor(Math.random() * greetings.length);
// 步骤2:返回对应的祝福语
return greetings[randomIndex];
}

/*
* @param {*} greetingDisplay 要显示内容的dom元素
*/
// show 将 writeGreeting 函数中返回的内容显示在 greetingDisplay 元素中
function show(greetingDisplay) {
// TODO 待补充代码
// 步骤1:获取随机祝福语
const greeting = writeGreeting();
// 步骤2:更新DOM显示内容
greetingDisplay.textContent = greeting;
}

module.exports = { show, writeGreeting }

随机数生成器(指定范围和个数的不重复的随机数数组)

生成指定范围和个数的不重复的随机数数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="./js/index.js"></script>
</head>
<body>
<script type="text/javascript">
var testArr = getRandomNum(1,30,3);
document.write("<h1>1-30 以内的 3 个随机数:"+testArr+"</h1>");
testArr = getRandomNum(1,100,10);
document.write("<h1>1-100 以内的 10 个随机数:"+testArr+"</h1>");
</script>
</body>
</html>

作答部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const getRandomNum = function(min, max, countNum) {
var arr = []; // 初始化一个空数组来存储结果
//在以下区域作答
while(arr.length < countNum) { // 当数组长度小于需要的随机数数量时循环
var num = Math.floor(Math.random() * (max - min + 1) + min); // 生成[min, max]范围内的随机整数

if (arr.indexOf(num) === -1) { // 如果新生成的随机数不在数组中
arr.push(num); // 将其添加到数组中
}
}

return arr; // 返回包含所有随机数的数组
}

module.exports = getRandomNum; // 导出函数以便其他模块可以使用
```

#### 知识点
生成``[min,max]``区间的随机整数公式
``Math.floor(Math.random() * (max - min + 1)) + min``
textContent:安全设置纯文本内容
innerHTML:可以插入HTML标签(本题不需要)
#### 相关:洗牌算法
这种方法提供了一种有效且公平的方式来从一系列数字中随机抽取若干个不重复的数字。
``` js
const getRandomNum = function(min, max, countNum) {
let pool = []; // 存放所有可能数值的池子

// 生成数值范围池(包含min到max所有整数)
for (let i = min; i <= max; i++) {
pool.push(i);
}

// Fisher-Yates洗牌算法(打乱数组)
for (let i = pool.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); // 随机索引j,范围是[0, i]
[pool[i], pool[j]] = [pool[j], pool[i]]; // 交换pool[i]和pool[j]
}

// 截取前countNum个元素
return pool.slice(0, countNum);
};

生成数值范围池:
使用一个循环从min到max遍历,并将每个值添加到pool数组中。
这一步的结果是一个包含了所有可能数值的数组,例如如果min是1,max是5,则pool会是[1, 2, 3, 4, 5]。
Fisher-Yates洗牌算法:
这是一种高效的算法,用于生成一个均匀随机排列的数组。对于每一个数组中的元素,选择一个随机位置与之交换,从而达到打乱数组的目的。
Math.floor(Math.random() * (i + 1))生成一个介于0和当前索引i之间的随机整数j。
[pool[i], pool[j]] = [pool[j], pool[i]]这行代码使用了ES6的解构赋值特性,交换数组中两个元素的位置。
截取前countNum个元素:
使用Array.prototype.slice()方法复制数组的一部分。这里是从数组的开始处(索引为0)开始,复制countNum个元素。
这样就得到了一个由原数组中随机选出的、不重复的countNum个元素组成的数组。

相关:随机颜色生成

1
2
3
function randomColor() {
return '#' + Math.floor(Math.random()*0xffffff).toString(16).padStart(6,0);
}

卡片化标签页(排他算法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 实现选项卡功能
function init() {
// TODO 待补充代码
var divs = document.querySelectorAll('.tabs>div');
var imgs = document.querySelectorAll('#content>div');

for(let i = 0;i<divs.length;i++)
{
divs[i].onclick=function(){
for(let a = 0;a<divs.length;a++)
{
divs[a].classList.remove('active');
imgs[a].classList.remove('active');
}
divs[i].classList.add('active');
imgs[i].classList.add('active');
}
}
}
init();

加减乘除计算 (switch case语句)

加减乘除计算
提供代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>简易计算器</title>
</head>

<body>
<h3>计算器</h3>
<input type="number" id="num1" size="10" />
<select id="type">
<option value="0">+</option>
<option value="1">-</option>
<option value="2">*</option>
<option value="3">/</option>
</select>
<input type="number" id="num2" size="10" />
<input type="button" value="计算" id="btnsubmit" />
<p id="val"></p>
</body>
<script type="text/javascript" src="cal.js"></script>
<script>
document.getElementById("btnsubmit").onclick = function() {
var num1 = document.getElementById("num1").value; //操作数1
var num2 = document.getElementById("num2").value; //操作数2
var type = document.getElementById("type").value; //符号
if (type == 3 && num2 == 0) {
alert("被除数不能为0");
return false;
}
var result = cal(parseInt(num1), parseInt(num2), parseInt(type));
document.getElementById("val").innerText = result;
};
</script>

</html>

补充代码如下:
break; 这里的break是不必要的,因为return已经结束了函数的执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function cal(num1, num2, type) {
// TODO: 在此处补充代码
switch(type){
case 0 :
return num1 + num2
case 1 :
return num1 - num2
case 2 :
return num1 * num2
case 3 :
return num1 / num2
}
}
module.exports = cal; //请勿删除

计算器

灯泡变色–定时器

页面加载完成 3 秒后灯的颜色变成红色。
在灯的颜色变成红色的 3 秒后,灯的颜色变成绿色(即 6 秒后灯光变成绿色)。
随后颜色不再变化。
请通过修改 display 属性来显示不同颜色的灯的图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function red() {
// 隐藏默认和绿色灯,显示红色灯
document.getElementById('defaultlight').style.display = 'none';
document.getElementById('greenlight').style.display = 'none';
document.getElementById('redlight').style.display = 'inline-block';
}

function green() {
// 隐藏默认和红色灯,显示绿色灯
document.getElementById('defaultlight').style.display = 'none';
document.getElementById('redlight').style.display = 'none';
document.getElementById('greenlight').style.display = 'inline-block';
}

function trafficlights() {
// 3秒后执行 red(),6秒后执行 green()
setTimeout(red, 3000);
setTimeout(green, 6000);
}

// 页面加载完成后立即调用 trafficlights()
trafficlights();

其他

fectch

购物狂欢节

要求:在 js/stores/useProducts.js 中补充 fetchProducts 函数, category 参数代表商品的分类。可能传来值为 books 、 clothing 、 electronics ,三个值对应的请求文件分别为 api/products/ 中 books.json 、 clothing.json 、 electronics.json 的数据,将请求到的数据赋值到 this.products 中(每次请求都要重新赋值,请求数据不累加),函数不需要返回值

使用 fetch(url) 发送请求。
使用 .json() 将响应体(res)解析为 JSON 数据。

1
2
3
4
5
6
function fetchProducts(category) {
fetch(`api/products/${category}.json`)
.then(res=>{return res.json()} )
.then(data=> {this.products=data})
.catch(err=>{console.log(err)})
}
1
2
3
function fetchProducts(category) {
const data = await( await fetch(`api/products/${category}.json`)).json();
this.products=data
1
2
3
4
async function fetchProducts(category) {
// TODO: 根据分类获取商品数据
products.value = await fetch(`./api/products/${category}.json`).then(res => res.json())
}

添加商品到购物车

1
2
3
4
5
6
7
8
9
function addProduct(product) {
// TODO: 添加商品到购物车,如果商品已存在,则数量+1
if (products.value.includes(product)) {
product.quantity++;
} else {
product.quantity = 1;
products.value.push(product);
}
}

计算总价

1
2
3
4
5
const totalPrice = computed(() => {
// TODO: 计算总价
var sum=products.value.reduce((acc, cur) => acc + (cur.price * cur.quantity), 0)
return sum
});

axios

知识点统计图–axios

请在 TODO 1 处使用 axios 完成题库数据请求,请求地址储存在 mockUrl 变量中,将请求到的数据以 js 数组对象的形式储存在 rawData 变量中。并将 rawData 变量值传递给函数 extractUniquePoints,最终将函数 extractUniquePoints 的执行结果赋值给变量 knowledgePoints。

1
2
3
4
5
6
7
8
onMounted(function () {
chart = echarts.init(chartContainer.value);
// TODO 1 请在下面补充代码
axios.get(mockUrl).then((res)=>rawData.value = res.data)
knowledgePoints= extractUniquePoints(rawData.value)

// TODO 1 END
});

学生探览–axios获取数据并保存给items

在 TODO1 处使用 AXIOS 请求班级数据,并储存到变量items中
数据地址已经储存在变量 mockUrl 中,请直接使用此变量作为请求地址

1
2
3
4
5
6
7
8
9
    axios.get(mockUrl).then((res)=>{
//console.log(res)
//console.log(res.data)
items.value=res.data
//console.log(items.value)
}).catch((err)=>{
console.log(err);
})
}

学生探览–计算属性,数组

TODO2 处补全代码,当对班级学生信息进行查询时会触发。根据班级数据(即目标 1 获取赋值的 items 变量)分别计算班级在“技术能力得分”、“硬实力得分”、“软技能得分”三个方面的平均成绩,并将这三个方面的平均成绩以数组的方式赋值到计算属性 classAverageScores 中。

1
2
3
4
5
6
const classAverageScores = computed(()=>
items.value.reduce((acc,cur,_)=>{
return cur.scores.map((item,index)=>
acc[index]+item)
},[0,0,0]).map(score=>Math.round(score/items.value.length))
)

Echarts

1
2
3
4
5
6
7
8
9
10
11
  radar: {
// TODO3 START 请在下面补充代码
shape:'circle',
startAngle:0,
indicator: [
{ name: "技术能力得分", max: 100, min: 0 },
{ name: "硬实力得分", max: 100, min: 0 },
{ name: "软技能得分", max: 100, min: 0 },
],
// TODO3 END
},

日期

每日签到

1
2
3
4
5
6
7
signInBtn.addEventListener("click",()=>{
let days=document.querySelectorAll("#days p")
let day=new Date().getDate()
days[day-1].classList.add("active")
document.querySelector("#sign_in_btn").classList.add("no-active")
document.querySelector("#sign_in_btn").innerText="明天也要记得来哦"
})

浏览器

你还在等我吗

补充函数 setItem,当点击页面上「点击存入」按钮时,会调用此函数,并将存入键、存入值、存入有效毫秒时长这三个数据传入参数 key 、value 、expired 。补全代码,使函数根据key 和value 存入 localStorage 。
补充函数 getItem,当点击页面上「点击取出」按钮时,会调用此函数,并将要取值的键传入参数 key。补全代码,使函数根据 key 值从 localStorage 中取出数据并返回,如果数据超过有效毫秒时长,则清除数据。

补充函数 removeItem,当点击页面上「点击进行清理」按钮时,会调用此函数,并将要清理的键传入参数 key。补全代码,使函数根据 key 值从 localStorage 中清除数据。

1
2
3
4
5
6
7
8
9
10
11
12
class Storage {
setItem(key, value, expired) {
localStorage.setItem(key, value)
setTimeout(() => this.removeItem(key), expired)
}
getItem(key) {
return localStorage.getItem(key)
}
removeItem(key) {
localStorage.removeItem(key)
}
}

小结

getAttribute 的作用:获取 HTML 元素的指定属性值
const name1=trigger.getAttribute("data-filter-name")
媒体查询

1
2
3
@media screen and (max-width:760ox){
屏幕宽度<760时生效
}

旋转角度`transform: rotate(60deg)` 新增元素添加样式再插入
1
2
3
const xz = createElement('span')
xz.style.xxx =xxx
oldelement.append(xz) //(弹幕那道题)
对象变数组拼接
1
2
const arr1 = Object.keys(要处理的对象).map(item=>item+"="要处理的对象[item]).join("$")
const arr2 = Object.entires(要处理的对象).map(([key,value])=>key+"="+value).join("&")

有没有包含+三目运算符
arr.includes(“包含的东西”) ? 包含了执行 : 没包含执行
ref声明的对象数组操作,别丢value!!
原题

1
2
3
4
5
const waste = ref([{k1:v1,k2:v2,k3:v3},{},{}])
waste.value.forEach(item=>{if(item.k1==='厨余垃圾'){
food_waste.value.push(item.k2)
}
})