ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] # 题1、使用原生 JS 的 DOM 实现:1. 弹出一个框,可以输入一个数字 n 。 2. 然后在页面中制作 n\*n 个的格子 3.每个格子鼠标放上时背景色变为红色,鼠标挪开时颜色恢复 4. 当点击一个格子时,颜色变成绿色并锁定这个颜色(鼠标放上、挪开不改变颜色) 5.点击一个已经被锁定的元素时,取消锁定颜色恢复,继承可以鼠标放上、挪开时改变颜色。(格子大小可以使用 CSS 控制,背景色的改变通过 JS 控制)JavaScript 小案例 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfat92dqw1j30ni0f4tdn.jpg) ## 实现01-先根据 N 生成 table ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfat9r7htrj316i1621kx.jpg) ## 实现02-鼠标放上、挪开时改变背景色 说明:因为为每个 td 绑定事件,比较浪费资源(td太多),所以我们可以使用 `事件委托` 技术只在 table 上绑定一个事件即可: ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfataa4ye4j30sk0pge00.jpg) ## 实现03-标记一个格子被锁定了 为了标记至少有两种实现思路: 思路一、创建一个 Map 数据类型,保存每个格子是否被锁定的状态。 let locked = new Map() // 当点击一个格子时 locked.set(格子对象, true) // 被锁定了 思路二、直接在鼠标上自定义一个属性来标记它的状态 ~~~ 使用时直接 对象.dataSet 即可 ~~~ 以下采用第二种 dataset 的方案 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfataw64i5j31k20r84qp.jpg) ## 实现04-如果锁定就不允许修改 在鼠标放上和挪开时判断是否被锁定: ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfatbn8xvgj30yo0v6nkr.jpg) ## 实现05-再次点击时解锁 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfatcg6889j30xs0os4hh.jpg) ~~~ <!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>        td {            width: 30px;            height: 30px;       }    </style> </head> ​ <body>    <div id="box"></div> ​    <script>        // 先获取 box 这个 div        let box = document.getElementById('box')        let num = prompt('请输入一个数字:')        // 创建格子 table        let table = document.createElement('table')        table.border = '1'        // 循环创建 n 个 tr        for (let i = 0; i < num; i++) {            let tr = document.createElement('tr')            // 在 tr 中添加 td            for (let j = 0; j < num; j++) {                let td = document.createElement('td')                // 把创建的 td 放到 tr 中                tr.appendChild(td)           }            // 把 tr 放到 table 中            table.appendChild(tr)       }        // 把 table 放到 box 中        box.appendChild(table) ​        // 为 table 绑定事件(事件委托技术:将事件绑定到父元素上)        // e:事件对象        // mouseover: 鼠标放上        table.addEventListener('mouseover', function (e) {            // e.srcElement: 触发这个事件的事件源            // 如果触发事件源是 TD 标签就改变背景色            if (e.srcElement.nodeName == 'TD') {                // 判断当前格子是否被锁定,如果没锁定就修改                if (e.srcElement.dataset.locked != 1) {                    e.srcElement.style.backgroundColor = 'red'               }           }       }) ​        // mouseout: 鼠标挪开        table.addEventListener('mouseout', function (e) {            // e.srcElement: 触发这个事件的事件源            // 如果触发事件源是 TD 标签就改变背景色            if (e.srcElement.nodeName == 'TD') {                // 判断当前格子是否被锁定,如果没锁定就修改                if (e.srcElement.dataset.locked != 1) {                    e.srcElement.style.backgroundColor = 'white'               }           }       }) ​        // click: 点击        table.addEventListener('click', function (e) {            // e.srcElement: 触发这个事件的事件源            // 如果触发事件源是 TD 标签就改变背景色            if (e.srcElement.nodeName == 'TD') {                e.srcElement.style.backgroundColor = 'green'                // 在这个 td 标签上标记一下,它已经被锁定了                e.srcElement.dataset.locked = 1           }       })    </script> </body> ​ </html> ~~~ ## 扩展:面向对象的写法 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfatcvg54gj30sw14ke81.jpg) # 題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) ~~~ 运行结果: ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfatdjyuljj30ks07iwhx.jpg) # 题3、随机颜色的 99 乘法表 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gfdyinbmsmj318e0ry4qp.jpg) 代码实现: ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg2fattij30nm11w4fe.jpg) # 题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万"   },   {       "image":   "https://imgcps.jd.com/ling/100008348542/5omL5py66LSt5a6e5oOg/5aSH6LSn6LaF5YC8/p-5bd8253082acdd181d02fa33/28403921/590x470.jpg",        "goods_name": "小米耳机",        "price": "135",        "comment": "1.4万"   } ] ~~~ ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg3oe4n6j31ji0tsqd9.jpg) OOP代码实现: 1. html 和 CSS ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg48wkhuj30b415u49q.jpg) 2. 购物车类 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg4pywpzj30fq0hw0zu.jpg) 3. 为类添加渲染数据的方法 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg5cmihyj30h60uuh3h.jpg) 4. 渲染购物车表格 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg5uf0grj30gy0lqqga.jpg) 5. 加入购物车 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg6b3zubj30mm0u2nia.jpg) 6. 删除 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg6pe5agj30is0iodq2.jpg) 7. 使用 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg72echij305y03y3z2.jpg) # 题5、表单验证 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg8aj405j30mi0iqabo.jpg) # 题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 制作完之后的效果: ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffg9x2edjj30ks0heacq.jpg) 代码实现: HTML 和 CSS ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffgal18wxj30ky0wuqf5.jpg) 封装原生 AJAX 为Promise 对象 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffgawuew3j30uu0fo10i.jpg) 两个辅导函数 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffgb8nuhuj30i0076whm.jpg) 点击搜索时调用接口获取某一页的数据 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffgbje5thj31g60wk4n3.jpg) 根据返回的数据渲染翻页按钮和数据列表 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffgbtwahpj30is0zwtnc.jpg) 搜索联想功能 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffgc6yqwuj30sq0waar8.jpg) 点击翻页按钮时重新调用接口 ![](http://ww1.sinaimg.cn/large/007WurYGgy1gffgcj3c1dj30jq0ai0yq.jpg) ~~~ <!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>        img {            max-width: 10%;       } ​        #suggestion {            position: absolute;            background-color: #eee;            width: 400px;       } ​        #suggestion div {            padding: 5px;       } ​        #suggestion div:hover {            background-color: skyblue;       } ​        #page {            list-style: none;       } ​        #page li {            display: inline-block;            border: 1px solid skyblue;            padding: 5px 10px;            margin: 5px;       } ​        #page li:hover,        #page li.active {            background-color: red;            color: white;            cursor: pointer;       }    </style> </head> ​ <body>    <!-- autocomplete: 关闭浏览器显示的提示 -->    <input type="text" autocomplete="off" id="keywords" placeholder="请输入内容">    <button id="btn">搜索</button>    <!-- 搜索联想 -->    <div id="suggestion"></div>    <!-- 数据列表 -->    <ul id="list"></ul>    <!-- 翻页 -->    <ul id="page"></ul> ​    <script>        // 封装原生 Ajax 为 Promise 对象        const axios = {            _xhr: new XMLHttpRequest(),            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', url)                    this._xhr.send()               })           }       } ​        // 辅导函数        function $(id) {            return document.getElementById(id)       } ​        function C(tag) {            return document.createElement(tag)       } ​        // 获取页面中的几个元素        const btn = $('btn')        const keywords = $("keywords")        const list = $('list')        const suggestion = $("suggestion")        const page = $('page') ​        // 当前页码        let pageNum = 1 ​        // 搜索按钮        btn.onclick = function () {            // 清空联想框            suggestion.innerHTML = ''            // 获取输入框中的关键字并去掉左右空格            let value = keywords.value.trim()            if (value === '') {                alert('必须要输入关键字!!!')                return false           }            // 调用接口取数据            axios.get(`http://ttapi.research.itcast.cn/app/v1_0/search?q=${value}&page=${pageNum}&per_page=15`)               .then(res => {                    if (res.message != 'OK') return alert('接口出错')                    // 渲染数据                    render(res.data.results)                    // 渲染翻页                    renderPage(res.data.total_count)               }) ​            // 渲染数据            function render(data) {                // 清空原数据                list.innerHTML = ''                // 循环渲染                let li, div, img                data.forEach(v => {                    li = C('li')                    div = C('div')                    div.innerHTML = v.title ​                    li.appendChild(div)                    v.cover.images.forEach(k => {                        img = C('img')                        img.src = k                        li.appendChild(img)                   });                    // li 放到页面                    list.appendChild(li)               });           } ​            // 渲染翻页页码            function renderPage(totalCount) {                // 清空原页码                page.innerHTML = '' ​                // 计算总的页数 = 向上取整(总的记录数 / 每页的条数)                let pageCount = Math.ceil(totalCount / 15) ​                // 循环创建按钮                let li                for (let i = 0; i < pageCount; i++) {                    // 创建一个按钮                    li = C('li')                    li.innerText = i                    // 如果是当前页就添加 active 这个类                    if (pageNum == i) {                        // 修改标签的 class 有两种写法: className 和 classList                        // li.className = 'active'                        li.classList.add('active')                   }                    // 放到页面中                    page.appendChild(li)               }           }       } ​        // 当搜索框中的值改变时        keywords.onkeyup = function () {            // 获取输入框中的关键字并去掉左右空格            let value = keywords.value.trim()            if (value === '') {                return false           } ​            // 调用联想接口 取数据            axios.get(`http://ttapi.research.itcast.cn/app/v1_0/suggestion?q=${value}`).then(res => {                if (res.message == 'OK') {                    // 渲染数据                    // 先清空上次数据                    suggestion.innerHTML = ''                    let div                    // 循环数据                    res.data.options.forEach(v => {                        // 创建div                        div = C('div')                        div.innerHTML = v                        // 放进去                        suggestion.appendChild(div)                   })               }           })       } ​        // 为联想的选项设置点击事件        // 我们可以直接为所有选项的父元素设置点击事件(事件委托)        // 因为事件是会冒泡的,点击子元素时,事件会传递给父元素        // 所以可以由这个父元素统一处理所有子元素的点击事件        // e.srcElmenet 代表:具体触发点击事件的子元素(事件源)        suggestion.onclick = function (e) {            // 获取点击的 div 的内容            let content = e.srcElement.innerText            // 把内容放到搜索框中            keywords.value = content            // 隐藏联想框            suggestion.innerHTML = ''            // 直接触发搜索按钮的点击事件            btn.onclick()       } ​        // 为所有的翻页按钮绑定点击事件,这里我们使用事件委托,把事件绑定给父元素        page.onclick = function (e) {            // 如果点击的是一个 li 标签就执行            if (e.srcElement.nodeName === 'LI') {                // 如果点击的页码不是当前页                if (pageNum !== e.srcElement.innerText) {                    // 修改当前页为点击的按钮的页码                    pageNum = e.srcElement.innerText                    // console.log(e.srcElement.innerText);                    // 重要根据页码调用接口获取数据                    btn.onclick()               }           }       }    </script> </body> ​ </html> ~~~