20230401----重返学习-冒泡相关事件-拖拽-放大镜

day-040-forty-20230401-冒泡相关事件-拖拽-放大镜

冒泡相关事件

mouseenter/mouseleave与mouseover/mouseout

  • mouseover/mouseout 有冒泡,忽略层级之间的关系

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>冒泡机制title>
        <style>
          * {
            margin: 0;
            padding: 0;
          }
          #parent {
            height: 500px;
            width: 500px;
            background-color: red;
          }
          #son {
            height: 300px;
            width: 300px;
            background-color: blue;
          }
        style>
      head>
      <body>
        <div id="parent">
          <div id="son">div>
        div>
      body>
    html>
    <script>
      let parent = document.getElementById("parent");
      let son = document.getElementById("son");
      parent.onmouseover =function(){
        console.log('parent移入-onmouseover')
      }
      parent.onmouseout =function(){
        console.log('parent移出-onmouseout')
      }
      son.onmouseover =function(){
        console.log('son移入-onmouseover')
      }
      son.onmouseout =function(){
        console.log('son移出-onmouseout')
      }
    script>
    
  • mouseenter/mouseleave 没冒泡,不会忽略层级之间的关系

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>冒泡机制title>
        <style>
          * {
            margin: 0;
            padding: 0;
          }
          #parent {
            height: 500px;
            width: 500px;
            background-color: red;
          }
          #son {
            height: 300px;
            width: 300px;
            background-color: blue;
          }
        style>
      head>
      <body>
        <div id="parent">
          <div id="son">div>
        div>
      body>
    html>
    <script>
      let parent = document.getElementById("parent");
      let son = document.getElementById("son");
      parent.onmouseenter =function(){
        console.log('parent移入-onmouseenter')
      }
      parent.onmouseleave =function(){
        console.log('parent移出-onmouseleave')
      }
      son.onmouseenter =function(){
        console.log('son移入-onmouseenter')
      }
      son.onmouseleave =function(){
        console.log('son移出-onmouseleave')
      }
    script>
    

事件委托

  • 事件委托: 也叫事件代理,将绑定的事件委托给祖先元素,祖先元素监听事件,并利用e.target来分配给当前元素
    • 原理是: 事件冒泡机制
  • 事件委托的好处
    1. 减少事件数量,提高性能

    2. 预测未来元素,新添加的元素仍然可以触发该事件

      DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <title>2.事件委托title>
        head>
        <body>
          <ul class="ulBox">
            <li>001li>
            <li>002li>
            <li>003li>
            <p>pppp>
            <li>004li>
            <li>005li>
          ul>
        body>
      html>
      <script>
        // let ulBox = document.querySelector(".ulBox");
        // let newli1 = document.createElement("li");
        // newli1.innerHTML = "aaa";
        // ulBox.appendChild(newli1);
        // let lis = document.querySelectorAll("li");
        // for (let i = 0; i < lis.length; i++) {
        //   lis[i].onclick = function () {
        //     console.log(i);
        //   };
        // }
        // let newli2 = document.createElement("li");
        // newli2.innerHTML = "bbb";
        // ulBox.appendChild(newli2);//这个默认没事件
      
        let ulBox = document.querySelector(".ulBox");
        let newli1 = document.createElement("li");
        newli1.innerHTML = "aaa";
        ulBox.appendChild(newli1);
        ulBox.onclick = function (e) {
          if (e.target.tagName === "LI") {
            console.log(e.target);
          }
        };
        let newli2 = document.createElement("li");
        newli2.innerHTML = "bbb";
        ulBox.appendChild(newli2); //这个依旧有事件
      script>
      
    3. 避免内存外泄,在低版本的IE中,防止删除元素而没有移除事件而造成的内存溢出

点击案例

点击按钮会打印出内部data-type属性的值

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>3.点击事件title>
head>
<body>
  <div class="box">
    <button data-type="+">+button>
    <button data-type="-">-button>
    <button data-type="*">*button>
    <button data-type="/">/button>
  div>
body>
html>
<script>
  let box = document.querySelector('.box')
  box.onclick=function(e){

    if(e.target.tagName==='BUTTON'){
      const theDataType = e.target.getAttribute('data-type')
      console.log(theDataType)
    }
  }
script>

拖拽

简版

  1. 对小盒子绑定一个mousedown鼠标点击事件
    1. 移入到小盒子里面按下时,通过e.offsetX与e.offsetY获取鼠标相对于当前盒子中的初始坐标
    2. 对window绑定一个mouseup事件。在整个页面,按着鼠标移动,抬起的瞬间求结束坐标
      • mouseup事件事件中,通过e.pageX与e.pageY拿到鼠标相对于文档的终点坐标,之后修改小盒子的top与left。
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>拖拽title>
    <style>
      .box {
        height: 100px;
        width: 100px;
        background-color: skyblue;

        position: absolute;
        top: 0px;
        left: 0px;
      }
    style>
  head>
  <body>
    <div class="box">div>
  body>
