WebApis知识总结以及案例(续3)

发布于:2024-04-18 ⋅ 阅读:(28) ⋅ 点赞:(0)

综合案例

小兔鲜页面注册

分析业务模块

  • 发送验证码模块

用户点击之后,显示05 秒后重新获取

时间到了,自动改为重新获取

    //1.发送短信验证码模块
    const code=document.querySelector('.code')
    let flag=true//通过一个变量来控制 节流阀 
    // 1.1 点击事件
    code.addEventListener('click',function(){
      if(flag){
        flag=false//时间未到点击之后不可触发事件
        let i=5
      // 点击完毕之后立马触发
      code.innerHTML=`0${i}秒后重新获取`

      let timerID=setInterval(function(){
        i--
        code.innerHTML=`0${i}秒后重新获取`
        if(i===0){
          clearInterval(timerID)
          flag=true//时间结束 点击之后可以触发事件
          // 重新获取
          code.innerHTML=`重新获取`
        }
      },1000)
      }
      
    })
  • 各个表单验证模块

用户名验证(注意封装函数 verifyxxx),失去焦点触发这个函数

  • 正则/^[a-zA-Z0-9-_]$/{6,16}
  • 如果不符合要求,则出现提示信息 并return false中断程序
  • 否则 则返回return true
  • 之所以返回布尔值 是为了最后的提交按钮做准备
  • 侦听使用change事件,当鼠标离开了表单,并且表单值发生了变化时触发(类似京东效果)
    const input =document.querySelector('input')
    input.addEventListener('change',function(){
      console.log(11);
    })
    // 2.验证用户名
    // 2.1获取用户名表单
    const username=document.querySelector('[name=username]')
    username.addEventListener('change',verfiyName)//函数名不加括号
    // 2.3封装vertifyName函数
    function verfiyName(){
      const span=username.nextElementSibling
      // console.log(11)
      // 2.4 定规则
      const reg=/^[a-zA-Z0-9-_]{6,16}$/
      if(!reg.test(username.value)){
        // console.log(11)
        span.innerText='输入不合法,请输入6~16位'
        return false
      }
      // 合法的 就清空span
      span.innerText=''
      return true
    }

手机号验证 

正则:/^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$/

其余同上

验证码验证

正则:/^\d{6}$/

其余同上

密码验证

正则:/^[a-zA-Z0-9-_]{6,20}$/

其余同上

再次密码验证

如果本次密码不等于上面输入的密码则返回错误信息

其余同上

我同意模块

添加类 .icon-queren2 则是默认选中样式,可以使用toggle切换类

表单提交模块

使用submit提交事件

  • 勾选已经阅读统一模块

如果没有勾选同意协议,则提示需要勾选

classList.contains()看看有没有包含某个类,如果有则返回true,没有则返回false

  • 下一步验证全部模块

只要上面有一个input验证不通过就不同意提交

