JavaScript 拖拽與觀察者模式的實(shí)現(xiàn)及應(yīng)用小結(jié)
前言
本文將通過幾個具體的代碼片段,深入探討 JavaScript 中的拖拽功能和觀察者模式(發(fā)布-訂閱模式)的實(shí)現(xiàn)及其應(yīng)用場景。
這些代碼片段不僅展示了如何實(shí)現(xiàn)這些功能,還解釋了其背后的原理和實(shí)際用途。通過閱讀本文,讀者可以更好地理解 JavaScript 的高級特性,并將其應(yīng)用到實(shí)際項(xiàng)目中。
1. 拖拽功能的實(shí)現(xiàn)
代碼片段
<!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 對象,這里需要一個實(shí)例對象 this.fnDown(evt); }.bind(this); } fnDown(evt) { let e = evt || window.event; // 求鼠標(biāo)的相對坐標(biāo)值 this.dis_x = e.offsetX; this.dis_y = e.offsetY; // 移動事件 document.onmousemove = function (evt) { // this: document this.fnMove(evt); }.bind(this); // 鼠標(biāo)抬起事件 document.onmouseup = this.fnUp.bind(this); // 取消默認(rèn)行為 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) { // 調(diào)用父類的構(gòu)造函數(shù) super(selector); } fnMove(evt) { let e = evt || window.event; let left = e.pageX - this.dis_x; let top = e.pageY - this.dis_y; // 設(shè)置邊界 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'; } }
功能說明
拖拽功能 是一種常見的用戶交互方式,允許用戶通過鼠標(biāo)移動元素。在上述代碼中,Drag
類實(shí)現(xiàn)了基本的拖拽功能,包括:
- 獲取拖拽對象:通過選擇器獲取要拖拽的 DOM 元素。
- 添加鼠標(biāo)按下事件:當(dāng)用戶按下鼠標(biāo)時,記錄鼠標(biāo)相對于元素的位置 (
dis_x
,dis_y
)。 - 添加鼠標(biāo)移動事件:根據(jù)鼠標(biāo)的當(dāng)前位置更新元素的位置。
- 添加鼠標(biāo)抬起事件:當(dāng)用戶釋放鼠標(biāo)時,移除鼠標(biāo)移動事件,防止后續(xù)不必要的移動。
- 取消默認(rèn)行為:阻止瀏覽器的默認(rèn)拖拽行為。
Subdrag
類繼承自 Drag
類,并在其基礎(chǔ)上增加了邊界限制,確保拖拽的元素不會超出視口范圍。這使得拖拽體驗(yàn)更加友好和實(shí)用。
2. 觀察者模式(發(fā)布-訂閱模式)
代碼片段 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> // 發(fā)布者 - 藥店 let drugstore = { // 花名冊 obj: {}, // 訂閱方法 listen: function (eventName, fn) { if (!(this.obj[eventName])) { this.obj[eventName] = []; } this.obj[eventName].push(fn); }, // 發(fā)布方法 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('先少買一點(diǎn),等有普通醫(yī)用口罩時,再多屯點(diǎn)'); } } drugstore.listen('mask', xiaoyi); // 小王 function xiaowang(data) { if (data.num > 100) { console.log('多屯點(diǎn),再高價賣給別人'); } } drugstore.listen('mask', xiaowang); // 老王 function laowang(data) { console.log('有佛祖保佑,不需要口罩'); } drugstore.listen('mask', laowang); drugstore.publish('mask', data); </script> </body> </html>
代碼解析
觀察者模式(發(fā)布-訂閱模式)是一種設(shè)計(jì)模式,用于定義對象間的一種一對多依賴關(guān)系。當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都會收到通知并自動更新。在上述代碼中,drugstore
作為發(fā)布者,維護(hù)了一個名為 obj
的花名冊,記錄了不同事件的訂閱者列表。
具體實(shí)現(xiàn)如下:
- 訂閱方法
listen
:接收事件名稱eventName
和回調(diào)函數(shù)fn
,將回調(diào)函數(shù)添加到對應(yīng)事件的訂閱者列表中。 - 發(fā)布方法
publish
:接收事件名稱eventName
和數(shù)據(jù)data
,遍歷對應(yīng)的訂閱者列表,依次調(diào)用每個回調(diào)函數(shù)并傳遞數(shù)據(jù)。
通過這種方式,多個訂閱者可以根據(jù)不同的條件對同一事件做出響應(yīng)。例如,在上述代碼中,四個不同的訂閱者(小周、小易、小王、老王)根據(jù)藥店發(fā)布的口罩信息做出了不同的反應(yīng)。
代碼片段 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> // 發(fā)布者--售樓處 let salesOffice = { // 花名冊 arr: [], // 訂閱方法 listen: function (fn) { this.arr.push(fn); }, // 發(fā)布消息 publish: function (data) { // 遍歷花名冊 this.arr.forEach(fn => { fn(data); }); } }; // 發(fā)布的消息 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('預(yù)算充足,價格還可以,馬上去訂購一套'); } }); // 小強(qiáng) salesOffice.listen(function (data) { if (parseInt(data.num) > 30) { console.log('趕緊去,先屯20套再說'); } }); // 發(fā)布消息 salesOffice.publish(data); </script> </body> </html>
代碼解析
在售樓處示例中,salesOffice
作為發(fā)布者,同樣使用了花名冊 arr
來記錄訂閱者的回調(diào)函數(shù)。不同的是,這里的訂閱者只關(guān)心新樓盤推出的消息,而不需要區(qū)分不同的事件類型。
具體實(shí)現(xiàn)如下:
- 訂閱方法
listen
:接收回調(diào)函數(shù)fn
并將其添加到花名冊中。 - 發(fā)布方法
publish
:接收數(shù)據(jù)data
,遍歷花名冊中的所有回調(diào)函數(shù)并依次調(diào)用。
通過這種方式,多個訂閱者可以根據(jù)新樓盤的信息做出不同的反應(yīng)。例如,在上述代碼中,三個不同的訂閱者(小明、小龍、小強(qiáng))根據(jù)售樓處發(fā)布的房屋信息做出了不同的決策。
結(jié)尾
通過以上兩個主要部分的詳細(xì)探討,我們深入分析了 JavaScript 中的拖拽功能和觀察者模式的實(shí)現(xiàn)及其應(yīng)用場景。拖拽功能使得用戶能夠直觀地操作頁面元素,而觀察者模式則提供了一種優(yōu)雅的方式來處理對象間的依賴關(guān)系,避免了緊耦合的問題。
在實(shí)際項(xiàng)目中,合理運(yùn)用這些技術(shù)和模式可以顯著提高用戶體驗(yàn)和代碼的可維護(hù)性。希望本文的內(nèi)容對讀者有所幫助,歡迎繼續(xù)探索 JavaScript 的更多可能性。無論是構(gòu)建復(fù)雜的用戶界面還是實(shí)現(xiàn)高效的數(shù)據(jù)通信,掌握這些高級編程技巧都將為開發(fā)者帶來更多的便利和靈活性。
到此這篇關(guān)于JavaScript 拖拽與觀察者模式的實(shí)現(xiàn)及應(yīng)用的文章就介紹到這了,更多相關(guān)js 拖拽與觀察者模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 使用JavaScript輕松實(shí)現(xiàn)拖拽功能
- vue elementUi+sortable.js實(shí)現(xiàn)嵌套表格拖拽問題
- 淺析原生JavaScript中拖拽屬性draggable的使用
- JavaScript實(shí)現(xiàn)文件的拖拽上傳功能
- vue結(jié)合el-table實(shí)現(xiàn)表格行拖拽排序(基于sortablejs)
- element-plus結(jié)合sortablejs實(shí)現(xiàn)table行拖拽效果
- 手把手教你用Javascript實(shí)現(xiàn)觀察者模式
- js觀察者模式的介紹及使用
- js觀察者模式的彈幕案例
- JavaScript設(shè)計(jì)模式之觀察者模式與發(fā)布訂閱模式詳解
- javascript設(shè)計(jì)模式 – 觀察者模式原理與用法實(shí)例分析
相關(guān)文章
js數(shù)值和和字符串進(jìn)行轉(zhuǎn)換時可以對不同進(jìn)制進(jìn)行操作
這篇文章主要介紹了js數(shù)值和和字符串進(jìn)行轉(zhuǎn)換時可以對不同進(jìn)制進(jìn)行操作,需要的朋友可以參考下2014-03-03JavaScript實(shí)現(xiàn)控制并發(fā)請求的方法詳解
這篇文章主要為大家詳細(xì)介紹了如果有100個請求,那么如何使用JavaScript實(shí)現(xiàn)控制并發(fā)請求,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一2024-03-03BootStrap Datetimepicker 漢化的實(shí)現(xiàn)代碼
這篇文章主要介紹了 BootStrap Datetimepicker 漢化的實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-02-02淺談javascript函數(shù)劫持[轉(zhuǎn)自xfocus]
javascript函數(shù)劫持,也就是老外提到的javascript hijacking技術(shù)。最早還是和劍心同學(xué)討論問題時偶然看到的一段代碼2008-02-02谷歌瀏覽器不支持showModalDialog模態(tài)對話框的解決方法
谷歌瀏覽器不支持showModalDialog模態(tài)對話框和無法返回returnValue,這個問題,想必很多朋友都有遇到過吧,解決方法很簡單,下面的思路,大家可以看看2014-09-09