js要点整理-14

69 阅读3分钟

事件

事件的组成

  • 触发谁的事件: 事件源
  • 触发什么事件: 事件类型
  • 触发后做什么: 事件处理函数

事件的绑定方式

onXXX

  • 这个写法中 on 是一个固定开头, 真正的事件类型, 应该是 (click/change)
  • 同一个元素, 只能通过这个方式绑定一个事件,如果写了第二个, 那么第一个会被覆盖

事件监听

addEventListener (非 IE7 8 下使用)

语法: 元素.addEventListener('事件类型', 事件处理函数, 冒泡还是捕获)

oDiv.addEventListener(
    "click",
    function () {
        console.log("我是第一个事件");
    },
    false
);
oDiv.addEventListener(
    "click",
    function () {
        console.log("我是第二个事件");
    },
    false
);
  • 点击 div 时, 两个函数都会执行, 并且会按照你注册的顺序执行
  • 先打印 我是第一个事件, 然后打印 我是第二个事件
  • 注意: 事件类型需要要写 on, 点击事件就是 onclick

attachEvent (IE 7 8 下 使用)

语法: 元素.attachEvent('事件类型', 事件处理函数)

        /**
         *  给 header 内的 三个 li 添加点击事件
         * 
         *  当点击到 header 内的 li 的时候, 将 content 内对应顺序的 li 展示
        */

        // var lis = document.querySelectorAll('li')    // 错误写法: 会获取到页面所有的 li
        // console.log(lis)
        var lis = document.querySelectorAll('.header li')
        var lis_c = document.querySelectorAll('.content li')

        // 这个循环只是为了给 每一个 header 内的 li 添加一个 事件, 其他没有任何用处
        for (let i = 0; i < lis.length; i++) {
            lis[i].addEventListener('click', function () {
                // 1. 取消 header 和 content 内 所有 li 的类名
                for (var k = 0; k < lis.length; k++) {
                    lis[k].className = ""
                    lis_c[k].className = ""
                }

                // 2. 给点击的 header 内的 li 添加类名
                lis[i].className = "active"

                // 3. 给对应顺序的 content 内的 li 添加类名
                lis_c[i].className = "active"

            })
        }

常见的事件

浏览器事件

  • load: 页面全部资源加载完毕
  • scroll: 浏览器滚动的时候触发

鼠标事件

  • click: 点击事件 (移动端也能触发点击事件)
  • dblclick: 双击事件
  • contextmenu: 右键事件
  • mousedown: 鼠标左键按下事件
  • mouseup: 鼠标左键抬起事件
  • mousemove: 鼠标移动
  • 移入子元素的时候也会触发事件
    • mouseover: 鼠标移入事件
    • mouseout: 鼠标移出事件
  • 移入子元素的时候 不会触发事件 (推荐使用)
    • mouseenter: 鼠标移入事件
    • mouseleave: 鼠标移出事件

键盘事件

目前 JS 中 只有 input 输入框和 document 对象可以绑定输入事件

  • keyup: 键盘抬起事件
  • keydown: 键盘按下事件
  • keypress: 键盘按下在抬起事件

表单事件

  • focus: 输入框获取焦点
  • blur: 输入框失去焦点
  • change: 表单内容改变事件
  • input: 表单内容输入事件

触摸事件

  • touchstart: 触摸开始事件
  • touchend: 触摸结束事件
  • touchmove: 触摸移动事件

事件对象

  • 在每一个事件被触发的时候, 会有一个对象内部存放一些关于当前 事件的一些描述信息
  • 浏览器将这个事件信息对象放在了一个叫做 window.event 的属性中
  • 在高版本浏览器中书写的方式为 事件处理函数的第一个形参
  • 写法最初就是 event 后来简化为 ev 目前简化为 e
        // 曾经在 低版本 IE 还有市场使用需求的时候, 我们一般需要一个兼容写法
        box.onclick = function (e) {
            e = e || window.event
        }

点击事件的光标点获取

相对于事件源

  • offsetX 和 offsetY
  • 相对于我们点击的元素的边框内测开始计算

相对于浏览器窗口你点击的坐标点

  • clientX 和 clientY
  • 相对于浏览器窗口来计算的, 不管你页面滚动到什么情况, 都是根据窗口来计算坐标

相对于页面你点击的坐标点

  • pageX 和 pageY
  • 是相对于整个页面的坐标点, 不管有没有滚动, 都是相对于页面拿到的坐标点

