鼠标跟随、DOM二级事件

一、鼠标跟随

  • 实现一个跟随鼠标移动的效果
  • 思路:
  1. 页面中的盒子并不会自己跟着鼠标移动,所谓的鼠标跟随就是在鼠标移动的时候,不断的计算鼠标所处的位置,然后把盒子的位置设置到鼠标所处的位置上即可;
  2. 很显然是鼠标移动的时候,所以需要监听鼠标移动事件,因为盒子在整个页面中所以需要监听document的鼠标移动事件;
  3. 在事件触发时计算鼠标的位置,把元素设置到鼠标的位置
  1. let box = document.querySelector('#box');
  2. document.onmousemove = function (e) {
  3. // 1. 获取鼠标的位置
  4. let {
  5. clientX,
  6. clientY
  7. } = e;
  8. console.log('move', clientX, clientY);
  9. // 2. 计算盒子的位置,并设置给元素
  10. box.style.left = clientX - box.offsetWidth / 2 + 'px';
  11. box.style.top = clientY - box.offsetHeight / 2 + 'px';
  12. };

二、限制边界的鼠标跟随

  • 前面已经实现了鼠标跟随,但是鼠标可以画出屏幕,现在要求限制盒子在屏幕的可是区域内;
  • 思路:
    1. 盒子的最大边界即盒子的left和top的最大值,left的最大值 = 浏览器可视窗口的宽度 - 盒子的宽度;top的最大值 = 浏览器的可视窗口的高度 - 盒子的高度;
    2. 盒子边最小边界即left=0,top=0的位置
  1. function win(attr) {
  2. return document.documentElement[attr] || document.body[attr];
  3. }
  4. let box = document.querySelector('#box');
  5. let minL = 0;
  6. let minT = 0;
  7. document.onmousemove = function (e) {
  8. // 1. 获取鼠标的位置
  9. let {
  10. clientX,
  11. clientY
  12. } = e;
  13. // 2. 获取盒子的offsetWidth/offsetHeight
  14. let {
  15. offsetWidth,
  16. offsetHeight
  17. } = box;
  18. console.log('move', clientX, clientY);
  19. // 3. 求盒子的最大边界值
  20. let maxL = win('clientWidth') - box.offsetWidth;
  21. let maxT = win('clientHeight') - box.offsetHeight;
  22. // 4. 根据鼠标位置求得盒子应该所处的位置
  23. let l = clientX - offsetWidth / 2;
  24. let t = clientY - offsetHeight / 2;
  25. // 5. 在设置之前对求出的l和t进行修正
  26. // left的边界判断
  27. if (l < minL) {
  28. l = minL;
  29. }
  30. if (l > maxL) {
  31. l = maxL;
  32. }
  33. // top的边界判断
  34. if (t < minT) {
  35. t = minT;
  36. }
  37. if (t > maxT) {
  38. t = maxT;
  39. }
  40. // 6. 把修正之后的值设置给元素对象
  41. box.style.left = l + 'px';
  42. box.style.top = t + 'px';
  43. };

