01、JS部分基础知识

1.事件和事件绑定

1. 什么是事件?


   事件是浏览器赋予元素的默认行为,也可以理解为事件是天生具备的,不论我们是否为其绑定方法,当某些行为触发的时候,相关的事件都会被触发执行!

 「浏览器赋予元素的事件行为」
   https://developer.mozilla.org/zh-CN/docs/Web/Events
   - 鼠标事件
     + click 点击事件(PC:频繁点击N次,触发N次点击事件) 单击事件(移动端:300ms内没有发生第二次点击操作,算作单击事件行为,所以click在移动端有300ms延迟)
     + dblclick 双击事件
     + contextmenu 鼠标右键点击触发
     + mousedown 鼠标按下
     + mouseup 鼠标抬起
     + mousemove 鼠标移动
     + mouseover 鼠标滑入
     + mouseover 鼠标滑出
     + mouseenter 鼠标进入
     + mouseleave 鼠标离开
     + wheel 鼠标滚轮滚动
     + ...
   - 键盘事件
     + keydown 键盘按下
     + keyup 键盘抬起
     + keypress 长按(除了Shift/Fn/CapsLock键之外) 
     + ...
   - 手指事件
    「Touch Event 单手指事件模型」
     + touchstart 手指按下
     + touchmove 手指移动
     + touchend 手指松开
    「Gesture Event 多手指事件模型」
   - 表单事件 
     + focus 获取焦点
     + blur 失去焦点
     + submit 表单提交(前提:表单元素都包含在form中,并且点击的按钮是submit)
     + reset 表单重置(前提:表单元素都包含在form中,并且点击的按钮是reset)
     + select 下拉框内容选中
     + change 内容改变
     + input 移动端中经常使用的,监控文本框中的内容随着输入的改变而触发
     + ...
   - 资源事件 
     + load 加载成功(window.onload / img.onload)
     + error 加载失败
     + beforeunload 资源卸载之前(window.onbeforeunload 页面关闭之前触发)
     + ...
   - CSS3动画事件
     + transitionend  transition动画结束
     + transitionstart transition动画开始
     + transitionrun  transition动画运行中
     + ...
   - 视图事件
     + resize 元素(浏览器)大小改变
     + scroll 滚动条滚动
     + ...
   - ...


2. 什么是事件绑定?

   给元素默认的事件行为绑定方法,这样可以在行为触发的时候,执行这个方法!
   - DOM0级事件绑定
     语法:[元素].on[事件]=[函数]
     document.body.οnclick=function(){}

     移除绑定:赋值为null或者其他非函数值皆可
     document.body.οnclick=null

     原理:每一个DOM元素对象的私有属性上都有很多类似于“onxxx”的私有属性,我们给这些代表事件的私有属性赋值,就是DOM0事件绑定
        + 如果没有对应事件的私有属性值(例如:DOMContentLoaded)则无法基于这种办法实现事件绑定
        + 只能给当前元素的某个事件行为绑定一个方法,绑定多个方法,最后一个操作会覆盖以往的
        + 好处是执行效率快,而且开发者使用起来方便

   - DOM2级事件绑定
     语法:[元素].addEventListener([事件],[方法],[捕获/冒泡])
     document.body.addEventListener('click',fn1,false);

     移除:[元素].removeEventListener([事件],[方法],[捕获/冒泡])  但是需要参数和绑定的时候一样
     document.body.removeEventListener('click',fn1,false);

     原理:每一个DOM元素都会基于__proto__,查找到EventTarget.prototype上的addEventListener/removeEventListener等方法,基于这些方法实现事件的绑定和移除;DOM2事件绑定采用事件池机制;
        + DOM2事件绑定,绑定的方法一般不是匿名函数,主要目的是方便移除事件绑定的时候使用
        + 凡是浏览器提供的事件行为,都可以基于这种模式完成事件的绑定和移除(例如:window.onDOMContentLoaded是不行的,因为没有这个私有的事件属性,但是我么可以 window.addEventListener('DOMContentLoaded',func) 这样是可以的)
        + 可以给当前元素的某个事件类型绑定多个“不同”的方法(进入到事件池),这样当事件行为触发,会从事件池中依次(按照绑定的顺序)取出