获取键盘按键

  • e.key(具体按键)
  • e.keyCode(Ascll码表)

拖拽案例

    <div class="box"></div>
    <script>
        var box = document.querySelector('.box')
        var flag = false    // 创建一个开关变量控制移动事件能否执行, 默认为关闭状态, 也就说正常不允许执行


        // 保存鼠标按下时的 定位
        var startX = 0
        var startY = 0

        // 保存鼠标按下时的 位置(偏移量)
        var startLeft = 0
        var startTop = 0

        box.onmousedown = function (e) {
            flag = true                 // 当鼠标按下的时候, 打开开关, 允许移动事件触发
            // console.log('鼠标按下', e.clientX, e.clientY)

            // 按下时 保存 鼠标的定位
            startX = e.clientX
            startY = e.clientY

            // 按下时 保存 元素的偏移量
            startLeft = box.offsetLeft
            startTop = e.target.offsetTop
        }



        document.onmousemove = function (e) {
            if (!flag) return      // 事件函数内部, 先判断 开关是打开还是关闭, 根据开关的状态决定能否执行移动事件
            // console.log('鼠标移动前的定位', startX, startY)
            // console.log('鼠标移动后的定位', e.clientX, e.clientY)

            // 3.1 获取最新的鼠标定位 - 鼠标按下时的定位 === 移动距离
            var moveX = e.clientX - startX
            var moveY = e.clientY - startY

            // 4.1 元素的初始位置 + 移动的距离 === 移动到哪里
            var left = startLeft + moveX
            var top = startTop + moveY

            // 5. 添加一个边界限制
            if (left <= 0) left = 0
            if (top <= 0) top = 0

            if (left > document.documentElement.clientWidth - box.offsetWidth) left = document.documentElement.clientWidth - box.offsetWidth
            if (top > document.documentElement.clientHeight - box.offsetHeight) top = document.documentElement.clientHeight - box.offsetHeight

            box.style.left = left + 'px'
            box.style.top = top + 'px'
        }



        box.onmouseup = function () {
            flag = false              // 当鼠标抬起的时候, 关闭开关, 禁止移动事件触发
            console.log('鼠标抬起')
        }
    </script>

购物车案例

/**
 *  功能:
 *      1. 页面的渲染
 *      2. 全选功能
 *      3. 总件数和总价格
 *
 *
 *  逻辑:
 *      1. 准备一个渲染函数
 *          * 根据数据渲染商品
 *          * 根据选中的商品 渲染总件数与总价格 全选按钮
 *
 *      2. 给每一个商品对应的选中按钮添加事件
 *      3. 给全选按钮 添加事件
 */

var cartList = [
    {
        id: 111234,
        status: true,
        pic: "https://img1.baidu.com/it/u=2511310783,721605137&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=332",
        name: "我是一个手机, 不知道是啥",
        price: 100,
        number: 3,
        total: 16,
    },
    {
        id: 123456,
        status: false,
        pic: "https://img1.baidu.com/it/u=1537709578,2453227648&fm=253&fmt=auto&app=120&f=JPEG?w=809&h=500",
        name: "我是一个电脑, 不知道是啥",
        price: 98.72,
        number: 1,
        total: 7,
    },
    {
        id: 965874,
        status: true,
        pic: "https://img2.baidu.com/it/u=3561506717,735421650&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500",
        name: "我是一个手纸, 不知道是啥",
        price: 356.21,
        number: 2,
        total: 22,
    },
];

// 0. 获取标签
var content = document.querySelector(".content");

// 0. 全局变量