如果上面input表单 只要有模块返回的是false 则阻止提交

    (function(){
      //1.发送短信验证码模块
    const code=document.querySelector('.code')
    let flag=true//通过一个变量来控制 节流阀 
    // 1.1 点击事件
    code.addEventListener('click',function(){
      if(flag){
        flag=false//时间未到点击之后不可触发事件
        let i=5
      // 点击完毕之后立马触发
      code.innerHTML=`0${i}秒后重新获取`

      let timerID=setInterval(function(){
        i--
        code.innerHTML=`0${i}秒后重新获取`
        if(i===0){
          clearInterval(timerID)
          flag=true//时间结束 点击之后可以触发事件
          // 重新获取
          code.innerHTML=`重新获取`
        }
      },1000)
      }
    })
    });
    
    // 2.验证用户名
    // 2.1获取用户名表单
    const username=document.querySelector('[name=username]')
    username.addEventListener('change',verfiyName)//函数名不加括号
    // 2.3封装vertifyName函数
    function verfiyName(){
      const span=username.nextElementSibling
      // console.log(11)
      // 2.4 定规则
      const reg=/^[a-zA-Z0-9-_]{6,16}$/
      if(!reg.test(username.value)){
        // console.log(11)
        span.innerText='输入不合法,请输入6~16位'
        return false
      }
      // 合法的 就清空span
      span.innerText=''
      return true
    }



    // 3.验证手机号
    // 2.1获取手机表单
    const phone=document.querySelector('[name=phone]')
    phone.addEventListener('change',verfiyPhone)//函数名不加括号
    // 2.3封装verfiyPhone函数
    function verfiyPhone(){
      const span=phone.nextElementSibling
      // console.log(11)
      // 2.4 定规则
      const reg=/^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$/
      if(!reg.test(phone.value)){
        // console.log(11)
        span.innerText='输入不合法,请输入正确的手机号码'
        return false
      }
      // 合法的 就清空span
      span.innerText=''
      return true
    }



    // 4.验证验证码
    // 2.1获取验证码表单
    const code=document.querySelector('[name=code]')
    code.addEventListener('change',verfiyCode)//函数名不加括号
    // 2.3封装verfiycode函数
    function verfiyCode(){
      const span=code.nextElementSibling
      // console.log(11)
      // 2.4 定规则
      const reg=/^\d{6}$/
      if(!reg.test(code.value)){
        // console.log(11)
        span.innerText='输入不合法,请输入6位数字'
        return false
      }
      // 合法的 就清空span
      span.innerText=''
      return true
    }



    // 5.验证密码框
    // 2.1获取密码表单
    const password=document.querySelector('[name=password]')
    password.addEventListener('change',verfiypassword)//函数名不加括号
    // 2.3封装verfiypassword函数
    function verfiypassword(){
      const span=password.nextElementSibling
      // console.log(11)
      // 2.4 定规则
      const reg=/^[a-zA-Z0-9-_]{6,20}$/
      if(!reg.test(password.value)){
        // console.log(11)
        span.innerText='输入不合法,6~20位数字字母符号组成'
        return false
      }
      // 合法的 就清空span
      span.innerText=''
      return true
    }



    // 6.密码的再次验证
    // 2.1获取再次验证表单
    const confirm=document.querySelector('[name=confirm]')
    confirm.addEventListener('change',verfiyconfirm)//函数名不加括号
    // 2.3封装verfiyconfirm函数
    function verfiyconfirm(){
      const span=confirm.nextElementSibling
      // console.log(11)
      // 当前表单的值不等于密码框的值就是错误的
      if(confirm.value!==password.value){
        // console.log(11)
        span.innerText='两次密码输入不一致'
        return false
      }
      // 合法的 就清空span
      span.innerText=''
      return true
    }


    // 7.我同意
    const queren=document.querySelector('.icon-queren')
    queren.addEventListener('click',function(){
      // 原来有的就删掉 原来没有的就添加
      this.classList.toggle('icon-queren2')
    })



    // 8.提交模块
    const form=document.querySelector('form')
    form.addEventListener('submit',function(e){
      // 判断是否勾选我同意模块 如果有icon-queren2说明就勾选了 否则没勾选\
      if(!queren.classList.contains('icon-queren2')){
        e.preventDefault()
        return alert('请勾选同意协议')
      }
      //依次判断上面的每个input是否通过,只要有一个没通过就阻止
      if(!(verfiyName()&verfiyCode()&verfiyPhone()&verfiyconfirm()&verfiypassword())){
        e.preventDefault()
      }
    })

登录页

点击切换盒子

使用事件委托时,尽量不要在盒子套的很复杂的时候用,如果li标签里包了一个a元素,a元素里又包了个img元素,此时若给a标签设置事件委托,就不容易实现,因为点击的时候点击的是img元素。

    // tab栏切换
    const tab_nav=document.querySelector('.tab-nav')
    const pane=document.querySelectorAll('.tab-pane')
    // 1.1事件监听
    tab_nav.addEventListener('click',function(e){
      if(e.target.tagName==='A'){
        //取消上一active
        //当前元素添加active
        tab_nav.querySelector('.active').classList.remove('active')
        e.target.classList.add('active')
        for(let i=0;i<pane.length;i++){
          // 先干掉所有人 for循环
          pane[i].style.display='none'
        }
        // 让对应序号的大pane 显示
        pane[e.target.dataset.id].style.display='block'
      }
    })