三、电商放大镜

  1. 页面中有两个等大的盒子,box1中放着原图和一个可以跟随鼠标移动的小盒子mask,mask相对于box1相对定位;
  2. 另一个box2放着大图,大图相对于盒子绝对定位;且大盒子overflow:hidden; 大图片的尺寸等于box1的尺寸乘以想放大的倍数,如本例中放大三倍,box1宽高300,所以大图宽高900;
  3. mask 的尺寸 box1的尺寸 = box2的尺寸 大图的尺寸
  4. 起初box2和mask都是隐藏的,当鼠标划入box1时,设置mask和box2的display: block;
  5. 接着在box1中移动鼠标,实现带边界的鼠标跟随;
  6. 因为mask占box1的比例和box2占大图的比例是相同的。但是大图的尺寸是box2的3倍,为了保证看到的是相同的地方,box2中图片的left和top的移动距离,应该是mask在box1中移动的3倍(放大三倍)
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. * {
  8. margin: 0;
  9. padding: 0;
  10. }
  11. .box1, .box2 {
  12. position: relative;
  13. float: left;
  14. width: 300px;
  15. height: 300px;
  16. border: 1px solid red;
  17. margin: 50px;
  18. overflow: hidden;
  19. }
  20. .box1 img {
  21. width: 100%;
  22. height: 100%;
  23. }
  24. .box2 img {
  25. position: absolute;
  26. left: 0;
  27. top: 0;
  28. width: 900px;
  29. height: 900px;
  30. }
  31. .mask {
  32. display: none;
  33. position: absolute;
  34. left: 0;
  35. top: 0;
  36. width: 100px;
  37. height: 100px;
  38. background: rgba(0, 0, 0, .5);
  39. cursor: move;
  40. }
  41. .box2 {
  42. display: none;
  43. }
  44. </style>
  45. </head>
  46. <body>
  47. <div id="box1" class="box1">
  48. <img src="./iphone.jpg" alt="">
  49. <div class="mask" id="mask" style="width: 100px;height: 100px"></div>
  50. </div>
  51. <div class="box2" id="box2">
  52. <img src="./iphone.jpg" alt="" id="bigImg">
  53. </div>
  54. <script src="js/5-放大镜.js"></script>
  55. </body>
  56. </html>
  1. let $ = sltcr => document.querySelector(sltcr);
  2. // 1. 获取元素对象
  3. let box1 = $('#box1');
  4. let box2 = $('#box2');
  5. let mask = $('#mask');
  6. let bigImg = $('#bigImg');
  7. // 2. 当鼠标划入时把mask和box2显示出来
  8. box1.onmouseenter = function () {
  9. box2.style.display = mask.style.display = 'block';
  10. };
  11. box1.onmouseleave = function () {
  12. box2.style.display = mask.style.display = 'none';
  13. };
  14. // 计算mask能够在box1中移动的边界
  15. let {
  16. offsetLeft,
  17. offsetTop,
  18. clientWidth,
  19. clientHeight
  20. } = box1;
  21. let {
  22. width,
  23. height
  24. } = mask.style;
  25. let maxL = clientHeight - parseFloat(width);
  26. let maxT = clientHeight - parseFloat(height);
  27. // 2. 监听box1的鼠标移动事件
  28. box1.onmousemove = function (e) {
  29. // 2.1 获取现在鼠标的位置并计算盒子应该出现的位置
  30. let l = e.clientX - offsetLeft - parseFloat(width) / 2;
  31. let t = e.clientY - offsetTop - parseFloat(height) / 2;
  32. // 2.2 根据边界修正l和t
  33. if (t < 0) {
  34. t = 0;
  35. }
  36. if (t > maxT) {
  37. t = maxT;
  38. }
  39. if (l < 0) {
  40. l = 0;
  41. }
  42. if (l > maxL) {
  43. l = maxL;
  44. }
  45. // 2.3 把l和t设置给mask实现mask鼠标跟随跟随
  46. mask.style.left = l + 'px';
  47. mask.style.top = t + 'px';
  48. // 2.4 设置大图left和top的值为mask移动距离的3倍
  49. bigImg.style.left = -l * 3 + 'px';
  50. bigImg.style.top = -t * 3 + 'px';
  51. };

四、DOM2级事件

  • JS中的事件分为DOM0级事件和DOM2级事件;

DOM0级事件

  • DOM0级事件绑定
  1. box.onclick = function () {};
  2. box.onclick = function () {
  3. console.log('1')
  4. };
  5. box.onclick = function () {
  6. console.log('2');
  7. };
  • DOM0级事件移除
  1. box.onclick = null;
  • 用DOM0级事件的方式绑定,只能绑定一个事件,因为DOM0级事件是DOM元素对象的一个属性,多次赋值,这个属性只能保存的是最后一次被赋的值;
  • DOM0级事件都是绑定在冒泡阶段的;

DOM2级事件:

  1. 可以为同一事件绑定多个事件函数;
  2. 可以指定绑定的在捕获阶段还是绑定再冒泡阶段;
  • DOM2级事件绑定:
  1. 元素.addEventListener('事件名称', 事件函数, false冒泡 | true捕获)
  2. box.addEventListener('click', function () {
  3. console.log(1)
  4. }, true);
  5. box.addEventListener('click', function () {
  6. console.log(2)
  7. }, false);
  8. box.addEventListener('click', function () {
  9. console.log(3);
  10. }, false);
  • 移除DOM2级事件
  1. 元素.removeEventListener('事件名称', 事件函数, false|true);

DOM0级事件和DOM2级事件绑定区别:

  1. DOM0级事件只有冒泡阶段,DOM2级事件的第三个参数false表示冒泡,true表示捕获
  2. DOM0级事件只能为一个事件绑定一个事件函数,而DOM2级事件可以绑定多个事件函数;

DOM2事件为啥可以绑定多个事件呢?

DOM2级事件给在绑定的时候是给每个元素的每个事件准备了一个事件池,类似一个数组,每次addEventListener()就是把事件函数放到事件池中,然后等事件触发的时候,再一个一个的把事件函数从事件池中拿出来执行一次;

  • IE低版本DOM2级事件:
  • 绑定事件:元素.attachEvent(‘onclick’, callback);
  • 移除事件:元素.detachEvent(‘onclick’, callback);
  • 而且IE的DOM2级事件只能绑定在冒泡阶段,如果绑定多个,事件触发时,事件函数的执行顺序和绑定顺序无关;