双向数据绑定指的就是,绑定对象属性的改变到用户界面的变化的能力,反之亦然。
换种说法,如果我们有一个user对象和一个name属性,一旦我们赋了一个新值给user.name,在UI上就会显示新的姓名了。同样地,如果UI包含了一个输入用户姓名的输入框,输入一个新值就应该会使user对象的name属性做出相应的改变。
- 我们需要一个UI元素和属性相互绑定的方法
- 我们需要监视属性和UI元素的变化
- 我们需要让所有绑定的对象和元素都能感知到变化
还是有很多方法能够实现上面的想法,有一个简单有效的方法就是使用PubSub模式。
这个思路很简单:我们使用数据特性来为HTML代码(UI元素)进行绑定,所有被绑定在一起的JavaScript对象和DOM元素都会订阅一个PubSub对象。只要JavaScript对象或者一个HTML输入元素监听到数据的变化时,就会触发绑定到PubSub对象上的事件,从而其他绑定的对象和元素都会做出相应的变化。
<h1>原生js双向绑定及其应用</h1> <div class="js-2-1section2 col-sm-10 col-xs-10"> <div><input type="text" data-bind-1="peopleName" id="text1"/> <!--data-bind-1="peopleName" 原生js双向绑定的格式--> </div> <div><input type="text" data-bind-2="killName" id="text2" /> <button class="btn btn-primary" onclick="randomGroup()">随机词组</button> </div> <p data-bind-1="peopleName"></p> <p data-bind-2="killName"></p> </div>
var DBind1 = new DBind( 1 ); var DBind2 = new DBind( 2 );//前面是变量,括号里面的是html那里填的数字 DBind1.set( "peopleName", '第一个' ); DBind2.set( "killName", '第二个' );//第一个是刚才html格式那里的变量名,第二个方框是赋值 function DataBinder( object_id ) { // 创建一个简单的pubSub对象 var pubSub = { callbacks: {}, on: function( msg, callback ) { 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 < len; i++ ) { this.callbacks[ msg ][ i ].apply( this, arguments ); } } }, data_attr = "data-bind-" + object_id, message = object_id + ":input", timeIn; changeHandler = function( evt ) { var target = evt.target || evt.srcElement, // IE8兼容 prop_name = target.getAttribute( data_attr ); if ( prop_name && prop_name !== "" ) { clearTimeout(timeIn); timeIn = setTimeout(function(){ pubSub.publish( message, prop_name, target.value ); },50); } }; // 监听事件变化,并代理到pubSub if ( document.addEventListener ) { document.addEventListener( "input", changeHandler, false ); } else { // IE8使用attachEvent而不是addEventListenter document.attachEvent( "oninput", changeHandler ); } // pubSub将变化传播到所有绑定元素 pubSub.on( message, function( evt, prop_name, new_val ) { var elements = document.querySelectorAll("[" + data_attr + "=" + prop_name + "]"), tag_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; } function DBind( uid ) { var binder = new DataBinder( uid ), user = { // 属性设置器使用数据绑定器pubSub来发布 attributes: {}, set: function( attr_name, val ) { this.attributes[ attr_name ] = val; // Use the `publish` method binder.publish( uid + ":input", attr_name, val, this ); }, get: function( attr_name ) { return this.attributes[ attr_name ]; }, _binder: binder }; // Subscribe to the PubSub binder.on( uid + ":input", function( evt, attr_name, new_val, initiator ) { if ( initiator !== user ) { user.set( attr_name, new_val ); } }); return user; }
双向绑定应用实例
function randomGroup() { var oGroup=[];//存放所有词汇的词组、 for(var i=0;i<20;i++){ oGroup[i]={};//设置数组中的每个元素都是一个对象 } //一个一个定义他们状态的字符串,然后在下面赋值 oGroup[0].people="降龙十八掌"; oGroup[0].killer="九阴白骨爪"; oGroup[1].people="快乐大本营"; oGroup[1].killer="天天向上"; oGroup[2].people="零花钱"; oGroup[2].killer="生活费"; oGroup[3].people="爷爷"; oGroup[3].killer="姥爷"; oGroup[4].people="同学"; oGroup[4].killer="同桌"; oGroup[5].people="小沈阳"; oGroup[5].killer="宋小宝"; oGroup[6].people="成吉思汗"; oGroup[6].killer="努尔哈赤"; oGroup[7].people="谢娜张杰"; oGroup[7].killer="邓超孙俪"; oGroup[8].people="新年"; oGroup[8].killer="跨年"; oGroup[9].people="保安"; oGroup[9].killer="保镖"; oGroup[10].people="眉毛"; oGroup[10].killer="胡须"; oGroup[11].people="端午节"; oGroup[11].killer="中秋节"; oGroup[12].people="摩托车"; oGroup[12].killer="电动车"; oGroup[13].people="高跟鞋"; oGroup[13].killer="增高鞋"; oGroup[14].people="汉堡包"; oGroup[14].killer="肉夹馍"; oGroup[15].people="牛奶"; oGroup[15].killer="豆浆"; oGroup[16].people="唇膏"; oGroup[16].killer="口红"; oGroup[17].people="公交"; oGroup[17].killer="地铁"; oGroup[18].people="结婚"; oGroup[18].killer="订婚"; oGroup[19].people="面包"; oGroup[19].killer="蛋糕"; //词汇出自——————谁是卧底的词汇大全 var oGroupNum=Math.floor(Math.random()*20);//抽取一个随机数,随机数范围跟上面数组的长度是一致的 oPeople=oGroup[oGroupNum].people; okiller=oGroup[oGroupNum].killer;//随机数的对应下标的状态字符串赋值给这个变量。 console.log(oPeople,okiller); DBind1.set( "peopleName", oPeople );//将上面的状态字符串赋值给input框,。这一步将在界面中直接显示出来 DBind2.set( "killName", okiller ); }