WEB APIs day5

在这里插入图片描述在这里插入图片描述

一、window对象

BOM属于window对象
在这里插入图片描述

1.BOM(浏览器对象模型)

在这里插入图片描述
bom里面包含着dom,只不过bom我们平时用得比较少,我们经常使用的是dom操作,因为我们页面中的这些标签都是在dom中取的,所以我们操作dom多一点。
在这里插入图片描述
window对象里面dom是核心(document object model),当然window对象里面除了dom对象之外还有很多其它很多对象(如navigator(显示你用的什么浏览器的),location(location有很多的方法,比如说我们的可以通过js的形式跳转页面,下载xx的时候不用写个a,写一个普通盒子就行了,因为我们js也可以跳转页面,实现下载的功能的,通过js的方式跳转页面。因为我们有的情况下是让它自动跳转页面,比如说我注册成功了,我让它5s之后自动跳转到首页,是自动跳转,不需要a链接来点击),history(历史对象),screen(屏幕)。
最大的是window,其次是document。
alert其实是window.alert(),console.log其实是window.console.log()。基本上我们所有的对象都是基于window的,所以大多数情况下可以省略。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>bom</title>
</head><body><script>// document.querySelector()// 按理说这个更合适,因为一层一层的去找,但是我们都知道window是最大的,没有比它更大的了,所以window是可以省略的 // window.document.querySelector()// 验证一下console.log(document === window.document)// 我们声明的这些函数或者全局变量,其实都挂在window上,我们调用的时候其实是window.fn()function fn() {console.log(11)}window.fn()// 我们全局声明的变量也是如此// var声明的是挂在window上,不是const,const不行// const num = 10// 所以var或者函数声明的这一些,默认都挂在window下,const和let就不是,因为它们是挂在自己的作用域内var num = 10console.log(window.num)</script>
</body></html>

2.定时器—延时函数

在这里插入图片描述
这个定时器和之前我们学的定时器interval不同,这个定时器只执行一次,(比如说弹出的一个小广告,显示几秒之后自动关闭,关闭之后就不再出来了),而之前学的定时器是一直执行的,(比如说轮播图)。第一个定时器是间歇定时器,间隔多少秒执行一次,而延时是多少秒之后执行一次,然后就不再执行了。和前面间歇函数的写法一模一样,只是名字和功能不一样而已。
定时器有两种,间歇(setInterval)和延时(setTimeout)
就执行一次为什么要清除?因为延时函数后面会有一些特殊的使用方法,它可以借助一种函数,叫递归函数,自己调自己可以来模拟定时器,但是自己调自己很容易把自己调晕,隔多少秒之后就来执行一次,执行完之后再回来调一次自己,又开始了。所以在特殊情况下我们需要清除。
平时我们只用一次的就不用去清除了,但是在某些特殊情况下,比如说setTimeout自己调自己,用到递归函数的时候我们就必须要清除了。这个后面会讲。
按道理,定时器都能开启和清除。setInteval返回的值是数字型的id(1、2…是第几个定时器),setTimeout返回的也是一个id(等待时间之后再回来执行回调函数)
在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>
</head><body><script>// 到了2s之后打印1次之后就不会再继续打印了setTimeout(function () {console.log('时间到了')}, 2000)</script>
</body></html>

在这里插入图片描述

案例

在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>5秒自动关闭的广告</title><style>img {position: fixed;left: 0;bottom: 0;}</style>
</head><body><img src="./images/ad.png" alt=""><script>// 1.获取元素  将图片获取过来const img = document.querySelector('img')// 2.开启定时器setTimeout(function () {img.style.display = 'none'}, 3000)</script>
</body></html>

3.JS执行机制

JS的执行机制:JS到底是怎么执行的。首先JS代码是从上往下执行的,第二,我们知道JS要解析,JS有JS解析器,浏览器有2个引擎,第一个引擎叫渲染引擎,可以渲染html,css,第2个引擎是用来解析JS的,叫JS解析器。在谷歌浏览器里面内置了一个V8引擎,是我们浏览器的一个,这个V8引擎是专门用来解析我们的JS的。
在这里插入图片描述
2个的结果都是1111,3333,2222
在这里插入图片描述
单线程有一个特点,比如说有一个元素,要进行删除操作,必须要先添加再删除,不能还没有就删除,不能同时进行添加和删除操作,这就是js的特点,从上往下依次执行,只能单线程不能多线程。单线程意味着我们所有的任务需要排队,前面一个任务结束后面一个任务才会进行,如果JS的执行时间过长,这样就会造成页面的渲染不连贯。
在这里插入图片描述
如果遇到上面那种耗时的操作,造成页面的阻塞,js有解决这个问题,利用了多核CPU的计算能力。JS本来是单线程的,但是我们为了解决这个问题,我们可以创建多个线程,所以这里JS出现了同步和异步。
在这里插入图片描述
定时器是异步任务,因为它是耗时的,哪怕是耗0s也是耗时的。耗时的任务都是异步任务,异步任务放在任务队列中的。(需要注意的是,我们JS是没有队列和栈的,只是借助其他语言的队列和栈来更好的理解我们这门语言)
在这里插入图片描述
什么是事件循环?(去面试一般都会问到)
就是整个执行栈里面执行完了之后,就去任务队列里面看,把任务队列里面的拿过来再执行,执行栈里面的这个异步任务执行完之后就又去任务队列里面拿,如果再有就再拿过来执行,反复这样转圈,就是一个循环。这个我们称之为事件循环。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>
</head><body><script>console.log(1)console.log(2)setTimeout(function () {console.log(3)})console.log(4)</script>
</body></html>

JS是单线程的,没有办法处理多线程,但是浏览器可以处理多线程。
在这里插入图片描述
先把1放入执行栈,再把2放入执行栈,因为同步任务是立马就执行完的,所以先打印1,再打印2。这个点击和定时器都耗时,是异步任务,所以JS就不处理了,提交给浏览器处理,将这两个异步事件放到浏览器,浏览器实时监测是先点击还是时间先到,假如是时间先到,就提交给任务队列,执行栈在执行完同步任务之后就一直在等待,所以当在任务队列里面看到时间之后就先执行时间,如果一直没点击就不执行点击事件(1234)。如果在执行完1、2之后,发现先点击了,就先把点击推过去,然后执行栈再取过来执行,之后时间到了才是时间(1243)
在这里插入图片描述

4. location对象

在这里插入图片描述在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>location</title>
</head><body><script>// console.log(window.location)// console.log(location)// console.log(location.href)// 不一定点链接让页面跳转,页面也可以自动跳转// 1. href 经常用href 利用js的方法跳转页面// 既然是属性,那我们就可以赋值// 最典型的一个使用场景是,当我注册成功之后,会提示5s之后自动跳转回首页(不需要点击,自动跳转,这是location.href的使用)location.href = 'http://www.baidu.com'</script>
</body></html>

在这里插入图片描述
这个经常见于注册一个账号,注册成功后几秒后自动跳转回首页,或者支付完之后多少秒跳转回首页。
我们应该用setInterval而不是setTimeout,因为里面的数字在变每隔1s就会换一次。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>5秒钟之后自动跳转页面</title><style>span {color: red;}</style>
</head><body><a href="http://www.itcast.cn">支付成功<span>5</span>秒钟之后跳转到首页</a><script>// 1. 获取元素const a = document.querySelector('a')// 2. 开启定时器// 3. 声明倒计时变量let num = 5// timerId里面存的是一个序号 let timerId = setInterval(function () {num--// 上来之后会立即变成4吗?不会,因为定时器1s之后才会调用这些代码a.innerHTML = `支付成功<span>${num}</span>秒钟之后跳转到首页`// 倒计时成了0就应该跳转了,并且关闭定时器// timeId里面存的是序号// 如果num === 0 则停止定时器,并且完成跳转功能if (num === 0) {// 没有名字清除不了clearInterval(timerId)// 4. 跳转  location.hreflocation.href = 'http://www.itcast.cn'}}, 1000)</script>
</body></html>

在这里插入图片描述

在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>location属性和方法</title>
</head><body><!-- 表单一定要有name,不然提交不了数据 --><form action=""><input type="text" name="username"><input type="password" name="pwd"><button>提交</button></form><!-- 只有那一块变了,后面vue会学到 点击之后是my就把my显示过来,是friend就把friend显示过来 --><!-- 用哈希拿到#后面的值 知道是点的哪一个 --><a href="#/my">我的</a><a href="#/friend">关注</a><a href="#/download">下载</a><button class="reload">刷新</button><!--location 除了那些属性外,还有一个方法 --><script>const reload = document.querySelector('.reload')reload.addEventListener('click', function () {// 类似于按下F5 刷新页面// location.reload()// 强制刷新  ctrl+f5location.reload(true)})</script>
</body></html>

在这里插入图片描述

在这里插入图片描述
spa单页面应用程序,有很多都是一个页面,它只是其中一块变了,别的没变,上面没刷新。这种应用后面会学,因为它是一种非常好用的应用,因为它不需要刷新整个页面,加载资源就会比较少,这种应用页面没有变,它没有跳转页面就可以实现页面跳转,它的实现原理是它是通过#后面的值来发生变化来发生判断的,所以我们在获取的时候一定要拿到#后面的值,后面还会讲。我们可以通过哈希来拿到这个值。在这里插入图片描述
在这里插入图片描述
true强制执行,比如说有些网页上面的小图标,favicon已经引进来了,但是图标一直不出来,刷新也不管用,这时候可以ctrl+f5强制刷新,所谓强制刷新就是不从本地里面去取数据了,而是在线重新渲染重新拉下来这些数据
在这里插入图片描述在这里插入图片描述

5. navigator对象

在这里插入图片描述
比如京东的页面,PC端打开就是PC端,移动端打开就是移动端。
这个代码不需要我们写,正则都出来了,不用去背,要用的时候直接cv就行。
如果是手机端跳转到http://m.itcast.cn上去,如果是pc端就不跳转。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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><!-- 拿过来之后直接放在最上面就行了 --><!-- 这是立即函数加了一个! --><script>// 检测 userAgent(浏览器信息)!(function () {const userAgent = navigator.userAgent// 验证是否为Android或iPhoneconst android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)// 如果是Android或iPhone,则跳转至移动站点if (android || iphone) {// 只需要把它换成自己想要的地址就可以了location.href = 'http://m.itcast.cn'}})()// 写在上面也可以// 最后一个要加;// (function () { })();    这个前面加不加!无所谓// 还有人喜欢这么写:这样写是错误的,因为JS不会认为这是一个整体,它会认为是调用的一个function函数// function(){}()// 但是如果加一个!或者+或者~等等,只要是一个算术表达式都能的,它就会把前面当做一个整体了// !function () { }()</script>
</head><body>这是PC端的页面<!--  <script>// (function () { })()</script> -->
</body></html>

6. history对象

在这里插入图片描述在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>history对象</title>
</head><body><button>后退</button><button>前进</button><script>const back = document.querySelector('button:first-child')// back的下一个兄弟const forward = back.nextElementSibling// 回调函数back.addEventListener('click', function () {// 后退一步// 怎样实现后退?复制有前进后退按钮的地址到一个页面中打开即可// history.back()// 单词难记,推出了更好记的方法history.go(-1)})forward.addEventListener('click', function () {// 前进一步// 要是前进不了是因为没有再打开一个新的页面,在前进后退按钮页面再打开另外一个地址// history.forward()history.go(1)// location.reload可以刷新页面})</script>
</body></html>

二、本地存储

在这里插入图片描述

1. 本地存储介绍

在这里插入图片描述
本地存储是一个仓库,用于存放数据,你可以理解为“数据库”,但是这个数据库比较特殊,是放在浏览器里面的。以前我们也有一个小仓库存放数据,叫变量,变量是放在内存的,内存有一个特点,一刷新就没了,但是有的数据我们不想丢失,所以我们在浏览器里面添加了一个小仓库,用于存放数据,只要浏览器在它就在,数据不会丢失。
本地存储一共有两个,sessionStorage和localStorage,每个都约5M左右。在这里插入图片描述https://todomvc.com/examples/vanilla-es6/在这里插入图片描述

2. 本地存储分类

在这里插入图片描述
大多数时候是可以共享的,当然也有不能的情况,当我们讲ajax的时候,它如果涉及到跨域的问题,是不能访问的,后面会讲。
在这里插入图片描述在这里插入图片描述在这里插入图片描述
另外打开一个页面,跟刚才的页面没有任何关系,发现有storage但是发现没有这些数据,原因是我们没有学过跨域,比如说京东和百度的数据肯定是不能共享的,他有不同的域名,前面的域名不相同是不能取的。当我们打开history对象这个代码的页面,F12点击storage(我们还没有讲跨域,讲了跨域之后就懂了)

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述我们发现打印出的18是字符型的。由此可知,本地存储只能存储字符串数据类型,不管什么类型都给你转换成字符串类型。这个跟变量不一样。在这里插入图片描述
localStorage关闭浏览器不会消失,sessionStorage关闭浏览器就消失。和变量的区别是sessionStorage也是存到仓库里面去的,只不过它关闭浏览器就会消失。sessionStorage和localStorage的用法一致(存,获取,删除,改)。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>localStorage存储</title>
</head><body><script>// 要存储一个名字 uname// 键值对要加引号,不加引号就当成变量来看了,是个字符串   //属性名 值localStorage.setItem('uname', 'pink老师')// 2. 获取方式  都加引号console.log(localStorage.getItem('uname'))// 3. 删除本地存储  只删除名字 (手动删除)// 能打印:因为是先存再打印最后再删除的// localStorage.removeItem('uname')// 4. 改   (如果原来有这个键,则是改,如果没有这个键则是增)localStorage.setItem('uname', 'black')// 我要存一个年龄   18不用加引号,因为是数字型localStorage.setItem('age', 18)// 除了数字型,其他的都要加引号// 实在记不住,全部都加上引号也行// 我们发现打印出的18是字符型的    由此可知,本地存储只能存储字符串数据类型,不管什么类型都给你转换成字符串类型console.log(localStorage.getItem('age'))// sessionStorage和localStorage的用法一致</script>
</body></html>

在这里插入图片描述

3. 存储复杂数据类型

在这里插入图片描述
一个一个地存太麻烦,考虑存对象或者数组的形式
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>
</head><body><script>const obj = {uname: 'pink老师',age: 18,gender: '女'}/* //存储 复杂数据类型// 第一个obj是键     第二个obj是值(因为是变量了所以就不加引号了)localStorage.setItem('obj', obj)   //直接采取这种格式我们得到的是[obj][obj]这种格式,为解决这个问题,我们把复杂数据类型转换成JSON字符串/*//这样也可,只不过上面那种更好localStorage.setItem('obj', {uname: 'pink老师',age: 18,gender: '女'})*/// 取 */// console.log(localStorage.getItem('obj'))// 不能像上面那么写   我们应该转换完了再存// 1. 复杂数据类型存储必须转换为JSON字符串存储// 因为要转换所以JSON.stringfy()是个方法// 这个代码会把对象转换为字符串存进去,也可以直接把obj里面的内容直接复制到方法的括号里面去localStorage.setItem('obj', JSON.stringify(obj))// 在本地存储里面只能存储字符串// 取// 打印出来是黑色的 所以是字符串而不是对象// console.log(localStorage.getItem('obj'))// 也可以检测一下是不是字符串console.log(typeof localStorage.getItem('obj'))// JSON对象  特点:属性和值有引号,而且引号统一是双引号// {"uname":"pink老师","age":18,"gender":"女"}   //看着像对象但它已经不是对象了,它是字符串// 我们取过来的是字符串,不能直接用,这样就拿不到数据(uname,age这些)了,那么反过来,我们把这个字符串转化成对象,以后就可以拿过来数据直接渲染了// 2. 把JSON字符串转换为对象// 先获取再转换console.log(JSON.parse(localStorage.getItem('obj')))/* // 如果觉得看起来挺费劲,可以这样const str = localStorage.getItem('obj')console.log(JSON.parse(str)) */</script>
</body></html>

三、综合案例在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

对象数组,因为数据有很多条,所以我们要把对象放到数组里面去
在这里插入图片描述
(2). 如果没有数据,则用 空数组来代替 的具体实施:
逻辑或中断,如果有数据就把数据存进去,返回的就是数组,数组不为空,那就是真的,根据逻辑或中断的原则,如果前面为真,则后面就不执行了,但是如果把本地存储里面的数据清空,那它里面就没有内容,没有内容就为空,空就是假,取出一个空字符串,根据逻辑或中断的原则,前面为假则执行后面的,那么就把后面的空数组返回过来了。
我们可以把存储数据注释起来看看效果。
根据本地存储的原则,即使已经注释起来了,但是刚才已经存过了,所以也有,我们可以把它清空,本地存储就没有数据了,重新刷新一下页面,再打开控制台,就会发现返回的是一个空数组,那说明我们写的程序是正确的,因为一会儿我们渲染页面,如果数组里面有数据,就根据数组里面的数据渲染页面,如果数组是空的,那么渲染的页面也就是空的页面。
在这里插入图片描述
前面我们已经拿到了数据,接下来我们根据数据渲染页面,比如说我有几条数据就可以在下面渲染几行
在这里插入图片描述
以前我们用字符串拼接str+=这种效率并不高,而我们以后的开发中利用的是map和join这两种数组方法来实现字符串拼接,这种的效率会更高一些,那么就意味着从今天开始以后所有的渲染页面拼接字符串统一使用数组的这两个方法来实现。因为这个方法需要以前拼接字符串str方式的铺垫,前面的学习了后面再学习这个会更简单。在这里插入图片描述
1.map的语法格式:map里面有一个回调函数,回调函数里面有2个参数,这个写法和for each很像,ele(element)是数组元素,index是数组索引号,只不过map在使用的时候可以添加下面这一句话:return ele + ‘颜色’,它的意思是让数组的每个元素进行添加颜色,每循环一次就把当前的结果返回过来,一共有3个数组元素所以循环了3次,那拼接完,map最大的特点是有返回值,返回新的数组,现在我们通过new arr把数组拿过来了,返回的就是把数据处理之后的结果,并且放到了一个数组里面。

2.map不仅可以遍历数组还可以处理数据,比如说可以让数组里的每一个数据拼接字符串,第一次循环让red加上颜色,第二次循环让blue加上颜色…每次循环都有一个返回值返回到了数组。它的本质是这样子:只要用到了map,它的返回值就是数组,先准备一个空数组,第一次循环就可以把red颜色返回过来,第二次循环就是blue颜色…,这样的话整个map执行完就会生成一个新的数组,然后我们就可以拿到这个数组了。
在这里插入图片描述
在这里插入图片描述
分析:我们前面已经能够拿到数据了 ,数组里面放着相关对象,然后我们可以根据里面有几条数据生成几个tr。我们可以这么去做,可以用map去遍历数组,看看有几个对象,然后根据对象去处理数据,也就是说可以生成tr,在生成tr的同时我们就就可以把拿到的数据的值放到相应的位置,也就是说我在生成tr的时候,把数据填充进去。第一次循环就生成一个tr,但是如果数组里面有多条数据,那么每次循环都有一个返回值放到数组里面去再生成第二个tr,所以map方法最大的特点就是可以生成对应数据的tr,然后再把数据填充进去。我们知道map方法最大的特点是有一个数组返回给我们,等到map全部遍历完了就根据你有几条数据返回对应的数组,所以map有一个数组返回给我们了,但是虽然里面有相应的tr了,它毕竟是个数组,我们没有办法把数组直接追加给tbody,所以我们还需要做一步,通过join的方法把数组转换成字符串,也就是说我们要的不是数组是字符串。我有2个tr全都连在一起就形成了一个字符串,我们最后就可以把这个字符串追加给tbody,有了这些tr页面之中就可以把它渲染出来了。这个就是我们说的通过map和join方法实现渲染页面的一个思路了。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><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>数组map方法和join方法</title>
</head><body><script>const arr = ['red', 'blue', 'pink']// 1.数组map方法  处理数据并且返回一个数组// 不要用map方法来遍历数组,不然就违背了我们设计map方法的初衷了。map方法设计的主要原则是我想要得到一个数组返回一个数组,就用map。在进行map遍历的时候,可以让它处理一些数据,然后再把处理的结果返回给我,这才是map的正确处理方法。const newArr = arr.map(function (ele, index) {// console.log(ele)   // 数组元素// console.log(index)  // 索引号return ele + '颜色'})console.log(newArr)// 2. 数组join方法 把数组转换为字符串// 小括号为空则默认逗号分隔,因为我们都知道数组默认逗号分隔console.log(newArr.join())   // red颜色,blue颜色,pink颜色// 小括号是空字符串,则元素之间没有分隔符console.log(newArr.join(''))  // red颜色blue颜色pink颜色console.log(newArr.join('|'))  // red颜色|blue颜色|pink颜色</script>
</body></html>

步骤:
因为我们要做渲染,所以我们还是封装成一个渲染函数叫做render,因为我们后面不管是增加也好还是删除也好需要重新渲染页面,所以我们封装成一个函数。
把字符串赋值给tbody之后,tbody就可以把tr显示出来了。
在这里插入图片描述在这里插入图片描述
前面已经把数据存到本地存储里面,本地存储有数据,有数据我们就可以通过map的方法来遍历arr数组,在遍历的同时就可以return返回tr,返回字符串,在返回的同时把里面的相关数据换成了活的数据,map最大的特点是返回的是一个数组,那么就把return后面的东西返回给了数组装起来了,简单的来说我们有一个数组,return就把tr拿过来放到数组里面,这样我们就可以拿到数组了,经过刚才的验证我们发现数组里面确实存的是一个tr的字符串。
先把render函数封装一下,然后通过map方法拿到返回值的数组,接下来只需要把数组转换成字符串就可以追加给tbody了。在上面先把tbody获取一下。
共有数据几条:是根据数组的长度得来的,数组里面有几条数据就变成几,所以我们只需要把盒子拿过来,把它的值改成数组的长度就行了。在这里插入图片描述
在这里插入图片描述在这里插入图片描述
点击添加没有实现跳转,说明阻止默认行为成功了,接下来做非空判断,如果前3个为空就不允许增加数据。终止程序,并且返回一个提示框。首先把相关的数据获取过来,在提交事件里面。
当我们新增一条数据的时候一定要把它存储到本地存储里面,不然页面一刷新数据就丢失了。当它存储完毕之后,就可以拿到本地存储的最新数据,再去渲染我们的页面,这样页面就会多一条数据,又因为本地存储的数据不会丢失,所以再刷新,页面中的数据就不会消失了 。
在这里插入图片描述
页面一刷新,代码从上往下去执行,我们的初始值已经没有用到了,刚开始的本地存储一定要注释起来,要不然我们刚才录入的数据就会被覆盖掉了,页面就只剩初始的那一条数据了。这句话只是为了测试用的。(localStorage.setItem(‘data’, JSON.stringify(initData)))
在这里插入图片描述
这句话一读到的时候,我就要去本地存储里面读最新的data数据,我们前面已经把那两条数据存到data里面去了,那arr取过来的就是最新的那两条数据,有数据,所以后面的空数组不执行。然后代码从上往下继续,渲染函数刚开始不执行,但是下面有一个渲染函数,所以就回头调用,一回头调用我们就拿arr里面的两条数据来做渲染,所以只要往本地存储里面去存储了,那本地存储里面存的就是最新的数据,然后每次刷新页面,我就要从本地存储里面取最新的数据然后赋值给arr,那arr就可以来进行遍历渲染了,这就是它的执行过程,所以我们在做提交事件的时候,千万不要忘了最后一条:把最新的arr存到data里面,新增业务完成。

删除业务
为什么给tbody注册点击事件呢?因为我们会有新的数据产生,如果直接给按钮注册点击事件,我们用原先传统的方式是注册不上的,而 事件委托可以给新增的这些数据添加点击事件,又因为按钮有行都会变化的,而tbody是不变的,所以我们给他们的父亲tbody注册点击事件。这么多按钮,如何知道点了哪一条数据呢?知道当前按钮的索引号就可以了。map里面除了有一个ele参数以外,还有一个index可以得到数组元素的索引号,所以在遍历的时候就可以得到每一条数据的索引号了,现在只需给a加上一个自定义属性就可以了
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
没有数据arr.length就是0,0当假来看,则执行表达式2的那一个值,如果里面有2条数据,数字型的2当真看,则执行表达式1的结果,把结果返回过来再赋值给stuId。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta http-equiv="X-UA-Compatible" content="ie=edge" /><title>学生就业统计表</title><link rel="stylesheet" href="./iconfont/iconfont.css"><link rel="stylesheet" href="css/index.css" />
</head><body><h1>学生就业统计表</h1><form class="info" autocomplete="off"><input type="text" class="uname" name="uname" placeholder="姓名" /><input type="text" class="age" name="age" placeholder="年龄" /><input type="text" class="salary" name="salary" placeholder="薪资" /><select name="gender" class="gender"><option value=""></option><option value=""></option></select><select name="city" class="city"><option value="北京">北京</option><option value="上海">上海</option><option value="广州">广州</option><option value="深圳">深圳</option><option value="曹县">曹县</option></select><button class="add"><i class="iconfont icon-tianjia"></i>添加</button></form><div class="title">共有数据<span>0</span></div><table><thead><tr><th>ID</th><th>姓名</th><th>年龄</th><th>性别</th><th>薪资</th><th>就业城市</th><th>录入时间</th><th>操作</th></tr></thead><tbody><!-- <tr><td>1</td><td>迪丽热巴</td><td>23</td><td>女</td><td>12000</td><td>北京</td><td>2099/9/9 08:08:08</td><td><a href="javascript:"><i class="iconfont icon-shanchu"></i>删除</a></td></tr> --></tbody></table><script>// 参考数据const initData = [{stuId: 1,uname: '迪丽热巴',age: 22,salary: '12000',gender: '女',city: '北京',time: '2099/9/9 08:08:08'}]// 这里是为了测试,先存了一下,我们看到我们正确的拿到了数据,但是建议把这一行一定要把它注释起来,因为我们页面第一次打开肯定是没有数据的,所以我们拿个空数组去代替是没有问题的,后面做增加等业务的时候就可以把最新的数据放到本地存储里面,这样再打开页面的时候就可以从本地存储里面取真正的数据了。// 页面一打开从上往下依次去执行,这里有一个对象数组,下面一句话把这个数据存到本地存储里面,那就意味着我们每次打开页面都会重新存储一下这个data数据,把它存储到本地存储里面去,如果后面的数据发生了变化,我们一打开就又给我们覆盖掉了,所以一定要把下面这句话注释起来,因为本地存储已经存到浏览器里面去了,本地存储(aplication)一直都是存在的,所以我们没有必要打开页面又重新去填写了// localStorage.setItem('data', JSON.stringify(initData))   // 把这个解开之后就可以往本地存储里面去存这个数组了,本地存储有了数据我们就可以来遍历这条数据了// 1. 渲染业务// 1.1 先读取本地存储的数据// (1). 本地存储有数据则记得转换为对象然后存储到变量里面,后期用于渲染页面// 我们数组本质上也属于对象// (2). 如果没有数据,则用 空数组来代替 const arr = JSON.parse(localStorage.getItem('data')) || []console.log(arr)// 1.2 利用map和join方法来渲染页面const tbody = document.querySelector('tbody')// 渲染页面需要封装一个函数function render() {// (1). 利用map遍历数组,返回对应tr的数组// 每循环一次就返回一个tr,在返回的同时把相关的数据填充进去// map里面的第一个参数是ele(数组元素),  里面的每一条数组元素,也就是数组对象// map在遍历数组,结果发现只有一条数据它就会这么做,map一旦遍历就会有一个空数组等着map的返回值,一运行的时候就发现有一个return,把tr返回,在把tr返回的同时把里面的数据更换,所以旧表整个tr装到了数组里面,因为只有一条数据,所以只遍历一次就结束了,那我们就可以得到一个新的数组,那我们就可以把这个数组拿到了// map的另一个参数:index,可以得到数组元素的索引号,所以我在遍历的时候就可以拿到每一条数据的索引号了,现在只需要给a加一个自定义属性,data-id = 索引号const trArr = arr.map(function (ele, index) {return `<tr><td>${ele.stuId}</td><td>${ele.uname}</td><td>${ele.age}</td><td>${ele.gender}</td><td>${ele.salary}</td><td>${ele.city}</td><td>${ele.time}</td><td><a href="javascript:" data-id="${index}"><i class="iconfont icon-shanchu"></i>删除</a></td></tr>`})console.log(trArr)// (2). 把数组转换为字符串 (join)// (3). 把生成的字符串换追加给tbody// 把多个tr转换成一个字符串tbody.innerHTML = trArr.join('')// 显示共有几条数据document.querySelector('.title span').innerHTML = arr.length}// 在打印的时候千万别忘了调用一下render函数render()// 到浏览器里面看看能不能拿到数组里面存着的tr,发现输出的是两个空数组,可能有一个情况是本地存储里面没有拿到数据,我没有遍历到,所以没有办法 往里面去存数据。到应用里面看看,果然本地存储里面没有数据,因为得到的是一个空数组,所以当然本地存储里面存不进去了,空数组没有办法遍历,所以没有办法加tr// 2. 新增业务// 2.1 form表单注册提交事件,阻止默认行为const info = document.querySelector('.info')const uname = document.querySelector('.uname')const age = document.querySelector('.age')const salary = document.querySelector('.salary')const gender = document.querySelector('.gender')const city = document.querySelector('.city')info.addEventListener('submit', function (e) {// 因为要阻止默认行为(跳转之类的),我们先不让它提交,所以要写个事件对象,然后去调用事件对象里面的方法e.preventDefault()// 2.2 非空判断if (!uname.value || !age.value || !salary.value) {return alert('输入内容不能为空')}// 2.3 给 arr 数组追加对象,里面存储表单获取过来的数据arr.push({// 新增一条新的数据,所以ID号就是原来的数组的长度+1,因为push是后追加// 这种做法不可取,因为如删除第一条数据之后再录入会出现ID号相同的情况// stuId: arr.length + 1,// 处理 stuId: 数组最后一条数据的stuId + 1// 还需考虑数据全部删除的情况// 全部删除之后再添加数据就报错了,因为我们数组里面没有数据,我们再用最后一条数据获取它的stuId就报错了,所以我们要加一个判断条件,如果没有数据,就直接让它赋值为1就行了 arr.length判断数组有没有长度stuId: arr.length ? arr[arr.length - 1].stuId + 1 : 1,uname: uname.value,age: age.value,salary: salary.value,gender: gender.value,city: city.value,// 用户录入了一条信息,它就自动获取当前的时间time: new Date().toLocaleString()})// 2.4 渲染页面和重置表单  (reset() 方法)render()// form表单是info,其实就是我们的this,因为this指向我们函数的调用者,也就是info。写this和info都可以this.reset()  // 重置表单(把表单输入框里面的值自动重置,恢复到原来的样子,清空内容)// 2.5 把数组重新存入本地存储里面,记得转换为JSON字符串存储// 因为我们已经有data这个数据了,一旦存了新的就把以前的覆盖掉了,实现了替换,这样就保证了data里面存的是最新的数据localStorage.setItem('data', JSON.stringify(arr))})// 3. 删除业务// 3.1 采用事件委托形式,给tbody注册点击事件tbody.addEventListener('click', function (e) {// 判断是否点击的是删除按钮  A链接if (e.target.tagName === 'A') {// alert(11)// 3.2 得到当前点击链接的索引号,渲染数据的时候,动态给a链接添加自定义属性例如 data-id="0"// 点击的那个对象有一个自定义属性叫data-id,这样可以拿到当前的索引号console.log(e.target.dataset.id)// 确认框确认是否要真的删除// confirm会返回两个值,如果点击确定就返回true,true就表示我要执行里面的代码,不用做删除,如果用户点击的是取消就返回false,就不执行大括号里面的代码if (confirm('你确定要删除这条数据吗?')) {// 3.3 根据索引号,利用 splice 删除数组这条数据arr.splice(e.target.dataset.id, 1)// 3.4 重新渲染页面render()// 3.5 把最新 arr 数组存入本地存储// 不然删了之后本地存储里面也还是有数据localStorage.setItem('data', JSON.stringify(arr))}}})</script>
</body></html>

下面这个案例未找到

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/104829.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Go版]算法通关村第十三关青铜——数字数学问题之统计问题、溢出问题、进制问题

