JavaScript中雙向數(shù)據(jù)綁定詳解
雙向數(shù)據(jù)綁定指的是將對象屬性變化綁定到UI,或者反之。換句話說,如果我們有一個擁有name屬性的user對象,當(dāng)我們給user.name賦予一個新值是UI也會相應(yīng)的顯示新的名字。同樣的,如果UI包括了一個輸入字段用來輸入用戶名,輸入一個新的值會導(dǎo)致user對象中的那么屬性發(fā)生變化。
許多流行的客戶端JavaScript框架例如Ember.js,AngularJS以及KnockoutJS都將雙向數(shù)據(jù)綁定作為自己的頭號特性。但是這并不意味著從零開始實(shí)現(xiàn)雙向數(shù)據(jù)綁定就很困難,同樣的當(dāng)我們需要雙向數(shù)據(jù)綁定時并不是只能夠選擇這些框架其中的一個。雙向數(shù)據(jù)綁定底層的思想非常的基本,它可以被壓縮成為三個步驟:
1.我們需要一個方法來識別哪個UI元素被綁定了相應(yīng)的屬性
2.我們需要監(jiān)視屬性和UI元素的變化
3.我們需要將所有變化傳播到綁定的對象和元素
雖然實(shí)現(xiàn)的方法有很多,但是最簡單也是最有效的途徑是使用發(fā)布者-訂閱者模式。思想很簡單:我們可以使用自定義的data屬性在HTML代碼中指明綁定。所有綁定起來的JavaScript對象以及DOM元素都將“訂閱”一個發(fā)布者對象。任何時候如果JavaScript對象或者一個HTML輸入字段被偵測到發(fā)生了變化,我們將代理事件到發(fā)布者-訂閱者模式,這會反過來將變化廣播并傳播到所有綁定的對象和元素。
使用jQuery的簡單實(shí)現(xiàn)
使用jQuery來實(shí)現(xiàn)雙向數(shù)據(jù)綁定非常的直接且簡單,因?yàn)檫@個流行的庫能夠是我們輕松的訂閱和發(fā)布DOM事件,以及我們自定義的事件:
function DataBinder(object_id){ //使用一個jQuery對象作為簡單的訂閱者發(fā)布者 var pubSub = jQuery({}); //我們希望一個data元素可以在表單中指明綁定:data-bind-<object_id>="<property_name>" var data_attr = "bind-" + object_id, message = object_id + ":change"; //使用data-binding屬性和代理來監(jiān)聽那個元素上的變化事件 // 以便變化能夠“廣播”到所有的關(guān)聯(lián)對象 jQuery(document).on("change","[data-" + data_attr + "]",function(evt){ var input = jQuery(this); pubSub.trigger(message, [ $input.data(data_attr),$input.val()]); }); //PubSub將變化傳播到所有的綁定元素,設(shè)置input標(biāo)簽的值或者其他標(biāo)簽的HTML內(nèi)容 pubSub.on(message,function(evt,prop_name,new_val){ jQuery("[data-" + data_attr + "=" + prop_name + "]").each(function(){ var $bound = jQuery(this); if($bound.is("input,text area,select")){ $bound.val(new_val); }else{ $bound.html(new_val); } }); }); return pubSub; }
在這個實(shí)驗(yàn)中可以按照以下代碼簡單的實(shí)現(xiàn)一個User模型:
function User(uid){ var binder = new DataBinder(uid), user = { atttibutes: {}, //屬性設(shè)置器使用數(shù)據(jù)綁定器PubSub來發(fā)布變化 set: function(attr_name,val){ this.attriures[attr_name] = val; binder.trigger(uid + ":change", [attr_name, val, this]); }, get: function(attr_name){ return this.attributes[attr_name]; }, _binder: binder }; binder.on(uid +":change",function(vet,attr_name,new_val,initiator){ if(initiator !== user){ user.set(attr_name,new_val); } }) }
現(xiàn)在,無論我們什么時候想把模型的屬性綁定到UI的一部分上,我們只需要在相應(yīng)的HTML元素上設(shè)置一個合適的data屬性即可。
//JavaScript var user = new User(123); user.set("name","Wolfgang"); //html <input type="number" data-bind-123="name" />
input字段的值會自動反映出user對象的name屬性,反之亦然。任務(wù)完成了!
不使用jQuery來創(chuàng)建數(shù)據(jù)雙向綁定
在入如今的大多數(shù)項(xiàng)目中,都可能已經(jīng)用到了jQuery,因此完全可以借用前面的例子。但是如果我們更進(jìn)一步,移除對jQuery的依賴會怎樣呢?事實(shí)上,這并不是太困難(尤其是當(dāng)我們限定只支持IE8以上的版本)。最終,我們需要使用原生的JavaScript來實(shí)現(xiàn)一個自定義的PubSub以及觀察DOM事件。
function DataBinder(object_id){ //創(chuàng)建一個簡單地PubSub對象 var pubSub = { callbacks: {}. on: function(msg,calssback){ this.callbacks[msg] = this.callbacks[msg] || []; this.callbacks[msg].push(callback); }, publish: function(msg){ this.callbacks[msg] = this.callbacks[msg] || []; for(var i = 0, len = this.callbacks[msg].length; i<lenli++){ this.callbacks[msg][i].apply(this,arguments); } } }, data_attr = "data-bind-" + object_id, message = object_id + ":change", changeHandler = function(evt){ var target = evt.target || evt.srcElemnt, //IE8兼容 prop_name = target.getAttribute(data_attr); if(prop_name && prop_name !== ""){ pubSub.publish(message,prop_name,target.value); } }; //監(jiān)聽變化事件并代理到PubSub if(document.addEventListener){ document.addEventListener("change",changeHandler,false); }else{ //IE8使用attachEvent而不是addEventListener document.attachEvent("onchange",changeHandler); } //PubSub將變化傳播到所有綁定元素 pubSub.on(message,function(vet,prop_name,new)_val){ var elements = document.querySelectorAll("[" + data_attr + "=" + prop_name + "]"), tah_name; for(var i = 0,len =elements.length; i < len; i++){ tag_name = elements[i].tagName.toLowerCase(); if(tag_name === "input" || tag_name === "textarea" || tag_name === "select"){ elements[i].value = new_val; }else{ elements[i].innerHTML = new_val; } } }); return pubSub; }
模型可以和勤勉你的例子保持一直,除了在設(shè)置器中調(diào)用那個jQuery的trigger方法之外,它需要通過調(diào)用一個自定義的PubSub的publish方法來實(shí)現(xiàn):
//在model的設(shè)置器中 function User(uid){ //... user = { //... set: function(attr_name,val){ this.attribute[attr_name] = val; //使用“publish”方法 binder.publish(uid+ ":change", attr_name, val,this); } } //... }
再一次,我們使用原生的JavaScript代碼實(shí)現(xiàn)了相同的結(jié)果,而不是使用臃腫的JavaScript框架。
本文譯自easy two way data-binding in JavaScript,原文地址http://www.lucaongaro.eu/blog/2012/12/02/easy-two-way-data-binding-in-javascript/
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- vue.js數(shù)據(jù)綁定的方法(單向、雙向和一次性綁定)
- 實(shí)現(xiàn)非常簡單的js雙向數(shù)據(jù)綁定
- javascript實(shí)現(xiàn)數(shù)據(jù)雙向綁定的三種方式小結(jié)
- 輕松實(shí)現(xiàn)javascript數(shù)據(jù)雙向綁定
- JS原生數(shù)據(jù)雙向綁定實(shí)現(xiàn)代碼
- JS數(shù)據(jù)雙向綁定原理與用法實(shí)例分析
- js實(shí)現(xiàn)視圖和數(shù)據(jù)雙向綁定的方法分析
- js項(xiàng)目中雙向數(shù)據(jù)綁定的簡單實(shí)現(xiàn)方法
相關(guān)文章
HTML中使背景圖片自適應(yīng)瀏覽器大小實(shí)例詳解
這篇文章主要介紹了HTML中使背景圖片自適應(yīng)瀏覽器大小實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04獲取任意Html元素與body之間的偏移距離 offsetTop、offsetLeft (For:IE5+ FF1 )[
獲取任意Html元素與body之間的偏移距離 offsetTop、offsetLeft (For:IE5+ FF1 )[...2006-12-12javascript 動態(tài)修改樣式和層疊樣式表代碼
javascript 動態(tài)修改樣式和層疊樣式表代碼,需要的朋友可以參考下。2010-04-04ES6中Set和Map數(shù)據(jù)結(jié)構(gòu),Map與其它數(shù)據(jù)結(jié)構(gòu)互相轉(zhuǎn)換操作實(shí)例詳解
這篇文章主要介紹了ES6中Set和Map數(shù)據(jù)結(jié)構(gòu),Map與其它數(shù)據(jù)結(jié)構(gòu)互相轉(zhuǎn)換操作,結(jié)合實(shí)例形式詳細(xì)分析了ES6中的Set和Map數(shù)據(jù)結(jié)構(gòu)的概念、原理、遍歷、去重等操作,以及Map與其它數(shù)據(jù)結(jié)構(gòu)互相轉(zhuǎn)換操作,需要的朋友可以參考下2019-02-02詳解JavaScript表單驗(yàn)證(E-mail 驗(yàn)證)
這篇文章主要為大家詳細(xì)介紹了JavaScript表單驗(yàn)證,重點(diǎn)介紹了E-mail驗(yàn)證,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-03-03JS利用ffmpeg和sharp玩轉(zhuǎn)音視頻和圖片
ffmpeg?是一個非常流行的開源軟件套件,用于處理音頻和視頻數(shù)據(jù),而要想對圖片之類的進(jìn)行壓縮,我們可以選擇?sharp?來進(jìn)行操作,所以下面我們就來學(xué)習(xí)一下前端如何利用ffmpeg和sharp玩轉(zhuǎn)音視頻和圖片吧2023-10-10echarts幾個公司內(nèi)部數(shù)據(jù)可視化圖表必收藏
最近公司有一個需求,要做一個數(shù)據(jù)可視化的頁面,所有的圖表都在下面,做這些都是本人自己寫的,全部都是真是的項(xiàng)目中的代碼,包含有柱狀圖、折線圖、水球圖以及散點(diǎn)圖,這里直接打出來給大家練手,希望大家多多支持,如果這篇文章對您有用的話必須收藏2022-08-08