对应的方法然后执行

2. 事件对象和阻止默认行为


   给当前元素的某个事件行为绑定方法,当事件行为触发,不仅会把绑定的方法执行,而且还会给方法默认传递一个实参,而这个实参就是事件对象

  • 事件对象中常用的属性

    • target & srcElement

    • type

    • code & key

    • keyCode & which

    • which / keyCode

    • clientX / clientY

    • pageX / ev.pageY

    • preventDefault

    • stopPropagation


         * 事件对象:存储当前事件操作及触发的相关信息的(浏览器本身记录的,记录的是当前这次操作的信息,和在哪个函数中无关)
         *   + 鼠标事件对象 MouseEvent 
         *     + clientX/clientY 鼠标触发点距离当前窗口的X/Y轴坐标
         *     + pageX/pageY 鼠标触发点距离BODY的X/Y轴坐标
         *     + type 事件类型
         *     + target / srcElement 获取当前事件源(当前操作的元素)
         *     + path 传播路径
         *     + ev.preventDefault() / ev.returnValue=false 阻止默认行为
         *     + ev.stopPropagation() / ev.cancelBubble=true 阻止冒泡传播
         *     + ...
         *
         *   + 键盘事件对象 KeyboardEvent
         *     + key / code 存储按键名字
         *     + which / keyCode 获取按键的键盘码
         *       + 方向键 “左37 上38 右39 下40”
         *       + Space 32
         *       + BackSpace 8
         *       + Del 46  MAC电脑中没有BackSpace,delete键是 8
         *       + Enter 13
         *       + Shift 16   
         *       + Ctrl 17   
         *       + Alt 18
         *       + ...
         *     + altKey 是否按下alt键(组合按键)
         *     + ctrlKey 是否按下ctrl键(组合按键)
         *     + shiftKey 是否按下shift键(组合按键)
         *
         *   + TouchEvent 手指事件对象(移动端)
         *     + changedTouches / touches 都是用来记录手指的信息的,平时常用的是changedTouches
         *       + 手指按下、移动、离开屏幕 changedTouches都存储了对应的手指信息,哪怕离开屏幕后,存储的也是最后一次手指在屏幕中的信息;而touches在手指离开屏幕后,就没有任何的信息了;=>获取的结果都是一个TouchList集合,记录每一根手指的信息;
         *     + ev.changedTouches[0] 第一根手指的信息
         *       + clientX/clientY
         *       + pageX/pageY
         *       + ...
         *    
         *   + Event 普通事件对象
    

3.事件的传播机制


<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>珠峰在线预科班</title>

    <!-- IMPORT CSS -->
    <link rel="stylesheet" href="css/reset.min.css">
    <style>
        .box {
            box-sizing: border-box;
            margin: 20px auto;
            padding-top: 20px;
            width: 300px;
            height: 300px;
            background: lightblue;
        }

        .outer {
            box-sizing: border-box;
            margin: 0 auto;
            padding-top: 20px;
            width: 200px;
            height: 200px;
            background: lightcoral;
        }

        .inner {
            margin: 0 auto;
            width: 100px;
            height: 100px;
            background: lightgreen;
        }
    </style>
</head>