这里写自定义目录标题 数字统计专题题目&#xff1a;数组元素积的符号思路分析&#xff1a;无需真计算&#xff0c;只需判断负数个数是奇是偶复杂度&#xff1a;时间复杂度 O ( n ) O(n) O(n)、空间复杂度 O ( 1 ) O(1) O(1)Go代码 题目&#xff1a;阶乘尾数0的个数思路分析&am…

CPU、MCU、MPU、SOC、SOCPC、概念解释之在嵌入式领域常听到的名词含义

CPU、MCU、MPU、SOC等几个在嵌入式领域学习过程中会涉及到的几个名词。我们来学习一下&#xff0c;资料从网上搜集的&#xff0c;有错的地方可以指出。。。 CPU、MCU、MPU、SOC、SOCPC、 1. CPU2. MPU3.MCUMPU和MCU的区别&#xff1a;4.SOC5. SoPC 1. CPU CPU&#xff0c;即中…

iis站点备份以及端口号查找

文件地址 %windir%\system32\inetsrv\config

iOS 17 及 Xcode 15.0 Beta7 问题记录

1、iOS 17 真机调试问题 iOS 17之后&#xff0c;真机调试Beta版本必须使用Beta版本的Xcode来调试&#xff0c;用以前复制DeviceSupport 方式无法调试&#xff0c;新的Beta版本Xcode中&#xff0c;已经不包含 iOS 17目录。如下图&#xff1a; 解决方案&#xff1a; 1&#x…