html>
<script>
  let box = document.querySelector(".box");
  let startX=0, startY=0, endX=0, endY=0;
  box.onmousedown = function (e) {
    //移入到小盒子里面按下获取初始坐标
    startX = e.offsetX;
    startY = e.offsetY;
    console.log(startX, startY);

    //在整个页面,按着鼠标移动,抬起的瞬间求结束坐标
    window.addEventListener("mouseup", move);
  };

  const move = function move(e) {
    //结束坐标
    endX = e.clientX;
    endY = e.clientY;

    //盒子最后的位置  结束坐标-初始坐标
    box.style.left = endX - startX + "px";
    box.style.top = endY - startY + "px";

    //抬起结束后,要移除抬起事件,否则盒子就一直还跟着鼠标
    window.removeEventListener("mouseup", move);
  };
script>

有限制版

  • 鼠标在当前元素中的坐标替换

    • e.offsetX可以用e.clientX-box.offsetLeft代替

    • e.offsetY可以用e.clientY-box.offsetTop

      box.onmousedown=function(e){//进入盒子按下的一瞬间,获取初始坐标
        startX=e.offsetX;
        startY=e.offsetY;
        //再整个文档中移动鼠标,抬起瞬间获取结束坐标
      }
      //等价于
      box.onmousedown = function (e) {
        startX = e.clientX - box.offsetLeft;
        startY = e.clientY - box.offsetTop;
      };
      
  • 在小盒子点击后,添加了window的mousemove事件。

    • 用于让小盒子在鼠标没松开时,一直跟随着鼠标移动。
      • 同时对于小盒子的移动范围进行了限制,让其只能在视口中出现
        • 视口用的客户端宽度document.documentElement.clientWidth减去当前小盒子宽度box.offsetWidth来计算得出的
  • 在小盒子移动后,依旧使用window的mouseup事件移除mousemove对应函数及mouseup对应事件函数

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>拖拽3title>
    <style>
      .box {
        height: 100px;
        width: 100px;
        background-color: skyblue;

        position: absolute;
        top: 0px;
        left: 0px;
      }
    style>
  head>
  <body>
    <div class="box">div>
  body>
html>
<script>
  let box = document.querySelector(".box");
  let startX, startY;
  box.onmousedown = function (e) {
    // //移入到小盒子里面按下获取初始坐标
    // startX = e.offsetX;
    // startY = e.offsetY;
    // 假设无法通过offsetX/offsetY获取初始坐标
    startX = e.clientX - this.offsetLeft;
    startY = e.clientY - this.offsetTop;

    console.log(startX, startY);

    //在整个页面,按着鼠标移动,抬起的瞬间求结束坐标
    window.addEventListener("mousemove", move);
    window.addEventListener("mouseup", up);
  };

  const move = function move(e) {
    let html = document.documentElement || document.body;
    let maxWidth = html.clientWidth - box.offsetWidth;
    let maxHeight = html.clientHeight - box.offsetHeight;

    //结束坐标
    let endX = e.clientX;
    let endY = e.clientY;

    let resX = endX - startX;
    resX = Math.max(0, resX);
    resX = Math.min(maxWidth, resX);

    let resY = endY - startY;
    resY = Math.max(0, resY);
    resY = Math.min(maxHeight, resY);

    //盒子最后的位置  结束坐标-初始坐标
    box.style.left = resX + "px";
    box.style.top = resY + "px";
  };
  const up = function up(e) {
    window.removeEventListener("mousemove", move);
    window.removeEventListener("mouseup", up);
  };
script>

多盒拖拽

  1. 每个盒子都有自己的起始坐标,结束坐标,范围值,多个盒子之间互不影响…
  2. 思路是:
    • 给每个盒子添加 私有的属性值
      • 在mousedown事件中使用this来给每个盒子添加需要用到的私有属性值
        • 如果是事件,也要修改this指向
          • 修改this指向,可以使用函数原型__proto__上的call()、apply()、bind()。这里主要使用bind()。
    • 在小盒子点击后,添加了window的mousemove事件,用于让小盒子跟随鼠标移动。
    • 在小盒子移动后,依旧使用window的mouseup事件移除mousemove对应函数及mouseup对应事件函数。
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>Documenttitle>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      #box {
        height: 100px;
        width: 100px;
        background-color: pink;
        position: fixed;
        left: 0;
        top: 0;
      }
      #boxtwo {
        height: 200px;
        width: 200px;
        background-color: aqua;
        position: fixed;
        left: 0;
        top: 200px;
      }
    style>
  head>
  <body>
    <div id="box">div>
    <div id="boxtwo">div>
  body>
