ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[toc] # 题1、使用原生 JS 的 DOM 实现:1. 弹出一个框,可以输入一个数字 n 。 2. 然后在页面中制作 n*n 个的格子 3.每个格子鼠标放上时背景色变为红色,鼠标挪开时颜色恢复 4. 当点击一个格子时,颜色变成绿色并锁定这个颜色(鼠标放上、挪开不改变颜色) 5.点击一个已经被锁定的元素时,取消锁定颜色恢复,继承可以鼠标放上、挪开时改变颜色。(格子大小可以使用 CSS 控制,背景色的改变通过 JS 控制) ![](https://img.kancloud.cn/f6/16/f616720f304963e011625f112d4c3a05_846x544.png) ## 实现01-先根据 N 生成 table ![](https://img.kancloud.cn/4f/f9/4ff91a5cc09f58c8f23753e885e6933b_1530x1514.png) ## 实现02-鼠标放上、挪开时改变背景色 说明:因为为每个 td 绑定事件,比较浪费资源(td太多),所以我们可以使用 `事件委托` 技术只在 table 上绑定一个事件即可: ![](https://img.kancloud.cn/91/c9/91c91647154a79cf57c4aa64bb7b7898_1028x916.png) ## 实现03-标记一个格子被锁定了 为了标记至少有两种实现思路: 思路一、创建一个 Map 数据类型,保存每个格子是否被锁定的状态。 let locked = new Map() // 当点击一个格子时 locked.set(格子对象, true) // 被锁定了 思路二、直接在鼠标上自定义一个属性来标记它的状态 <td data-locked="1"></td> <td data-locked="0"></td> 使用时直接 对象.dataSet 即可 以下采用第二种 dataset 的方案 ![](https://img.kancloud.cn/44/de/44defba9e2679163ac0852c21123f195_2018x980.png) ## 实现04-如果锁定就不允许修改 在鼠标放上和挪开时判断是否被锁定: ![](https://img.kancloud.cn/07/c4/07c4bd19cdd4f4c9fab03eec58db522e_1248x1122.png) ## 实现05-再次点击时解锁 ![](https://img.kancloud.cn/f2/96/f296d2d71cafb1e27b3311885c7e405d_1216x892.png) ## 扩展:面向对象的写法 ![](https://img.kancloud.cn/cf/08/cf08bbdb5a79d697f545861a1b79676d_1040x1460.png) # 題2、弹出两个框,一个框输入一个拥有总金额数量(整数),另一个框可以输入多个商品的价格(多个数字之间用空格隔开),计算得出,这些总金额最多能购买多少件商品(每件商品只能购物一次)。 比如:输入 总金额:100 多个商品价格:10 40 49 60 70 80 100 300 得出的结果: 最多能买:3 个 哪些价格的商品:10 40 49 花费多少钱:99 实际思路: 1. 先把价格的字符串转成数组(split 通过空格转) 2. 对数组排序(sort((a,b)=>a-b)) 3. 循环数组从第1个(最便宜的)开始循环累加商品金额,直到累加的金额大于,总金额为止 ~~~ // 总金额 let totalPrice = prompt("请输入总金额:") // 商品价格列表 let goodsPrice = prompt("请输入商品价格(多个用空格隔开):") // 商品价格列表转数组 let goodsArr = goodsPrice.split(/\s+/) // 根据至少一个连续的空格转数组 // 让数组升序排列 goodsArr.sort((a,b)=>a-b) // 从数组的第一个商品开始购买 let sum = 0 // 已购买的商品的总价 let buy = [] // 已购买的商品的单价 let price for(let i=0; i<goodsArr.length; i++) { // 价格转成数字(+、Number、parseInt、parseFloat) price = Number(goodsArr[i]) // 如果没有超出就继续 if( (price + sum) <= totalPrice) { sum += price buy.push(price) } else { // 超出了就退出 break } } // 结果输出 console.log("拥有的总钱数:"+totalPrice) console.log("可以购买的商品列表:"+goodsPrice) console.log("最多能购买:"+buy.length+" 个商品") console.log("能够购买的商品价格为:"+buy.toString()) console.log("购买这件商品共花费:"+sum) ~~~ 运行结果: ![](https://img.kancloud.cn/66/70/6670a865cb61c48f9d3355fc87ee7824_748x270.png) # 题3、随机颜色的 99 乘法表 ![](https://img.kancloud.cn/72/6b/726b784857b099accb0a5511d11c3f8a_1598x1006.png) 代码实现: ![](https://img.kancloud.cn/c7/6b/c76be00f5b5429a9620e278951002b04_850x1364.png) # 题4、JS 原生购物车 数据: ~~~ const data = [ { "image": "https://img13.360buyimg.com/babel/s1180x940_jfs/t1/112612/27/8633/100927/5ed0ffe3Ee5006a06/142195ec551409e6.jpg.webp", "goods_name": "小米移动电源10000mAh", "price": "135", "comment": "1.4万" }, { "image": "https://img10.360buyimg.com/pop/s1180x940_jfs/t1/133282/15/506/78667/5ece44afEd0d8193e/89395514aa661a69.jpg.webp", "goods_name": "小米电源 高配版", "price": "135", "comment": "1.4万" }, { "https://img10.360buyimg.com/da/s1180x940_jfs/t1/120568/26/3467/101836/5ed0fda0E49973841/e1801a3d7e067ce7.jpg.webp", "goods_name": "小米活塞耳机", "price": "135", "comment": "1.4万" }, { "https://imgcps.jd.com/ling/100008348542/5omL5py66LSt5a6e5oOg/5aSH6LSn6LaF5YC8/p-5bd8253082acdd181d02fa33/28403921/590x470.jpg", "goods_name": "小米耳机", "price": "135", "comment": "1.4万" } ] ~~~ ![](https://img.kancloud.cn/30/28/3028ccdc3f5a8bae17551cd79000d5a4_1998x1072.png) OOP代码实现: 1. html 和 CSS ![](https://img.kancloud.cn/7c/2d/7c2d63a64413043ec34210d5332ba39f_400x1506.png) 2. 购物车类 ![](https://img.kancloud.cn/32/74/3274de939c1f4c81864e323fd515fd6a_566x644.png) 3. 为类添加渲染数据的方法 ![](https://img.kancloud.cn/72/65/72657a7290cf37aed7a542ca5270ef1d_618x1110.png) 4. 渲染购物车表格 ![](https://img.kancloud.cn/21/bf/21bf2d4c5e9fc4b03046b09dbf663535_610x782.png) 5. 加入购物车 ![](https://img.kancloud.cn/b3/35/b3357cc50d4d2ae492959eeee7c70c00_814x1082.png) 6. 删除 ![](https://img.kancloud.cn/34/87/34878ea9316b1f91c01ca265f3bff196_676x672.png) 7. 使用 ![](https://img.kancloud.cn/4b/63/4b63d92f6618c90c319e2ca2822092b7_214x142.png) # 题5、表单验证 ![](https://img.kancloud.cn/50/57/5057e22396dc1d68eb96aacbe8ed5639_810x674.png) # 题6、有一个数组,数组中有10件商品,每件商品的是一对象类型的数据,在页面中每次显示三条记录,并可以上下按钮翻页? ~~~ <div id="app"></div> <button onclick="prev()">上一页</button> <button onclick="next()">下一页</button> /********** 1. 构建 10 件商品的数据 *******/ let data = [] // 循环生成 10 件商品 for(let i=0; i<10; i++) { data.push({ id: i+1, goods_name: '手机-' + i, price: (Math.random()*100).toFixed(2) }) } /*********** 2. 在页面中渲染三件商品 如何截取数组? slice:不会修改原数组 splice:从原数组中把截取的数据删除 翻页? 使用 slice 从数组中截取出三件商品,截取时的下标: 第1页 --》 0 ~ 3 第2页 --> 3 ~ 6 第3页 --> 6 ~ 9 第4页 --> 9 ~ 12 ....... 第n页 --> (n-1)*3 ~ (n-1)*3+3 ******/ const app = document.getElementById('app') // 当前页 let page = 1 // 显示第 i 页的数据 function showData(page) { // 计算截取的下标 let start = (page-1)*3 let end = start + 3 // 截取出第1页的数据 let goods = data.slice(start, end) // 先把原数据清空 app.innerHTML = '' goods.forEach(v=>{ let div = document.createElement('DIV') div.innerHTML = "商品名称:"+v.goods_name + ",价格:¥"+v.price app.appendChild(div) }) } showData( page ) // 下一页 function next() { // 如果不是最后一页 if(page < 4) { page++ showData(page) } } // 上一页 function prev() { // 如果不是第1页 if(page > 1) { page-- showData(page) } } ~~~ 扩展练习:有一个数组,数组中有10件商品,一次在页面中显示三件商品,有一个“换一换”,每点击一次换三件。要求:页面中始终显示三件商品,不够三件时从前面取,比如:最后只剩一件不够三件了,那么就从最前面拿2件凑够三件显示。 实现思路:1. 每次从数组中截取出前三件商品并从数组中把这三件商品删除(splice(0,3)) 2. 把截取出的三件商品再合并到数组的最后(concat) ~~~ <button onclick="change()">换一换</button> function change() { // 取出前3件商品,并从数组中删除这三件 let goods = data.splice(0,3) // 把这3件商品再放回数组的最后 data = data.concat(goods) // 渲染这三件商品 app.innerHTML = '' goods.forEach(v=>{ let div = document.createElement('DIV') div.innerHTML = "商品名称:"+v.goods_name + ",价格:¥"+v.price app.appendChild(div) }) } ~~~ # 题7、实现一个文章搜索功能:制作一个搜索框和一个搜索按钮,当点击搜索按钮时就调用接口搜索相关文章,每页显示15条,并实现翻页功能? 接口文档地址:[http://ttapi.research.itcast.cn/app/v1\_0/search](http://ttapi.research.itcast.cn/app/v1_0/search) 请求方式:GET 参数:q(搜索关键字) page(当前页码) per_page (每页条数) 使用技术:原生 JS 制作完之后的效果: ![](https://img.kancloud.cn/a4/7a/a47a853c292076636b2aed76397435df_748x626.png) 代码实现: HTML 和 CSS ![](https://img.kancloud.cn/06/1c/061c91bc2e6980c6cb35cf73c8928a15_754x1182.png) 封装原生 AJAX 为Promise 对象 ![](https://img.kancloud.cn/ee/31/ee31c43f9e852fbeb0586b3a146a1597_1110x564.png) 两个辅导函数 ![](https://img.kancloud.cn/e2/2e/e22ec42fcc278fb579676a20438e7019_648x258.png) 点击搜索时调用接口获取某一页的数据 ![](https://img.kancloud.cn/3f/56/3f56e016d70e14c5e5027472c06be110_1878x1172.png) 根据返回的数据渲染翻页按钮和数据列表 ![](https://img.kancloud.cn/61/dc/61dc57e9b9881f0c8c3a8324a3bb7eea_676x1292.png) 搜索联想功能 ![](https://img.kancloud.cn/2b/e3/2be3eb5110f3115849db8a4721dd7b7f_1034x1162.png) 点击翻页按钮时重新调用接口 ![](https://img.kancloud.cn/2b/12/2b122ddcb46bc20da989d3a16945568a_710x378.png) # 题8、现有三个接口:登录-注册 、获取短验证码、令牌过期刷新,请使用前端技术实现登录功能? 功能描述: ![](https://img.kancloud.cn/7d/d3/7dd3b1200a88c83262283d192b358a27_1208x850.png) 代码实现(使用 OOP 的语法) 1. 静态页面 ![](https://img.kancloud.cn/46/ae/46ae09387e4599c1dd81ff3b6757b17f_778x1402.png) 2. 封装 AJAX 对象 ![](https://img.kancloud.cn/a2/6a/a26ab5e9ff6269372268c9cb9f8cd3da_798x994.png) 3. 创建一个辅助类 ![](https://img.kancloud.cn/d6/69/d6692858914e5d244df08090c95958f6_430x282.png) 4. 创建实现业务逻辑的类 ![](https://img.kancloud.cn/d7/ea/d7eafa5b247941861646304dd92f0e8b_546x532.png) 类中有三个方法 ![](https://img.kancloud.cn/80/a5/80a54905e4ec78e87de09f3b89858f17_856x780.png) ![](https://img.kancloud.cn/44/42/444285eee12d5f634b5653ed0b4133cf_1082x1340.png) 5. 在页面中使用这个类实现功能 ![](https://img.kancloud.cn/82/6f/826f1121a94091d5a3a3c732945da9b5_618x244.png) # 题9、以第题8的基础上,能够获取并修改头像? 接口一、获取用户信息的接口 地址:http://ttapi.research.itcast.cn/app/v1_0/user/profile 方式:GET 请求参数: ![](https://img.kancloud.cn/71/f3/71f3f89fc7f0a8a86a8341c468d3751a_1246x570.png) 接口二、修改头像 地址:http://ttapi.research.itcast.cn/app/v1_0/user/photo 方式:PATCH 请求参数: ![](https://img.kancloud.cn/8d/cb/8dcb7cad20e3bbfd553cb327bacc1912_1092x1156.png) HTML 代码 ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> html, body { margin: 0; padding: 0; height: 100%; } #login-layer { width: 100%; height: 100%; background-color: rgba(0,0,0,.4); position: fixed; left: 0; top: 0; display: none; } #login { width: 400px; height: 250px; background-color: #fff; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); border-radius: 10px; padding: 50px 0 0 50px; box-sizing: border-box; } input { padding: 5px; margin-bottom: 20px; } #avatar { max-width: 350px; } </style> </head> <body> <div id="login-info"></div> <hr> <div><img id="avatar"></div> <button id="btn-info">获取头像</button> <div> <input type="file" id="upload-file" /> <br> <button id="upload-btn">更新头像</button> </div> <!-- 登录表单 --> <div id="login-layer"> <div id="login"> <form id="login-form"> <div><input type="text" name="mobile" placeholder="请输入11位手机号码"></div> <div> <input type="text" name="code" placeholder="请输入短信验证码"> <button name="send-code">发送短信验证码</button> </div> <div> <button name="login">登录</button> <button name="close">关闭</button> </div> </form> </div> </div> <script src="./index.js"></script> <script> new Index() </script> </body> </html> ~~~ JS 代码 ~~~ // Index 类(逻辑逻辑) function Index() { // 创建工具对象 this.tool = new Tool() // 获取页面中的元素 this.loginInfo = this.tool.$('login-info') this.loginLayer = this.tool.$('login-layer') this.loginForm = this.tool.$('login-form') this.infoBtn = this.tool.$('btn-info') this.avatar = this.tool.$('avatar') this.uploadFile = this.tool.$('upload-file') this.uploadBtn = this.tool.$('upload-btn') // 1. 检查登录信息 this.checkLogInfo() // 2. 处理登录、退出两个按钮 this.bindLoginBtn() // 3. 处理表单中的按钮 this.bindFormBtn() // 4. 显示个人信息 this.getUserInfo() // 5. 上传图片 this.bindUpload() } // 判断登录信息 Index.prototype.checkLogInfo = function () { if (localStorage.getItem('token') === null) { this.loginInfo.innerHTML = '您好,游客 <button name="login">登录</button>' } else { let mobile = localStorage.getItem('mobile') this.loginInfo.innerHTML = `${mobile} <button name="logout">退出</button>` } } // 为登录、退出绑定点击事件 Index.prototype.bindLoginBtn = function () { // 为父元素绑定一个点击事件,处理这个元素中所有的按钮(事件委托) this.loginInfo.onclick = e => { // 当点击的是按钮时 if (e.srcElement.nodeName === 'BUTTON') { // 点击是登录 if (e.srcElement.name === 'login') { // 显示登录层 this.loginLayer.style.display = 'block' } // 点击的是退出 if (e.srcElement.name === 'logout') { // 清空令牌 localStorage.clear() // 重新检查登录状态 this.checkLogInfo() } } } } // 为表单中的按钮 Index.prototype.bindFormBtn = function () { this.loginForm.onclick = e => { if (e.srcElement.nodeName === 'BUTTON') { // 阻止按钮的默认行为(提交表单) e.preventDefault() if (e.srcElement.name === 'send-code') { // 1. 验证手机号码并去掉左右空格 let mobile = this.loginForm.mobile.value.trim() let RE = /^1[34578]\d{9}$/ if (!RE.test(mobile)) { return alert("手机号码格式不正确!") } // 2. 发送短信(调用接口) axios.get('/sms/codes/' + mobile).then(res => { if (res.message === 'OK') { // 3. 按钮 60s 倒计时 let sec = 60 // 按钮状态为禁用 e.srcElement.disabled = true e.srcElement.innerText = '60s' // 启动定时器 let si = setInterval(() => { sec-- if (sec < 0) { // 停止定时器 clearInterval(si) e.srcElement.disabled = false e.srcElement.innerText = '发送短信验证码' } else { e.srcElement.innerText = sec + 's' } }, 1000) } else { alert('发短信失败!') } }) } else if (e.srcElement.name === 'login') { // 1. 表单验证 let mobile = this.loginForm.mobile.value.trim() if (!/^1[34578]\d{9}$/.test(mobile)) { return alert("手机号码格式不正确!") } let code = this.loginForm.code.value.trim() if (!/^\d{6}$/.test(code)) { return alert("短信验证码不正确!") } // 2. 调用接口 axios.post('/authorizations', { mobile, code }).then(res => { if (res.status == 201) { // 3. 保存令牌 localStorage.setItem('token', res.data.data.token) localStorage.setItem('refresh_token', res.data.data.refresh_token) // 如何把 137 8888999 9 中间 7 位变成* ---> 137*******9 // 方法一、正则: mobile.replace(/^(\d{3})(\d{7})(\d)$/, "$1******$3") // 方法二、字符串截取 let mobile = mobile.substr(0,3) + '*******' + mobile.substr(-1) localStorage.setItem('mobile', mobile.replace(/^(\d{3})(\d{7})(\d)$/, "$1******$3")) // 4. 更新用户信息 this.loginLayer.style.display = 'none' this.checkLogInfo() } else { alert("验证码不正确!") } }) } else if (e.srcElement.name === 'close') { // 隐藏登录层 this.loginLayer.style.display = 'none' } } } } // 获取用户信息 Index.prototype.getUserInfo = function () { this.infoBtn.onclick = () => { axios.get('/user/profile').then(res => { // 设置头像到图片上 this.avatar.src = res.data.photo }) } } // 上传图片 Index.prototype.bindUpload = function () { // 将要上传的图片对象 let image // 获取上传的图片 this.uploadFile.onchange = e => { // 把选择的图片保存到外层的变量中 image = e.srcElement.files[0] // 预览图片 const fr = new FileReader() fr.readAsDataURL(e.srcElement.files[0]) // 设置读取完之后的回调函数 fr.onload = e1 => { // 把读取的图片路径设置到头像 this.avatar.src = e1.srcElement.result } } // 更新头像按钮 this.uploadBtn.onclick = e => { // 在上传文件时一般需要使用 FormData 这个对象 let fd = new FormData() fd.append('photo', image) // 调用接口更新头像 axios.patch('/user/photo', fd).then(res => { console.log(res) }) } } // 封装 AJAX 的对象 const axios = { baseURL: 'http://ttapi.research.itcast.cn/app/v1_0', _xhr: new XMLHttpRequest(), _addToken: function (isjson = true) { // 获取令牌 let token = localStorage.getItem('token') if (token !== null) { // 把令牌添加到请求协议头上 this._xhr.setRequestHeader('Authorization', 'Bearer ' + token) } if (isjson) { this._xhr.setRequestHeader('Content-Type', 'application/json') } }, get: function (url) { return new Promise((ok, err) => { this._xhr.onreadystatechange = () => { if (this._xhr.readyState == 4 && this._xhr.status == 200) { ok(JSON.parse(this._xhr.responseText)) } } this._xhr.open('GET', this.baseURL + url) // 设置协议头 this._addToken() this._xhr.send(null) }) }, post: function (url, data) { return new Promise((ok, err) => { this._xhr.onreadystatechange = () => { // 只要连接成功就返回数据 if (this._xhr.readyState == 4) { // 返回状态码和数据 ok({ "status": this._xhr.status, "data": JSON.parse(this._xhr.responseText) }) } } this._xhr.open('POST', this.baseURL + url) // 设置协议头 this._addToken() // 发送 this._xhr.send(JSON.stringify(data)) }) }, patch: function (url, data) { return new Promise((ok, err) => { this._xhr.onreadystatechange = () => { // 只要连接成功就返回数据 if (this._xhr.readyState == 4) { // 返回状态码和数据 ok({ "status": this._xhr.status, "data": JSON.parse(this._xhr.responseText) }) } } this._xhr.open('PATCH', this.baseURL + url) // 设置协议头(提交的不是 json) this._addToken(false) // 发送 this._xhr.send(data) }) } } // 辅助类 function Tool() { } Tool.prototype.$ = function(id) { return document.getElementById(id) } Tool.prototype.C = function(tag) { return document.createElement(tag) } ~~~