前言
本文将通过几个具体的代码片段,深入探讨 javascript 中的拖拽功能和观察者模式(发布-订阅模式)的实现及其应用场景。
这些代码片段不仅展示了如何实现这些功能,还解释了其背后的原理和实际用途。通过阅读本文,读者可以更好地理解 javascript 的高级特性,并将其应用到实际项目中。
1. 拖拽功能的实现

代码片段
<!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>
new drag('#father');
new subdrag('#son');
</script>
</body>
</html>代码解析
拖拽类 drag
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: document
this.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
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 类实现了基本的拖拽功能,包括:
- 获取拖拽对象:通过选择器获取要拖拽的 dom 元素。
- 添加鼠标按下事件:当用户按下鼠标时,记录鼠标相对于元素的位置 (
dis_x,dis_y)。 - 添加鼠标移动事件:根据鼠标的当前位置更新元素的位置。
- 添加鼠标抬起事件:当用户释放鼠标时,移除鼠标移动事件,防止后续不必要的移动。
- 取消默认行为:阻止浏览器的默认拖拽行为。
subdrag 类继承自 drag 类,并在其基础上增加了边界限制,确保拖拽的元素不会超出视口范围。这使得拖拽体验更加友好和实用。
2. 观察者模式(发布-订阅模式)
代码片段 1:药店示例
<!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>
// 发布者 - 药店
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>
代码解析
观察者模式(发布-订阅模式)是一种设计模式,用于定义对象间的一种一对多依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。在上述代码中,drugstore 作为发布者,维护了一个名为 obj 的花名册,记录了不同事件的订阅者列表。
具体实现如下:
- 订阅方法
listen:接收事件名称eventname和回调函数fn,将回调函数添加到对应事件的订阅者列表中。 - 发布方法
publish:接收事件名称eventname和数据data,遍历对应的订阅者列表,依次调用每个回调函数并传递数据。
通过这种方式,多个订阅者可以根据不同的条件对同一事件做出响应。例如,在上述代码中,四个不同的订阅者(小周、小易、小王、老王)根据药店发布的口罩信息做出了不同的反应。
代码片段 2:售楼处示例
<!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>
// 发布者--售楼处
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 来记录订阅者的回调函数。不同的是,这里的订阅者只关心新楼盘推出的消息,而不需要区分不同的事件类型。
具体实现如下:
- 订阅方法
listen:接收回调函数fn并将其添加到花名册中。 - 发布方法
publish:接收数据data,遍历花名册中的所有回调函数并依次调用。
通过这种方式,多个订阅者可以根据新楼盘的信息做出不同的反应。例如,在上述代码中,三个不同的订阅者(小明、小龙、小强)根据售楼处发布的房屋信息做出了不同的决策。
结尾
通过以上两个主要部分的详细探讨,我们深入分析了 javascript 中的拖拽功能和观察者模式的实现及其应用场景。拖拽功能使得用户能够直观地操作页面元素,而观察者模式则提供了一种优雅的方式来处理对象间的依赖关系,避免了紧耦合的问题。
在实际项目中,合理运用这些技术和模式可以显著提高用户体验和代码的可维护性。希望本文的内容对读者有所帮助,欢迎继续探索 javascript 的更多可能性。无论是构建复杂的用户界面还是实现高效的数据通信,掌握这些高级编程技巧都将为开发者带来更多的便利和灵活性。
到此这篇关于javascript 拖拽与观察者模式的实现及应用的文章就介绍到这了,更多相关js 拖拽与观察者模式内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论