Nodejs-nrm:快速切换npm源 / npm官方源和其他自定义源之间切换

一、理解 Nodejs nrm Nodejs nrm 是一个管理 npm 源的工具。由于 npm 在国内的速度较慢&#xff0c;很多开发者会使用淘宝的 npm 镜像源&#xff0c;但是也会遇到一些问题&#xff0c;例如某些包在淘宝镜像源中不存在&#xff0c;或者淘宝镜像源本身也会有问题。 Nodejs nrm …

SpringCloud教程 | 第六篇: 分布式配置中心(Spring Cloud Config)

在上一篇文章讲述zuul的时候&#xff0c;已经提到过&#xff0c;使用配置服务来保存各个服务的配置文件。它就是Spring Cloud Config。 一、简介 在分布式系统中&#xff0c;由于服务数量巨多&#xff0c;为了方便服务配置文件统一管理&#xff0c;实时更新&#xff0c;所以需…

根据源码,模拟实现 RabbitMQ - 虚拟主机设计(5)

目录 一、虚拟主机设计 1.1、需求分析 1.1.1、核心 API 1.1.2、虚拟主机的是用来干什么的&#xff1f; 1.1.3、如何表示 交换机和虚拟主机 之间的从属关系&#xff1f; 二、实现 VirtualHost 类 2.1、属性 2.2、锁对象 2.3、公开实例 2.4、虚拟主机构造方法 2.5、交…