html>
<script>
  let html = document.documentElement || document.body;
  //每个盒子都有自己的起始坐标,结束坐标,范围值...
  //给每个盒子添加 私有的属性值
  function down(e) {
    this.startX = e.offsetX;
    this.startY = e.offsetY;

    //修改this指向 call apply bind
    this._move = move.bind(this);
    this._up = up.bind(this);

    window.addEventListener("mousemove", this._move);
    window.addEventListener("mouseup", this._up);
  }

  function move(e) {
    //console.log(this);//window
    //console.log(e.target);
    this.endX = e.clientX;
    this.endY = e.clientY;

    this.resultX = this.endX - this.startX;
    this.resultY = this.endY - this.startY;

    this.maxW = html.clientWidth - this.offsetWidth;
    this.maxH = html.clientHeight - this.offsetHeight;

    this.resultX = this.resultX >= this.maxW ? this.maxW : this.resultX <= 0 ? 0 : this.resultX;
    this.resultY = this.resultY >= this.maxH ? this.maxH : this.resultY <= 0 ? 0 : this.resultY;

    this.style.left = this.resultX + "px";
    this.style.top = this.resultY + "px";
  }

  function up() {
    window.removeEventListener("mousemove", this._move);
    window.removeEventListener("mouseup", this._up);
  }

  box.addEventListener("mousedown", down);
  boxtwo.addEventListener("mousedown", down);
script>

放大镜

放大镜简版

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Documenttitle>
    <style>
      .box {
        width: 700px;
        margin: 50px auto;
        height: 400px;
        display: flex;
      }
      .leftbox {
        width: 300px;
        height: 300px;
        position: relative;
      }
      .leftbox img {
        width: 300px;
        height: 300px;
      }
      .leftbox .mark {
        display: none;
        width: 100px;
        height: 100px;
        background-color: red;
        opacity: 0.5;
        position: absolute;
        left: 0;
        top: 0;
      }
      .rightbox {
        display: none;
        width: 400px;
        height: 400px;
        overflow: hidden;
        position: relative;
      }
      .rightbox img {
        /* width:1200px;
        height:1200px;   */
        position: absolute;
        left: 0;
        top: 0;
      }
    style>
  head>
  <body>
    <div class="box">
      <div class="leftbox">
        <img src="images/1.jpg" alt="" />
        <div class="mark">div>
      div>
      <div class="rightbox">
        <img src="images/2.jpg" alt="" />
      div>
    div>
  body>
html>
<script>
  (function () {
    //1.求出大图片的高度和宽度
    let leftbox = document.querySelector(".leftbox"); //左侧盒子
    let mark = document.querySelector(".mark"); //遮罩层
    let rightbox = document.querySelector(".rightbox"); //右侧盒子
    let bigimg = document.querySelector(".rightbox img"); //大图片

    function getwh(ele, attr) {
      return parseFloat(window.getComputedStyle(ele)[attr]);
    }

    //左侧盒子 高度和宽度
    let leftboxW = leftbox.offsetWidth;
    let leftboxH = leftbox.offsetHeight;

    //右侧盒子 高度和宽度
    //隐藏的盒子无法通过offsetWidth/offsetHeight 求值,默认为0
    let rightboxW = getwh(rightbox, "width");
    let rightboxH = getwh(rightbox, "height");

    //遮罩层 高度和宽度
    let markW = getwh(mark, "width");
    let markH = getwh(mark, "height");

    //设置大图片的高度和宽度
    bigimg.style.width = (rightboxW / markW) * leftboxW + "px";
    bigimg.style.height = (rightboxH / markH) * leftboxH + "px";

    //移入盒子,mark和右侧盒子显示
    leftbox.onmouseenter = function () {
      mark.style.display = "block";
      rightbox.style.display = "block";
    };
    //移出盒子,mark和右侧盒子隐藏
    leftbox.onmouseleave = function () {
      mark.style.display = "none";
      rightbox.style.display = "";
    };

    //鼠标在左侧小盒移动,mark也移动
    leftbox.onmousemove = function (e) {
      let cx = e.clientX;
      let cy = e.clientY;
      let maxW = leftboxW - markW;
      let maxH = leftboxH - markH;

      let resX = cx - markW / 2 - leftbox.offsetLeft;
      let resY = cy - markH / 2 - leftbox.offsetTop;

      resX = resX < 0 ? 0 : resX > maxW ? maxW : resX;
      resY = resY < 0 ? 0 : resY > maxH ? maxH : resY;

      //移动红色遮罩层
      mark.style.left = resX + "px";
      mark.style.top = resY + "px";

      //移动大图片---按照比率
      bigimg.style.left = "-" + (rightboxW / markW) * resX + "px";
      bigimg.style.top = "-" + (rightboxH / markH) * resY + "px";
    };
  })();
