JavaScript 拖拽与观察者模式的实现及应用

devtools/2025/1/21 21:13:17/

前言

本文将通过几个具体的代码片段,深入探讨 JavaScript 中的拖拽功能和html" title=观察者模式>观察者模式(发布-订阅模式)的实现及其应用场景。

这些代码片段不仅展示了如何实现这些功能,还解释了其背后的原理和实际用途。通过阅读本文,读者可以更好地理解 JavaScript 的高级特性,并将其应用到实际项目中。


1. 拖拽功能的实现

在这里插入图片描述

代码片段

html"><!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>#father {width: 100px;height: 100px;background: red;position: absolute;}#son {width: 100px;height: 100px;background: green;position: absolute;left: 110px;}</style>
</head>
<body><div id="father">father</div><div id="son">son</div><script src="js/drag.js"></script><script src="js/subdrag.js"></script><script>html" title=javascript>javascript">new Drag('#father');new Subdrag('#son');</script>
</body>
</html>

代码解析

拖拽类 Drag
html" title=javascript>javascript">class Drag {constructor(selector) {// 获取拖拽的对象this.ele = document.querySelector(selector);// 添加事件this.ele.onmousedown = function (evt) {// this: 原来指向了 ele 对象,这里需要一个实例对象this.fnDown(evt);}.bind(this);}fnDown(evt) {let e = evt || window.event;// 求鼠标的相对坐标值this.dis_x = e.offsetX;this.dis_y = e.offsetY;// 移动事件document.onmousemove = function (evt) {// this: documentthis.fnMove(evt);}.bind(this);// 鼠标抬起事件document.onmouseup = this.fnUp.bind(this);// 取消默认行为document.ondragstart = function () {return false;};}fnMove(evt) {let e = evt || window.event;this.ele.style.left = e.pageX - this.dis_x + 'px';this.ele.style.top = e.pageY - this.dis_y + 'px';}fnUp() {document.onmousemove = null;}
}
子类 Subdrag 继承自 Drag
html" title=javascript>javascript">class Subdrag extends Drag {constructor(selector) {// 调用父类的构造函数super(selector);}fnMove(evt) {let e = evt || window.event;let left = e.pageX - this.dis_x;let top = e.pageY - this.dis_y;// 设置边界if (left <= 0) {left = 0;} else if (left >= document.documentElement.clientWidth - this.ele.offsetWidth) {left = document.documentElement.clientWidth - this.ele.offsetWidth;}if (top <= 0) {top = 0;} else if (top >= document.documentElement.clientHeight - this.ele.offsetHeight) {top = document.documentElement.clientHeight - this.ele.offsetHeight;}this.ele.style.left = left + 'px';this.ele.style.top = top + 'px';}
}

在这里插入图片描述

功能说明

拖拽功能 是一种常见的用户交互方式,允许用户通过鼠标移动元素。在上述代码中,Drag 类实现了基本的拖拽功能,包括:

  1. 获取拖拽对象:通过选择器获取要拖拽的 DOM 元素。
  2. 添加鼠标按下事件:当用户按下鼠标时,记录鼠标相对于元素的位置 (dis_x, dis_y)。
  3. 添加鼠标移动事件:根据鼠标的当前位置更新元素的位置。
  4. 添加鼠标抬起事件:当用户释放鼠标时,移除鼠标移动事件,防止后续不必要的移动。
  5. 取消默认行为:阻止浏览器的默认拖拽行为。

Subdrag 类继承自 Drag 类,并在其基础上增加了边界限制,确保拖拽的元素不会超出视口范围。这使得拖拽体验更加友好和实用。


2. html" title=观察者模式>观察者模式(发布-订阅模式)

代码片段 1:药店示例

html"><!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>
</head>
<body><script>html" title=javascript>javascript">// 发布者 - 药店let drugstore = {// 花名册obj: {},// 订阅方法listen: function (eventName, fn) {if (!(this.obj[eventName])) {this.obj[eventName] = [];}this.obj[eventName].push(fn);},// 发布方法publish: function (eventName, data) {this.obj[eventName]?.map(fn => {fn(data);});}};let data = {type: "N95",price: 30,num: 500};let liaohuadata = {type: '连花',price: 200,num: 50};// 小周function xiaozhou(data) {if (data.price > 20) {console.log('黑店,太贵了,我就算不出门,我也不去买!');}}drugstore.listen('mask', xiaozhou);// 小易function xiaoyi(data) {if (data.type === 'N95') {console.log('先少买一点,等有普通医用口罩时,再多屯点');}}drugstore.listen('mask', xiaoyi);// 小王function xiaowang(data) {if (data.num > 100) {console.log('多屯点,再高价卖给别人');}}drugstore.listen('mask', xiaowang);// 老王function laowang(data) {console.log('有佛祖保佑,不需要口罩');}drugstore.listen('mask', laowang);drugstore.publish('mask', data);</script>
</body>
</html>

在这里插入图片描述

代码解析

html" title=观察者模式>观察者模式(发布-订阅模式)是一种设计模式,用于定义对象间的一种一对多依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。在上述代码中,drugstore 作为发布者,维护了一个名为 obj 的花名册,记录了不同事件的订阅者列表。

具体实现如下:

  1. 订阅方法 listen:接收事件名称 eventName 和回调函数 fn,将回调函数添加到对应事件的订阅者列表中。
  2. 发布方法 publish:接收事件名称 eventName 和数据 data,遍历对应的订阅者列表,依次调用每个回调函数并传递数据。

通过这种方式,多个订阅者可以根据不同的条件对同一事件做出响应。例如,在上述代码中,四个不同的订阅者(小周、小易、小王、老王)根据药店发布的口罩信息做出了不同的反应。

代码片段 2:售楼处示例

html"><!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>
</head>
<body><script>html" title=javascript>javascript">// 发布者--售楼处let salesOffice = {// 花名册arr: [],// 订阅方法listen: function (fn) {this.arr.push(fn);},// 发布消息publish: function (data) {// 遍历花名册this.arr.forEach(fn => {fn(data);});}};// 发布的消息let data = {size: 80,money: '10000元/平',num: '50套'};// 小明salesOffice.listen(function (data) {if (data.size < 100) {console.log('房子太小,不在考虑');}});// 小龙salesOffice.listen(function (data) {if (parseInt(data.money) < 150000) {console.log('预算充足,价格还可以,马上去订购一套');}});// 小强salesOffice.listen(function (data) {if (parseInt(data.num) > 30) {console.log('赶紧去,先屯20套再说');}});// 发布消息salesOffice.publish(data);</script>
</body>
</html>

在这里插入图片描述

代码解析

在售楼处示例中,salesOffice 作为发布者,同样使用了花名册 arr 来记录订阅者的回调函数。不同的是,这里的订阅者只关心新楼盘推出的消息,而不需要区分不同的事件类型。

具体实现如下:

  1. 订阅方法 listen:接收回调函数 fn 并将其添加到花名册中。
  2. 发布方法 publish:接收数据 data,遍历花名册中的所有回调函数并依次调用。

通过这种方式,多个订阅者可以根据新楼盘的信息做出不同的反应。例如,在上述代码中,三个不同的订阅者(小明、小龙、小强)根据售楼处发布的房屋信息做出了不同的决策。


结尾

通过以上两个主要部分的详细探讨,我们深入分析了 JavaScript 中的拖拽功能和html" title=观察者模式>观察者模式的实现及其应用场景。拖拽功能使得用户能够直观地操作页面元素,而html" title=观察者模式>观察者模式则提供了一种优雅的方式来处理对象间的依赖关系,避免了紧耦合的问题。

在实际项目中,合理运用这些技术和模式可以显著提高用户体验和代码的可维护性。希望本文的内容对读者有所帮助,欢迎继续探索 JavaScript 的更多可能性。无论是构建复杂的用户界面还是实现高效的数据通信,掌握这些高级编程技巧都将为开发者带来更多的便利和灵活性。



http://www.ppmy.cn/devtools/152425.html

相关文章

小程序疫苗预约网站系统ssm+论文源码调试讲解

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

为什么相关性不是因果关系?人工智能中的因果推理探秘

目录 一、背景 &#xff08;一&#xff09;聚焦当下人工智能 &#xff08;二&#xff09;基于关联框架的人工智能 &#xff08;三&#xff09;基于因果框架的人工智能 二、因果推理的基本理论 &#xff08;一&#xff09;因果推理基本范式&#xff1a;因果模型&#xff0…

纯 Python、Django、FastAPI、Flask、Pyramid、Jupyter、dbt 解析和差异分析

一、纯 Python 1.1 基础概念 Python 是一种高级、通用、解释型的编程语言&#xff0c;以其简洁易读的语法和丰富的标准库而闻名。“纯 Python” 在这里指的是不依赖特定的 Web 框架或数据分析工具&#xff0c;仅使用 Python 原生的功能和标准库来开发应用程序或执行任务。 1.…

MySQL(七)MySQL和Oracle、PostgreSQL的区别

文章目录 一、MySQL和Oracle 1.1 基本差别*1.2 使用区别 二、MySQL和PostgreSQL 2.1 基本差别*2.2 使用差别 本系列文章&#xff1a; MySQL&#xff08;一&#xff09;SQL语法、数据类型、常用函数、事务 MySQL&#xff08;二&#xff09;MySQL SQL练习题 MySQL&#xff08;…

路由器缓冲区如何调节的指南说明

调整路由器缓冲区大小是一个复杂且需要细致操作的过程&#xff0c;涉及到对网络流量、设备性能以及缓冲区类型的深入理解。本位作为一篇详尽的指南&#xff0c;提供调整路由器缓冲区的具体方法。 一、前期准备与评估 1. 了解路由器型号与操作系统&#xff1a; 不同品牌和型号…

VSCODE SSH远程连接报错或无法联网安装.vscode-server

问题 问题1:"无法与 SSH 建立连接&#xff1a;XHR Failed"解决篇 在有网得情况下ssh连接总是提示如下&#xff1a; 问题2&#xff1a;远程linux环境无法联网&#xff0c;不能在线下载 解决办法 ​​​​​​​分析&#xff1a;根据报错显示&#xff0c;正在下载 “…

mongoose 支持https踩坑纪实

简述 mongoose是C编写的嵌入式web服务&#xff0c;它能够支持https协议&#xff0c;可以简单的部署&#xff0c;但要做到完美部署&#xff0c;不是那么容易。 部署方法 本人使用的是最新的7.16版&#xff0c;以前版本似乎是要通过修改 头文件中的 MG_ENABLE_SSL 宏定义&…

第四届机器学习、云计算与智能挖掘国际会议

一、会议信息 会议名称&#xff1a;第四届机器学习、云计算与智能挖掘国际会议&#xff08;MLCCIM 2025&#xff09;​​​​​​​ 会议地点&#xff1a;中国漠河 会议时间&#xff1a;2025年7月21-25日 支持单位&#xff1a;佛山市人工智能学会、佛山大学 二、大会主席 …