语言、习俗与成功:海外网红营销的文化敏感性

随着全球互联网的普及和社交媒体的兴起&#xff0c;海外网红营销正成为越来越多品牌和企业的选择。然而&#xff0c;要在海外市场取得成功&#xff0c;单纯的翻译内容和机械式的推广已经远远不够&#xff0c;文化敏感性成为海外网红营销的关键。本文Nox聚星将深入探讨语言、习俗…

算法与数据结构(十)--图的入门

一.图的定义和分类 定义&#xff1a;图是由一组顶点和一组能够将两个顶点连接的边组成的。 特殊的图&#xff1a; 1.自环&#xff1a;即一条连接一个顶点和其自身的边; 2.平行边&#xff1a;连接同一对顶点的两条边&#xff1b; 图的分类&#xff1a; 按照连接两个顶点的边的…

Linux 压缩解压(归档管理):tar命令

计算机中的数据经常需要备份&#xff0c;tar是Unix/Linux中最常用的备份工具&#xff0c;此命令可以把一系列文件归档到一个大文件中&#xff0c;也可以把档案文件解开以恢复数据。 tar使用格式 tar [参数] 打包文件名 文件 tar命令很特殊&#xff0c;其参数前面可以使用“-”&…

java八股文面试[java基础]——异常

自定义异常&#xff1a; 异常Exception 是指程序运行时&#xff0c; 由于输入错误、网络、程序逻辑等原因导致运行时出现的问题。出现异常时&#xff0c;程序会暂时中断执行&#xff0c;并根据产生异常的原因&#xff0c;创建对应异常类型的异常对象&#xff0c;并抛出给JVM捕…