<body>
    <div class="box">
        <div class="outer">
            <div class="inner">

            </div>
        </div>
    </div>


    <!-- IMPORT JS -->
    <script>
         因为点击事件行为存在冒泡传播机制,所以不论点击INNER/OUTER/BOX,最后都会传播到BODY上,触发BODY的CLICK事件行为,把为其绑定的方法执行
         在方法执行接受到的事件对象中,有一个target/srcElement属性(事件源),可以知道当前操作的是谁,我们此时方法中完全可以根据事件源的不同,做不同的处理
         上述机制就是 “事件委托/事件代理”:利用事件的冒泡传播机制(核心/前提),我们可以把一个“容器A”中所有后代元素的某个“事件行为E”触发要做的操作,委托给A的事件行为E,这样后期只要触发A中任何元素的E行为,都会传播到A上,把给A绑定的方法执行;在方法执行的时候,基于事件源不同做不同的处理;
         + 性能高 60%左右
         + 可以操作动态绑定的元素
         + 某些需求必须基于它完成
         + ...
         document.body.onclick = function (ev) {
            let target = ev.target,
                targetClass = target.className;
            if (targetClass === "inner") {
                console.log('INNER');
                return;
            }
            if (targetClass === "outer") {
                console.log('OUTER');
                return;
            }
            if (targetClass === "box") {
                console.log('BOX');
                return;
            }
        }; 

         let box = document.querySelector('.box'),
            outer = document.querySelector('.outer'),
            inner = document.querySelector('.inner');

        inner.onclick = function (ev) {
            console.log('INNER');
            ev.stopPropagation();
        };

        outer.onclick = function (ev) {
            console.log('OUTER');
            ev.stopPropagation();
        };

        box.onclick = function (ev) {
            console.log('BOX');
            ev.stopPropagation();
        }; 

        
         * DOM0事件绑定中给元素事件行为绑定的方法,都是在目标阶段/冒泡阶段触发的 
         * 
         * DOM2事件绑定可以控制在绑定的方法在捕获阶段触发(虽然没有什么实际的意义)
         *   + 元素.addEventListener(事件,方法,false/true)
         *   + 最后一个参数默认是false:控制方法是在冒泡阶段触发执行的,如设置为true可以控制在捕获阶段触发执行
        
         inner.onclick = function (ev) {
            console.log('INNER==>', ev);
            ev.stopPropagation();
        };

        outer.onclick = function (ev) {
            console.log('OUTER==>', ev);
        };

         box.addEventListener('click', function (ev) {
            console.log('BOX==>', ev);
        }, true); 
        box.onclick = function (ev) {
            console.log('BOX==>', ev);
        };

        document.body.onclick = function (ev) {
            console.log('BODY==>', ev);
        }; 
    </script>
</body>

</html>

 4、 mouseover和mouseenter区别

推荐mouseenter和mouseleave配合

5.图片延迟加载思路1(兼容IE)


<!DOCTYPE html>
<html>

<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>
    <!-- IMPORT CSS -->
    <link rel="stylesheet" href="css/reset.min.css">
    <style>
        /*
         图片懒加载的思路:
           + 最开始加载页面的时候,IMG的SRC不赋值(这样就不会加载真实图片,把真实图片的地址赋值给IMG的自定义属性,方便后期想要加载真实图片时候获取) 
           + 如果SRC不赋值或者加载图片是错误的,会显示“碎图”,这样样式不美观,所以:我们最开始让IMG是隐藏的「可以设置display、也可以设置透明度为0(透明度改变可以设置过渡效果)」
           + 给图片所在的盒子设置背景占位图(或者背景颜色),在真实图片没有加载之前,用其占位「盒子宽高事先设置好的」
           ------
           啥时候加载?
           + 当页面第一次渲染完(其它资源加载完成,例如:window.onload)
           + 把出现在当前可视窗口内的图片进行加载
           ------
           如何加载?
           + 获取图片的自定义属性值,拿到真实图片地址
           + 给图片的SRC赋值真实地址:如果图片可以正常加载成功,则让IMG显示
         */
        html,
        body {
            height: 500%;
        }

        .pic-box {
            box-sizing: border-box;
            position: absolute;
            top: 1000px;
            left: 100px;
            width: 600px;
            height: 337px;
            background: #DDD;
        }

        .pic-box img {
            display: block;
            width: 100%;
            height: 100%;
            opacity: 0;
            transition: opacity 1s;
        }
    </style>
</head>

