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

 更新时间:2025年01月15日 10:57:24   作者:前端青山  
本文通过代码片段详细介绍了JavaScript中的拖拽功能和观察者模式(发布-订阅模式)的实现及其应用场景,拖拽功能允许用户通过鼠标移动元素,而观察者模式则定义了一种一对多的依赖关系,使得对象能够自动更新,感兴趣的朋友跟随小编一起看看吧

前言

本文将通过几个具体的代码片段,深入探讨 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 拖拽与观察者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JSON.stringify()方法使用小结

    JSON.stringify()方法使用小结

    本文主要介绍了JSON.stringify()方法使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-11-11
  • js和as的稳定传值问题解决

    js和as的稳定传值问题解决

    最近在实现flash的播放音乐的功能,这就涉及到js和as交互的问题,因为要实现动态改变音乐文件的功能,可是如何判定呢?
    2013-07-07
  • JS实现登录页密码的显示和隐藏功能

    JS实现登录页密码的显示和隐藏功能

    在登录页经常会用到通过点击文本框的类似小眼睛图片来实现隐藏显示密码的功能。下面给大家介绍基于JS实现登录页密码的显示和隐藏功能,需要的朋友参考下吧
    2017-12-12
  • 微信小程序顶部导航栏滑动tab效果

    微信小程序顶部导航栏滑动tab效果

    这篇文章主要为大家详细介绍了微信小程序顶部导航栏滑动tab效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • Javascript实现商品秒杀倒计时(时间与服务器时间同步)

    Javascript实现商品秒杀倒计时(时间与服务器时间同步)

    在一些购物商城经常看到有很多商品做秒杀活动,也就是倒计时,本篇文章给大家介绍Javascript实现商品秒杀倒计时(时间与服务器时间同步),需要的朋友可以了解下
    2015-09-09
  • 解析ES6中的解构赋值(数组,对象,嵌套,默认值)

    解析ES6中的解构赋值(数组,对象,嵌套,默认值)

    解构赋值是一种特殊的语法,它使我们可以将数组或对象“拆包”至一系列变量中,因为有时这样更方便,接下来通过本文给大家介绍ES6中的解构赋值(数组,对象,嵌套,默认值),需要的朋友可以参考下
    2022-11-11
  • bootstrap daterangepicker汉化以及扩展功能

    bootstrap daterangepicker汉化以及扩展功能

    这篇文章主要为大家详细 介绍了bootstrap daterangepicker汉化以及扩展功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • js检测客户端不是firefox则提示下载

    js检测客户端不是firefox则提示下载

    js检测客户端不是firefox则提示下载...
    2007-04-04
  • Cpage.js给组件绑定事件的实现代码

    Cpage.js给组件绑定事件的实现代码

    Cpage.js是一款轻量级的Mvvm框架,使用TypeScript(JavaScript的超集)开发。下面通过本文给大家分享Cpage.js给组件绑定事件的实现代码,需要的的朋友参考下吧
    2017-08-08
  • JavaScript中的对象继承关系

    JavaScript中的对象继承关系

    这篇文章主要介绍了JavaScript中的对象继承关系的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-08-08

最新评论