script>

个人优化版

  • 用了css变量,在容器上放置了相对定位对放大镜用了绝对定位。
    • 进一步优化,.left-box为容器,大图片盒子放在.left-box里,去除.box这一层级。
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Documenttitle>
    <style>
      .box {
        --min-image-width: 300px;
        --min-image-height: 300px;
        --mark-width: 100px;
        --mark-height: 100px;
        --scale: 4;
        margin: 250px;
        display: flex;
        position: relative;
      }
      .box .left-box {
        width: var(--min-image-width);
        height: var(--min-image-height);
        position: relative;
      }
      .box .left-box img {
        width: var(--min-image-width);
        height: var(--min-image-height);
      }
      .box .left-box .mark {
        display: none;
        width: var(--mark-width);
        height: var(--mark-height);
        background-color: red;
        opacity: 50%;
        position: absolute;
        left: 0px;
        top: 0px;
      }
      .box .right-box {
        display: none;
        width: calc(var(--mark-width) * var(--scale));
        height: calc(var(--mark-height) * var(--scale));
        overflow: hidden;
        position: absolute;
        left: var(--min-image-width);
        top: 0px;
      }
      .box .right-box img {
        width: calc(var(--min-image-width) * var(--scale));
        height: calc(var(--min-image-height) * var(--scale));
        position: absolute;
        left: 0px;
        top: 0px;
      }
    style>
  head>
  <body>
    <div class="box">
      <div class="left-box">
        
        <img
          src="https://cdn.cnbj0.fds.api.mi-img.com/b2c-shopapi-pms/pms_1672220519.85821941.png"
          alt="大图"
        />
        <div class="mark">div>
      div>
      <div class="right-box">
        <img
          src="https://cdn.cnbj0.fds.api.mi-img.com/b2c-shopapi-pms/pms_1672220519.85821941.png"
          alt="大图"
        />
      div>
    div>
  body>
  <script>
    (function () {
      //1. 未出图片的高度和宽度
      let box = document.querySelector(".box");
      let leftBox = box.querySelector(".left-box");
      let mark = leftBox.querySelector(".mark");
      let rightBox = box.querySelector(".right-box");
      let bigImage = rightBox.querySelector("img");

      //左侧盒子 高度和宽度
      let leftBoxWidth = leftBox.offsetWidth;
      let leftBoxHeight = leftBox.offsetHeight;

      const getWH = function getWH(ele, attr) {
        // console.log(window.getComputedStyle(ele)[attr]);
        return parseInt(window.getComputedStyle(ele)[attr]) || 0;
      };

      //右侧盒子 高度和宽度
      // 隐藏的盒子无法通过offsetWidth/offsetHeight求值,默认为0
      let rightBoxWidth = getWH(rightBox, "width");
      let rightBoxHeight = getWH(rightBox, "height");

      let markWidth = getWH(mark, "width");
      let markHeight = getWH(mark, "height");

      let bigImageWidth = (rightBoxWidth / markWidth) * leftBoxWidth;
      let bigImageHeight = (rightBoxHeight / markHeight) * leftBoxHeight;
      // console.log(bigImageWidth, bigImageHeight);

      bigImage.style.width = `${bigImageWidth}px`;
      bigImage.style.height = `${bigImageHeight}px`;

      leftBox.onmouseenter = function () {
        mark.style.display = "block";
        rightBox.style.display = "block";
      };
      leftBox.onmouseleave = function () {
        mark.style.display = "none";
        rightBox.style.display = "none";
      };

      leftBox.onmousemove = function (e) {
        let cx = e.clientX;
        let cy = e.clientY;

        let maxWidth = leftBoxWidth - markWidth;
        let maxHeight = leftBoxHeight - markHeight;

        let resX = cx - markWidth / 2 - leftBox.getBoundingClientRect().left;
        let resY = cy - markHeight / 2 - leftBox.getBoundingClientRect().top;
        console.log(cx, cy, resX, resY, maxWidth, maxHeight);

        resX = resX < 0 ? 0 : resX > maxWidth ? maxWidth : resX;
        resY = resY < 0 ? 0 : resY > maxHeight ? maxHeight : resY;

        mark.style.left = `${resX}px`;
        mark.style.top = `${resY}px`;

        bigImage.style.left = `-${resX * (rightBoxWidth / markWidth)}px`;
        bigImage.style.top = `-${resY * (rightBoxHeight / markHeight)}px`;
      };
    })();
  script>
html>

进阶参考

你可能感兴趣的:(重返学习,学习,javascript,前端)