<body>
    <div class="pic-box">
        <img src="" data-img="images/girl.webp" alt="">
    </div>

    <!-- IMPORT JS -->
    <script src="js/utils.js"></script>
    <script>
        const picBox = document.querySelector('.pic-box'),
            imgBox = picBox.querySelector('img'),
            HTML = document.documentElement;
        // 图片懒加载
        const lazy = function lazy() {
            let trueImg = imgBox.getAttribute('data-img');
            imgBox.src = trueImg;
            imgBox.onload = () => {
                imgBox.style.opacity = 1;
            };
            // 设置自定义属性:当前图片已经处理过延迟加载了
            picBox.isLoad = true;
        };

        // 计算何时加载{完全出现}
        const computed = function computed() {
            // 如果之前已经处理过,则无需再次处理了
            if (picBox.isLoad) return;
            let C = HTML.clientHeight,
                { top: B, bottom: A } = picBox.getBoundingClientRect();
            if (A <= C && B >= 0) {
                lazy();
            }
        };

        // 第一次其它资源加载完计算一次 & 页面滚动中随时计算
        //   + scroll事件会在浏览器滚动条滚动中进行触发,并且按照浏览器最快反应时间(一般5~7ms)的频率进行触发!!例如:我们滚动100ms,按照5ms触发一次,一共触发20次!! “触发频率太快了,造成了没必要的计算和性能消耗”
        //   + 此时我们需要“降低”触发频率「我们不是降低浏览器的触发频率,而是把computed函数执行的频率降下来」,我们此操作称之为“函数节流”!!
        window.onload = computed;
        window.onscroll = utils.throttle(computed, 100);
    </script>

    <script>
        /*
        const picBox = document.querySelector('.pic-box'),
            imgBox = picBox.querySelector('img');
        // 图片懒加载
        const lazy = function lazy() {
            /!* 
            // 如果页面中的IMG没有设置隐藏,此时的操作可以确保图片地址是正确的情况下,再给页面中的IMG赋值,防止因图片加载失败,出现“裂图”的情况!!
            let trueImg = imgBox.getAttribute('data-img');
            let img = new Image(); //document.createElement('img')
            img.src = trueImg;
            img.onload = () => {
                imgBox.src = trueImg;
                imgBox.style.opacity = 1;
            }; 
            *!/

            let trueImg = imgBox.getAttribute('data-img');
            imgBox.src = trueImg;
            imgBox.onload = () => {
                // 真实图片加载成功
                imgBox.style.opacity = 1;
            }; 
        };
        // 当页面其它资源都加载完成后,执行延迟加载
        window.onload = lazy;
        // setTimeout(lazy, 1000); 
        */
    </script>
</body>

</html>

 6.图片延迟加载2(IntersectionObserver[es6的语法])

IntersectionObserver介绍:

 /*

        IntersectionObserver:ES6新增的一个内置类

          + 不兼容IE浏览器

          + new IntersectionObserver(callback) 创建它的一个实例:创建一个监听器,用来监听一个或者多个DOM元素和浏览器可视窗口的交叉状态和信息

            + 当交叉状态「出现在可视窗口中、离开可视窗口」发生改变,都会触发监听器的回调函数callback执行

            + 在回调函数中可以获取所有监听的DOM和可视窗口的交叉信息

        */

        let box1 = document.querySelector('#box1'),

            box2 = document.querySelector('#box2');

        // 创建监听器

        let ob = new IntersectionObserver((changes) => {

            /*

            回调函数执行:

             + 创建监听器、且监听了DOM元素会立即执行一次(连续监听多个DOM只触发一次,但是如果监听是分隔开的,每新监听一个元素都会触发执行一次)

             + 当监听的元素和可视窗口交叉状态改变,也会触发执行「默认是“一露头”或者“完全出去”,会触发;当然可以基于第二个Options配置项中的threshold来指定规则;」

               + threshold: [0]  一露头&完全出去

               + ...

               + threshold: [1]  完全出现&出去一点

             ----

             changes:是一个数组,记录了每一个监听元素和可视窗口的交叉信息

               + boundingClientRect:记录当前监听元素的getBoundingClientRect获取的值

               + isIntersecting:true/false  true代表出现在可视窗口中,false则反之

               + target:存储当前监听的这个DOM元素对象

               + ...

            */

            console.log(changes);

        }, { threshold: [1] });

        // 监听某个DOM元素和可视窗口的交叉状态改变;unobserve移除监听;

        ob.observe(box1);

        ob.observe(box2);

 IntersectionObserver实现延迟加载

<!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>Document</title>
  <style>
* {
  margin: 0;
  padding: 0;
}

html,
body {
  height: 500%;
}

