web api第四天

109 阅读9分钟

事件对象

  • 事件对象是什么

    • 也是个对象,这个对象里有事件触发时的相关信息
    • 例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息
  • 如何获取

    • 在事件绑定的回调函数的第一个参数就是事件对象
    • 一般命名为event、ev、e
元素.addEventListener('click', function (e) {
    //这里的e就是事件对象
}

事件对象常用属性

  • 部分常用属性

    • type

      • 获取当前的事件类型
    • clientX/clientY

      • 获取光标相对于浏览器可见窗口左上角的位置
    • offsetX/offsetY

      • Ø获取光标相对于当前DOM元素左上角的位置
    • key

      • 用户按下的键盘键的值
      • 现在不提倡使用keyCode

事件流

  • 事件流指的是事件完整执行过程中的流动路径
  • 说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
  • 简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父

事件捕获和事件冒泡

  • 事件冒泡概念

当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡

  • 简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
  • 事件冒泡是默认存在的
  • 事件捕获概念

从DOM的根元素开始去执行对应的事件 (从外到里)

  • 事件捕获需要写对应代码才能看到效果
  • 代码:
DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
  • 说明

    • addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)
    • 若传入false代表冒泡阶段触发,默认就是false
    • 若是用 L0 事件监听,则只有冒泡阶段,没有捕获

阻止事件流动

  • 因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
  • 若想把事件就限制在当前元素内,就需要阻止事件流动
  • 阻止事件流动需要拿到事件对象
  • 语法:
事件对象.stopPropagation()
  • 此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效

鼠标经过事件

  • 鼠标经过事件

    • mouseover 和 mouseout 会有冒泡效果
    • mouseenter 和 mouseleave 没有冒泡效果(推荐)

阻止默认行为

  • 阻止默认行为,比如链接点击不跳转,表单域的不提交
  • 语法:
e.preventDefault()

两种注册事件的区别:

  • 传统on注册

    • 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
    • 直接使用null覆盖偶就可以实现事件的解绑
    • 都是冒泡阶段执行的
  • 事件监听注册

    • 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
    • 后面注册的事件不会覆盖前面注册的事件(同一个事件)
    • 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
    • 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
    • 匿名函数无法被解绑

事件委托

  • 事件委托是利用事件流的特征解决一些开发需求的知识技巧

  • 总结:

    • 优点: 给父级元素加事件(可以提高性能)
    • 原理: 事件委托其实是利用事件冒泡的特点, 给父元素添加事件,子元素可以触发
    • 实现: 事件对象.target 可以获得真正触发事件的元素

forEach循环

forEach方法使用:他可以遍历数组,要求传入一个回调函数

回调函数有两个参数:

value:代表当前遍历到的数组的元素

index:当前元素所对应的索引

语法

数组.forEach(function(value,index){操作})

购物车-实现步骤

所有操作都要基于数据:

dom操作的本质是数据操作,任何的dom操作一定要体现到数据

动态渲染

准备静态结构

准备好数据--模拟

遍历拼接

赋值

// 1.实现数据的渲染:后期所有的操作基本上都需要重新渲染,所以我将渲染写成一个函数
function init() {
    let htmlStr = ''
    // 遍历  + 拼接
    data.forEach(function(value, index) {
        htmlStr += `<tr>
                    <td>
                      <input class="s_ck" type="checkbox" ${
                        value.state ? 'checked' : ''
                      }/>
                    </td>
                    <td>
                      <img src="${value.img}" />
                      <p>${value.name}</p>
                    </td>
                    <td class="price">${value.price}</td>
                    <td>
                      <div class="count-c clearfix">
                        <button class="reduce">-</button>
                        <input type="text" value="${value.count}" />
                        <button class="add">+</button>
                      </div>
                    </td>
                    <td class="total">${value.price * value.count}¥</td>
                    <td>
                      <a href="javascript:" class="del">删除</a>
                    </td>
                  </tr>`
    })
​
    // 赋值
    carBody.innerHTML = htmlStr
}
init()

计算出总数和总价格

可以认为商品有没有选中,是由复选框的状态来决定,但是复选框的选中状态由  数据决定的
结论:商品到底有没有选中是由数据决定的
所以,我们可以直接从数据的角度出发。。。
dom元素只是一个载体,只是数据的一种呈现方式。。
​
细节:数量增减,选中状态的切换,删除  都需要重新计算总数和总价格,所以我们将计算总数和总价格的操作封装为一个函数
// 2.计算出总数和总价格
function calculateTotal() {
    // 所有计算都要参照数据
    let totalCount = 0 // 总数
    let totalPrice = 0 // 总价格
    // 遍历数据
    data.forEach(function(value, index) {
        // 并不是所有商品都要参与计算,只有state为true的商品才需要计算总数和总价格
        if (value.state == true) {
            totalCount++
            // totalCount += value.count
            totalPrice += value.count * value.price
        }
    })
    // 赋值
    totalCountEle.innerText = totalCount
    totalPriceEle.innerText = totalPrice + '¥'
}
calculateTotal()