七大排序算法详解

1.概念 1.排序的稳定性 常见的稳定的排序有三种&#xff1a;直接插入排序&#xff0c;冒泡排序&#xff0c;归并排序 对于一组数据元素排列&#xff0c;使用某种排序算法对它进行排序&#xff0c;若相同数据之间的前后位置排序后和未排序之前是相同的&#xff0c;我们就成这种…

数学建模(五)非线性规划

课程推荐&#xff1a; 13 非线性规划算法在数学建模中的应用与编程实现_哔哩哔哩_bilibili 一、非线性规划模型 如果目标函数或约束条件中包含非线性函数&#xff0c;就称这种规划问题为非线性规划问题。一般说来&#xff0c;解非线性规划要比解线性规划问题困难得多。而且&am…

cs231n assignment3 q4 Generative Adversarial Networks

文章目录 嫌墨迹直接看代码Q4 :Generative Adversarial Networkssample_noise题面解析代码输出 discriminator题面解析代码输出 generator题面解析代码输出 discriminator_loss题面解析代码输出 generator_loss题面解析代码 get_optimizer题面解析代码输出 ls_discriminator_lo…

c语言函数指针和指针函数的区别,以及回调函数的使用。

函数指针是什么&#xff0c;函数指针本质也是指针&#xff0c;不过是指向函数的指针&#xff0c;存储的是函数的地址。 指针函数是什么,指针函数其实就是返回值是指针的函数&#xff0c;本质是函数。 函数指针是如何定义的呢&#xff0c;如下 void (*pfun)(int a,int b) 这…