点击登陆可以跳转页面

  • 先阻止默认行为
  • 如果没有勾选同意,则提示要勾选
  • required属性不能为空
  • 假设登陆成功
  • 把用户名记录到本地存储当中
  • 同时跳转首页 location.href
    // 点击提交模块
    const form=document.querySelector('form')
    const agree=document.querySelector('[name=agree]')
    const username=document.querySelector('[name=username]')
    form.addEventListener('submit',function(e){
      e.preventDefault()
      // 判断是否勾选同意协议
      if(!agree.checked){
        return alert('请勾选同意协议')
      }
      //记录用户名到本地存储
      localStorage.setItem('xtx-uname',username.value)
      // 跳转到首页
      location.href='./index.html'
    })

小兔鲜首页页面

步骤:

最好写个渲染函数,因为一会退出还要用到

1.如果本地存储有记录的用户名,读取本地存储数据

需要把用户名写到第一个li里面

格式:<a href="javascript:;"><i class="iconfont icon-user">用户名</i></a>

因为登陆了,所以第二个里面的文字变为:退出登录

格式:<a href="javascript:;">退出登录</a>

2.如果本地没有数据,则复原为默认的结构

3,点击退出登录,删除本地数据,并重新渲染函数

    // 1.获取第一个小li
    const li1=document.querySelector('.xtx_navs li:first-child')
    const li2=li1.nextElementSibling
    // 2.最好做个渲染函数 因为退出登录需要重新渲染
    function render(){
    // 2.1读取本地存储的用户名
    const uname=localStorage.getItem('xtx-uname')
    // console.log(uname)
    if(uname){
      li1.innerHTML=`<a href="javascript:;"><i class="iconfont icon-user">${uname}</i></a>`
      li2.innerHTML=`<a href="javascript:;">退出登录</a>`
    }else{
      li1.innerHTML='<a href="./login.html">请先登录</a>'
      li2.innerHTML='<a href="./register.html">免费注册</a>'
    }
}
render()
// 2.点击退出登录模块
li2.addEventListener('click',function(){
  //删除本地存储的数据
  localStorage.removeItem('xtx-uname')
  render()
})

放大镜效果

业务分析:

  • 鼠标经过对应小盒子,左侧中等盒子显示对应中等图片

1.获取对应的元素

2.采取事件委托的形式,监听鼠标经过小盒子里面扽图片,注意此时需要使用mouseover事件,因为需要事件冒泡触发small

3.让鼠标经过小图片,可以拿到小图片的src,可以做两件事

  • 让中等盒子的图片换成这个小图片的src
  • 让大盒子的背景图片,也换成这个小图片的src
    // 1. 获取三个盒子
    // 2. 小盒子 图片切换效果
    const small = document.querySelector('.small')
    //  中盒子
    const middle = document.querySelector('.middle')
    //  大盒子
    const large = document.querySelector('.large')
    // 2. 事件委托
    small.addEventListener('mouseover', function (e) {//事件冒泡
      if (e.target.tagName === 'IMG') {
        // console.log(111)
        // 排他 干掉以前的 active  li 上面
        this.querySelector('.active').classList.remove('active')
        // 当前元素的爸爸添加 active
        e.target.parentNode.classList.add('active')
        // 拿到当前小图片的 src
        // console.log(e.target.src)
        // 让中等盒子里面的图片,src 更换为   小图片src
        middle.querySelector('img').src = e.target.src
        // 大盒子更换背景图片
        large.style.backgroundImage = `url(${e.target.src})`
      }
    })
  • 鼠标经过中盒子,右侧会显示放大镜效果的大盒子

1.用到鼠标经过和i离开,鼠标经过中盒子,大盒子利用dislay来显示和隐藏

2,鼠标离开不会立马消失,而是有200ms的延迟,用户体验更好,所以尽量使用定时器做个延迟 settimeout

3.显示和隐藏也尽量定义一个函数,因为鼠标经过离开中等盒子,会显示隐藏,同时,鼠标经过大盒子,也会显示和隐藏