数量增减

我们真正要做的操作是修改数据本身,而不是修改dom

因为只要修改了数据,我再根据数据进行渲染,那么dom就会有一个相应的变化

细节:动态生成的元素的事件绑定需要使用事件委托

dom元素变化 ,让数据发生改变 --- 双向数据绑定

 // 3.数量增减
carBody.addEventListener('click', function(e) {
    if (e.target.className == 'add') {
        // 获取数据索引
        let index = e.target.id
        // 修改对应数据的count
        data[index].count++
        // 重新渲染
        init()
        // 重新计算总数和总价格
        calculateTotal()
    } else if (e.target.className == 'reduce') {
        // 获取数据索引
        let index = e.target.id
        // 修改对应数据的count
        data[index].count--
        // 重新渲染
        init()
        // 重新计算总数和总价格
        calculateTotal()
    }
})

禁用减按钮

<button class="reduce" id='${index}' ${value.count == 1 ? 'disabled' : '' }>-</button>

全选

根据当前全选复选框的选中状态,修改数据中的state值,之后重新渲染和重新计算总价...

// 4.全选
all.addEventListener('click', function() {
    // 获取全选复选框的状态
    let state = all.checked
    // 修改所有数据中的state状态
    data.forEach(function(value) {
        value.state = state
    })
    init()
    calculateTotal()
})

列表中的复选框的单击操作

修改当前复选框所对应的数据对象的state值,再重新渲染和计算总价

// 5.列表中的复选框的单击操作
carBody.addEventListener('click', function(e) {
    if (e.target.className == 's_ck') {
        // 获取数据索引
        let index = e.target.id
        // 值:取反值
        data[index].state = !data[index].state
​
        init()
        calculateTotal()
    }
})

删除所选商品

所选的商品: state=true 的商品

细节:体验使用for进行删除的特点,找到bug以及解决bug的方式

filter方法的使用--新的数组的遍历操作

使用for

// 6.删除所选商品 --- 为什么我们不用for来做删除
delall.addEventListener('click', function() {
    // 所谓选中的商品就是  数据中state值为true的商品
    for (let i = 0; i < data.length; i++) {
        if (data[i].state == true) {
            data.splice(i, 1)
            i--
        }
    }
    init()
    calculateTotal()
})

使用filter

主要用于 查询 和 删除 场景

// 6.删除所选商品 --- 为什么我们不用for来做删除
delall.addEventListener('click', function() {
    // 所谓选中的商品就是  数据中state值为true的商品,留下 的就是state为false的值
    // 以后删除和查询 一般会使用filter来实现
    data = data.filter(function(value) {
        return !value.state
    })
    init()
    calculateTotal()
})

清理购物车

data = []

data.length = 0

// 7.清理购物车
clear.addEventListener('click', function() {
    // data = []
    // 这种可以将数组的元素全部清除,优点在于不会重新创建新数组,节省性能
    data.length = 0
    init()
    calculateTotal()
})

数据删除

将当前的 删除按钮 所对应的数据从数组中删除

事件使用委托方式进行绑定

 // 8.数据删除
carBody.addEventListener('click', function(e) {
    if (e.target.className == 'del') {
        // 删除数组中的数据
        data.splice(e.target.id, 1)
        // 重新渲染和计算总价
        init()
        calculateTotal()
    }
})

学生信息管理

数据渲染

一.说到渲染,必定会有 静态模板结构数据 的参与

意味着,以后实现渲染业务之前,先

1.准备好静态结构

2.获取到数据

二:渲染的具体实现:遍历 + 拼接,生成html结构,赋值给指定的元素

      // 2.获取元素
      let tbody = document.querySelector('tbody')
​
      // 3.实现数据的渲染
      let htmlStr = ''
      for (let i = 0; i < arr.length; i++) {
        htmlStr += `<tr>
                      <td>${i + 1}</td>
                      <td>${arr[i].uname}</td>
                      <td>${arr[i].age}</td>
                      <td>${arr[i].gender}</td>
                      <td>${arr[i].salary}</td>
                      <td>${arr[i].city}</td>
                      <td>
                        <a href="javascript:" class="del">删除</a>
                      </td>
                    </tr>`
      }
      tbody.innerHTML = htmlStr

使用forEach进行遍历