服务器的介绍

1.服务器概述 1.1 服务器的基本概念 服务器是计算机的一种&#xff0c;是网络中为客户端计算机提供各种服务的高性能计算机&#xff1b; 服务器在网络操作系统的控制下&#xff0c;将与其相连的硬盘、磁带、 打印机及昂贵的专用通讯设备提供给网络上的客户站点共享&#xf…

2023年国赛 高教社杯数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…

HTTP协议初识·上篇

目录 认识URL urlencode和urldecode 如何编码解码和验证过程 一个基本的网络服务器的流程 代码验证请求与响应 准备工作 HTTPServer.hpp Protocol.hpp makefile 1请求 HTTPServer.hpp 1.0函数handlerHttp-基本流程 再次处理 HttpServer.cc(新建文件) 测试1 -- 请…

iOS开发之查看静态库(.a/.framework)中包含的.o文件和函数符号(ar,nm命令)

.a/.framework其实是把编译生成的.o文件&#xff0c;打包成一个.a/.framework文件。a的意思是archive/归档的意思。 查看静态库.a文件包含的内容用下面的命令解压&#xff1a; ar x xxx.a 用ar命令打包静态库&#xff1a; 参数r是将后面的*.o或者*.a文件添加到目标文件中 参数…

031 - 浮点类型(近似值 FLOAT,DOUBLE)

-FLOAT&#xff0c;DOUBLE&#xff1a; FLOAT和DOUBLE类型代表近似数字数据值。MySQL将四个字节用于单精度值&#xff0c;并将八个字节用于双精度值。 对于FLOAT&#xff0c;SQL标准允许对FLOAT括号中的关键字后面的位以精度&#xff08;而不是指数的范围&#xff09;进行可选规…