.pic-box {
  position: absolute;
  width: 1200px;
  height: 675px;
  background-color: #ccc;
  top: 300%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.pic-img {
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 0.5s;
}
  </style>
</head>

<body>

  <div class="pic-box">
    <img src="" data-img="./images/girl.webp" alt="" class="pic-img">
  </div>
  <script src="./utils.js"></script>
  <script>
const picBox = document.querySelector('.pic-box'),
  imgBox = picBox.querySelector('img')

// 延迟加载
const lazy = function lazy() {
  let trueImg = imgBox.getAttribute('data-img')
  imgBox.src = trueImg
  imgBox.onload = () => {
    imgBox.style.opacity = 1
  }
  console.log('ok');
}

// 创建监听器监听pic-box,控制延迟加载: 无需再进行复杂运算、也无需考虑函数节流
const ob = new IntersectionObserver(([item]) => {
  if (item.isIntersecting) {
    // 完全出现在视口中: 延迟架子啊
    lazy()
    // 处理过的需要移除监听
    ob.unobserve(item.target)
  }
}, { threshold: [1] })
ob.observe(picBox)
  </script>

</body>

</html>

7.京东放大镜效果

 jquery写法:

<!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>Document</title>
  <style>
.magnifier {
  margin: 20px auto;
  width: 500px;
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
}

.magnifier .abbre {
  position: relative;
  box-sizing: border-box;
  width: 200px;
  height: 200px;
}

.magnifier .abbre img {
  width: 100%;
  height: 100%;
}

.magnifier .abbre .mark {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 10;
  width: 80px;
  height: 80px;
  background: rgba(255, 0, 0, .4);
  cursor: move;
}

.magnifier .detail {
  display: none;
  position: relative;
  box-sizing: border-box;
  width: 300px;
  height: 300px;
  overflow: hidden;
}

.magnifier .detail img {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 10;
}
  </style>
</head>

<body>
  <section class="magnifier">
    <!-- 缩略图 -->
    <div class="abbre">
      <img src="images/1.jpg" alt="">

      <!-- MARK遮罩层 -->
      <div class="mark"></div>
    </div>

    <!-- 详情图 -->
    <div class="detail">
      <img src="images/2.jpg" alt="">
    </div>
  </section>

  <script src="js/jquery.min.js"></script>
  <script>
$(function() {
  // 1. 获取元素 
  let $magnifier = $('.magnifier'),
    $abbre = $magnifier.find('.abbre'),
    $mark = $abbre.find('.mark'),
    $detail = $magnifier.find('.detail'),
    $detailIMG = $detail.find('img');

  // 动态计算出大图的大小
  let abbreW = $abbre.width(),
    abbreH = $abbre.height(),
    abbreOffset = $abbre.offset(),
    markW = $mark.width(),
    markH = $mark.height(),
    detailW = $detail.width(),
    detailH = $detail.height(),
    detailIMGW = 0,
    detailIMGH = 0


  detailIMGW = detailW / (markW / abbreW) // 图片的宽高= 父盒子的宽高/(缩略图的宽高/)
  detailIMGH = detailH / (markH / abbreH)
  $detailIMG.css({
    width: detailIMGW,
    height: detailIMGH
  })

  // 计算“mark/大图”移动的位置
  const computed = function computed(ev) {
    let curL = ev.pageX - abbreOffset.left - markW / 2,
      curT = ev.pageY - abbreOffset.top - markH / 2
    // 边界处理
    let minL = 0,
      minT = 0,
      maxL = abbreW - markW,
      maxT = abbreH - markH
    curL = curL < minL ? minL : (curL > maxL ? maxL : curL)
    curT = curT < minT ? minT : (curT > maxT ? maxT : curT)

    $mark.css({
      left: curL,
      top: curT
    });
    $detailIMG.css({
      left: -curL / abbreW * detailIMGW,
      top: -curT / abbreH * detailIMGH
    });
  }

  // 2. 事件触发
  $abbre.mouseenter(function(ev) {
    $mark.css('display', 'block');
    $detail.css('display', 'block');
    computed(ev)

  }).mousemove(function(ev) {
    computed(ev)
  }).mouseleave(function(ev) {
    $mark.css('display', 'none');
    $detail.css('display', 'none');
  });

})
  </script>
</body>

</html>

8.哔哩哔哩导航

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值