// 3.实现数据的渲染
let htmlStr = ''
​
// forEach方法的使用:它可以从头到尾遍历数组(如果只要遍历其中一些,不要使用它),要求传入一个回调函数
// 回调函数有两个参数:
// value:代表当前遍历到的数组的元素
// index:当前元素所对应的索引
// 数组.forEach(function(value,index){操作})// 如果遍历的是数据,那么参数一般名称为  value|v   index|i
// 如果遍历的是元素伪数组,那么参数一般名称为 ele/element// value:当前遍历到的元素  --数组中的一个一个的元素
// index:遍历到的元素所对应的索引
arr.forEach(function(value, index) {
    htmlStr += `<tr>
                    <td>${index + 1}</td>
                    <td>${value.uname}</td>
                    <td>${value.age}</td>
                    <td>${value.gender}</td>
                    <td>${value.salary}</td>
                    <td>${value.city}</td>
                    <td>
                    <a href="javascript:" class="del">删除</a>
                    </td>
                </tr>`
})
​
tbody.innerHTML = htmlStr

数据新增

核心业务就是实现数据的添加

1.收集好用户数据

2.将数据添加到指定的结构(将数据添加到数组arr中),所以我们收集的数据,最终应该是一个对象

3.让页面中呈现效果:出现刚刚新增的记录

第一种方式

根据数据创建元素,设置内容并追加到指定的结构

// 4.实现数据的新增
add.addEventListener('click', function() {
    // 4.1.收集数据,生成一个对象
    let newObj = {
        stuId: 1004,
        uname: '欧阳霸天123',
        age: 19,
        gender: '男',
        salary: '40000',
        city: '广州'
    }
    // 4.2.将数据添加到数组
    // arr.push(newObj)
​
    // 4.3 让页面中呈现效果
    // 之前: 创建一个元素 ,设置内容,追加到页面结构中
    let newTr = document.createElement('tr')
    newTr.innerHTML = `<td>1</td>
                        <td>${newObj.uname}</td>
                        <td>${newObj.age}</td>
                        <td>${newObj.gender}</td>
                        <td>${newObj.salary}</td>
                        <td>${newObj.city}</td>
                        <td>
                        <a href="javascript:" class="del">删除</a>
                        </td>`
    tbody.appendChild(newTr)
})

上述方式的问题

1.页面刷新之后,之前添加的数据会消失,我们需要将数据进行存储,后期根据存储的数据实现数据的渲染
​
2.如果你是使用  创建经并追加  ,移除元素  这种方式来实现页面交互操作,那么就会造成后期在项目中数据渲染出现异常
    1.有可能这个页面会有很多条记录
    2.有可能造成这个页面没有填满

新增要基于数据

收集用户数据,生成一个对象

将对象添加到数据源数组--ajax

重新渲染

// 新增用户时的需要的dom元素
let uname = document.querySelector('.uname')
let age = document.querySelector('.age')
let gender = document.querySelector('.gender')
let salary = document.querySelector('.salary')
let city = document.querySelector('.city')
      
      
// 4.实现数据的新增
add.addEventListener('click', function() {
    // 获取数据源数组中的最后一个元素的stuId + 1,如果没有数据,索引为1001
    let id = arr.length > 0 ? arr[arr.length - 1].stuId + 1 : 1001
​
    // 4.1.收集数据,生成一个对象
    let newObj = {
        // id你准备如何生成:获取数据源数组中的最后一个元素的stiId + 1
        stuId: id,
        uname: uname.value,
        age: age.value,
        gender: gender.value,
        salary: salary.value,
        city: city.value
    }
​
    // 4.2.将数据添加到数组,以后是发起ajax请求,将数据存储到服务器
    arr.push(newObj)
​
    // 4.3 让页面中呈现效果--重新渲染--修改数据源,根据数据源重新渲染
    init()
})

数据删除

一:操作一定要基于数据

1.删除数据源数组中的数据

2.重新渲染

二:动态生成的元素的事件绑定需要使用事件委托

委托方式绑定事件

// 5.实现数据的删除:事件委托
// 事件委托:将事件绑定给 父元素
tbody.addEventListener('click', function(e) {
    // 判断用户当前单击的是不是删除按钮
    if (e.target.className == 'del') {
    }
})

实现删除功能

修改模板结构,存储数据索引

<td>
    <a href="javascript:" class="del" id='${index}'>删除</a>
</td>

实现数据的删除

  • 获取元素中的索引

  • 删除数组中的数据 -- ajax

  • 重新渲染

    // 5.实现数据的删除:事件委托
    // 事件委托:将事件绑定给 父元素
    tbody.addEventListener('click', function(e) {
        // 判断用户当前单击的是不是删除按钮
        if (e.target.className == 'del') {
            // 获取当前删除元素所对应s的数据索引
            let index = e.target.id
            // 将数据源数组中的数据进行删除
            // 删除元素需要 索引下标,所以:
            // 我要什么:给我一个当前你想要删除的数据的索引
            // 我有什么: 我有一个e,准确的说是我有e.target
            arr.splice(index, 1)
    ​
            // 重新渲染
            init()
        }
    })