4,给大盒子里面的背景图片一个默认的第一张图片

 

    // 3. 鼠标经过中等盒子, 显示隐藏 大盒子
    middle.addEventListener('mouseenter', show)
    middle.addEventListener('mouseleave', hide)
    let timeId = null
    // 显示函数 显示大盒子
    function show() {
      // 先清除定时器
      clearTimeout(timeId)
      large.style.display = 'block'
    }
    // 隐藏函数 隐藏大盒子
    function hide() {
      timeId = setTimeout(function () {
        large.style.display = 'none'
      }, 200)
    }


    // 4. 鼠标经过大盒子, 显示隐藏 大盒子
    large.addEventListener('mouseenter', show)
    large.addEventListener('mouseleave', hide)
  • 黑色遮罩盒子跟着鼠标来移动
  1. 先做鼠标经过小盒子small盒子,显示隐藏黑色遮罩的盒子
  2. 让黑色遮罩跟着鼠标来走,需要用到鼠标移动事件 mousermove
  3. 让黑色盒子移动的核心思想:不断把鼠标在中等盒子内的坐标给遮罩层left top,这样遮罩层就可以跟着移动了

算法

  • 得到鼠标在页面中的坐标,利用事件对象的pageX
  • 得到中等盒子在页面中的坐标 middle.getBoundingClientRect()
  • 鼠标在中等盒子中的坐标=鼠标在页面中的坐标-middle中等盒子的坐标
  • 黑色遮罩层不断得到 鼠标在中等盒子中的坐标 就可以移动起来了
  • 注意y坐标特殊,需要减去页面被卷去的头部
  • 不用offsetLeft和offsetTop的原因:因为这两个属性跟带有定位的父级有关系,很容易被父级影响,而getBoundingClientRect()不受定位父元素的影响

限定遮罩的盒子只能在middle内部移动,需要添加判断

  • 限定水平方向 大于等于0并且小于等于400
  • 限定垂直方向大于等于0并且小于等于400

遮罩盒子移动的坐标:

  • 声明一个mx作为移动的距离
  • 水平坐标x如果小于等于100,则移动的距离mx就是0,不应该移动
  • 水平坐标如果大于等于100并且小于300,移动的距离就是mx-100(100)是遮罩层盒子自身宽度的一半
  • 水平坐标如果大于等于300,移动的距离就是mx=200不用再移动了
  • 其实水平移动就是在100~200之间移动的
  • 鼠标在中等盒子上移动,大盒子的图片跟着显示对应位置
    // 5. 鼠标经过中等盒子,显示隐藏 黑色遮罩层
    const layer = document.querySelector('.layer')
    middle.addEventListener('mouseenter', function () {
      layer.style.display = 'block'
    })
    middle.addEventListener('mouseleave', function () {
      layer.style.display = 'none'
    })
    // 6.移动黑色遮罩盒子
    middle.addEventListener('mousemove', function (e) {
      // let x = 10, y = 20
      // console.log(11)
      // 鼠标在middle 盒子里面的坐标 = 鼠标在页面中的坐标 - middle 中等盒子的坐标
      // console.log(e.pageX)鼠标在页面中的坐标
      // middle 中等盒子的坐标
      // console.log(middle.getBoundingClientRect().left)
      let x = e.pageX - middle.getBoundingClientRect().left
      let y = e.pageY - middle.getBoundingClientRect().top - document.documentElement.scrollTop//解决页面滚动时框下移的问题,减去被卷去的头部
      // console.log(x, y)
      // 黑色遮罩移动 在 middle 盒子内 限定移动的距离
      if (x >= 0 && x <= 400 && y >= 0 && y <= 400) {
        // 黑色盒子不是一直移动的
        // 声明2个变量 黑色盒子移动的 mx my变量 
        let mx = 0, my = 0
        if (x < 100) mx = 0
        if (x >= 100 && x <= 300) mx = x - 100
        if (x > 300) mx = 200

        if (y < 100) my = 0
        if (y >= 100 && y <= 300) my = y - 100
        if (y > 300) my = 200

        layer.style.left = mx + 'px'
        layer.style.top = my + 'px'
        // 大盒子的背景图片要跟随 中等盒子移动  存在的关系是 2倍   
        large.style.backgroundPositionX = -2 * mx + 'px'
        large.style.backgroundPositionY = -2 * my + 'px'
      }
    })