// 0. 创建一个渲染函数
function bindHtml() {
    // 计算选中的商品, 总商品数量, 总价格
    var checked_num = 0; // 存储选中商品的数量
    var checked_num_total = 0; // 存储选中商品所有的收藏数量
    var checked_total_price = 0; // 存储选中商品的总价
    for (var k = 0; k < cartList.length; k++) {
        if (cartList[k].status) {
            checked_num++;
            checked_num_total = checked_num_total + cartList[k].number;
            checked_total_price =
                checked_total_price + cartList[k].number * cartList[k].price;
        }
    }

    var str = `
        <div class="top">
            <input class="checked_all" type="checkbox" ${
                checked_num === cartList.length ? "checked" : ""
            }> 全选
        </div>
        <ul>`;

    // 利用 循环 遍历 数组, 拿到每一个商品数据, 有多少个商品 就 拼接 多少个 li
    for (var i = 0; i < cartList.length; i++) {
        str += `
            <li>
                <div class="status">
                    <input class="item" data-id="${
                        cartList[i].id
                    }" type="checkbox" ${cartList[i].status ? "checked" : ""}>
                </div>
                <div class="show">
                    <img src="${cartList[i].pic}" alt="">
                </div>
                <div class="title">
                ${cartList[i].name}
                </div>
                <div class="price">
                    ¥ ${cartList[i].price.toFixed(2)}
                </div>
                <div class="number">
                    <button class="prev" data-id="${cartList[i].id}">-</button>
                    <input type="text" value="${cartList[i].number}">
                    <button class="next" data-id="${cartList[i].id}">+</button>
                </div>
                <div class="sub">
                    ¥ ${(cartList[i].number * cartList[i].price).toFixed(2)}
                </div>
                <div class="destory">
                    <button class="del_item" data-id="${
                        cartList[i].id
                    }">删除</button>
                </div>
            </li>`;
    }

    str += `</ul>
        <div class="bottom">
            <div class="totalNum">
                总件数 : ${checked_num_total}
            </div>
            <div class="btns">
                <button class="clear">清空购物车</button>
                <button class="pay" data-num="${checked_num}" data-price="${checked_total_price.toFixed(2)}">去结算</button>
                <button class="del" data-num="${checked_num}">删除所有已选中</button>
            </div>
            <div class="totalPrice">
                总价格 : ¥ <span>${checked_total_price.toFixed(2)}</span>
            </div>
        </div>
    `;

    content.innerHTML = str;
}

// 1. 初次打开页面 调用渲染函数
bindHtml();

// 2. 利用事件委托将后续需要添加的事件 委托给共同的父级 content
content.onclick = function (e) {
    // 2.1 全选按钮
    if (e.target.className === "checked_all") {
        // 1. 更改数据
        for (var i = 0; i < cartList.length; i++) {
            cartList[i].status = e.target.checked;
        }

        // 2. 重新渲染页面
        bindHtml();
    }

    // 2.2 更改商品的选中状态
    if (e.target.className === "item") {
        // 1. 更改数据
        for (var k = 0; k < cartList.length; k++) {
            if (e.target.dataset.id - 0 === cartList[k].id) {
                cartList[k].status = !cartList[k].status;
            }
        }

        // 2. 重新渲染页面
        bindHtml();
    }

    // 增加
    if (e.target.className === "prev") {
        for (var q = 0; q < cartList.length; q++) {
            if (e.target.dataset.id - 0 === cartList[q].id) {
                if (cartList[q].number === 1) return;
                cartList[q].number--;
            }
        }
        bindHtml();
    }
    // 减少
    if (e.target.className === "next") {
        for (var w = 0; w < cartList.length; w++) {
            if (e.target.dataset.id - 0 === cartList[w].id) {
                if (cartList[w].number === cartList[w].total)
                    return alert("库存不足");
                cartList[w].number++;
            }
        }
        bindHtml();
    }

    // 删除
    if (e.target.className === "del_item") {
        // 0. 防止刁民手滑
        if (!confirm('您确定删除吗?')) return

        // 1. 更改数据
        for (var r = 0; r < cartList.length; r++) {
            if (e.target.dataset.id - 0 === cartList[r].id) {
                cartList.splice(r, 1)
            }
        }

        // 2. 重新渲染页面
        bindHtml()
    }

    // 清空购物车
    if (e.target.className === 'clear') {
        // 0. 防止刁民手滑
        if (!confirm('您确定删除吗?')) return

        // 1. 清空购物车数据
        cartList = []

        // 2. 重新渲染页面
        bindHtml()
    }

    // 去结算
    if (e.target.className === 'pay') {
        if (e.target.dataset.num - 0 === 0) return alert('请选中一些商品后在去结算')
        alert(e.target.dataset.price)
    }

    // 删除所有已选中
    if (e.target.className === 'del') {
        if (e.target.dataset.num === '0') return alert('当前没有已选中的商品')

        // 0. 防止刁民手滑
        if (!confirm('您确定删除吗?')) return

        // 1. 更改数据
        for (var t = 0; t < cartList.length; t++) {
            if (cartList[t].status) {
                cartList.splice(t, 1)
                t--
            }
        }

        // 2. 重新渲染页面
        bindHtml()
    }

    // 当没有选中商品的时候禁用 '去结算' 和 '删除所有已选中'
};