首页 文章

如何在JavaScript中实现DOM数据绑定

提问于
浏览
220

请将此问题视为严格教育 . I'm still interested in hearing new answers and ideas to implement this

tl;博士

我如何使用JavaScript实现双向数据绑定?

数据绑定到DOM

通过数据绑定到DOM我的意思是,例如,拥有一个带有属性 b 的JavaScript对象 a . 然后有一个 <input> DOM元素(例如),当DOM元素改变时, a 改变,反之亦然(也就是说,我的意思是双向数据绑定) .

以下是AngularJS的示意图:

two way data binding

所以基本上我有类似的JavaScript:

var a = {b:3};

然后输入(或其他形式)元素,如:

<input type='text' value=''>

我'd like the input'的值是 a.b 's value (for example), and when the input text changes, I' d,如 a.b 也要改变 . 当 a.b 在JavaScript中更改时,输入会更改 .

问题

在纯JavaScript中完成此操作的基本技术有哪些?

具体来说,我想要一个很好的答案来参考:

  • 如何绑定对象的工作?

  • 如何听取表格中的变化可能有效?

  • 是否可以以简单的方式仅在模板级别修改HTML?我想不跟踪HTML文档本身的绑定,而只是跟踪JavaScript(使用DOM事件,并且JavaScript保持对所使用的DOM元素的引用) .

我尝试了什么?

我是Mustache的忠实粉丝,所以我尝试用它来模板化 . 但是,当我尝试执行数据绑定本身时遇到了问题,因为Mustache将HTML作为字符串处理,因此在得到结果之后,我没有引用viewmodel中对象的位置 . 我能想到的唯一解决方法是使用属性修改HTML字符串(或创建DOM树)本身 . 我不介意使用不同的模板引擎 .

基本上,我有一种强烈的感觉,我正在使手头的问题变得复杂,并且有一个简单的解决方案 .

Note: 请不要提供使用外部库的答案,特别是那些数千行代码的库 . 我've used (and like!) AngularJS and KnockoutJS. I really don'想要答案的形式是'use framework x' . 最理想的是,我知道如何使用许多框架来掌握如何自己实现双向数据绑定 . 我不希望得到一个完整的答案,而是一个能够理解这个想法的答案 .

12 回答

  • 97

    如何绑定对象的工作?如何听取表格中的变化可能有效?

    更新两个对象的抽象

    我想还有其他技术,但最终我有一个对象,它持有对相关DOM元素的引用,并提供一个接口来协调对自己的数据及其相关元素的更新 .

    .addEventListener() 为此提供了一个非常好的界面 . 您可以为它提供一个实现 eventListener 接口的对象,并且它将使用该对象作为 this 值调用其处理程序 .

    这使您可以自动访问元素及其相关数据 .

    定义对象

    原型继承是实现这一目标的好方法,当然不是必需的 . 首先,您将创建一个接收元素和一些初始数据的构造函数 .

    function MyCtor(element, data) {
        this.data = data;
        this.element = element;
        element.value = data;
        element.addEventListener("change", this, false);
    }
    

    所以这里的构造函数将元素和数据存储在新对象的属性中 . 它还将 change 事件绑定到给定的 element . 有趣的是它传递新对象而不是函数作为第二个参数 . 但仅此一点是行不通的 .

    实现eventListener接口

    要使其工作,您的对象需要实现 eventListener 接口 . 完成此任务所需要的只是为对象提供 handleEvent() 方法 .

    这就是继承的来源 .

    MyCtor.prototype.handleEvent = function(event) {
        switch (event.type) {
            case "change": this.change(this.element.value);
        }
    };
    
    MyCtor.prototype.change = function(value) {
        this.data = value;
        this.element.value = value;
    };
    

    有许多不同的方式可以构建它,但是对于协调更新的示例,我决定使 change() 方法只接受一个值,并让 handleEvent 传递该值而不是事件对象 . 这样, change() 也可以在没有事件的情况下调用 .

    所以现在,当 change 事件发生时,它将更新元素和 .data 属性 . 当您在JavaScript程序中调用 .change() 时也会发生同样的情况 .

    使用代码

    现在,您只需创建新对象,然后让它执行更新 . JS代码中的更新将出现在输入中,并且JS代码可以看到输入上的更改事件 .

    var obj = new MyCtor(document.getElementById("foo"), "20");
    
    // simulate some JS based changes.
    var i = 0;
    setInterval(function() {
        obj.change(parseInt(obj.element.value) + ++i);
    }, 3000);
    

    DEMO: http://jsfiddle.net/RkTMD/

  • 24

    所以,我决定把自己的解决方案扔进锅里 . 这是working fiddle . 请注意,这只会运行在非常现代的浏览器上

    它的用途

    这种实现非常现代 - 它需要一个(非常)现代的浏览器和用户两种新技术:

    • MutationObservers检测dom中的更改(也使用事件侦听器)

    • Object.observe检测对象的变化并通知dom . 危险,因为这个答案已经写成了O.o已经被ECMAScript TC讨论并决定反对,考虑使用polyfill .

    它是如何工作的

    • 在元素上,放一个 domAttribute:objAttribute 映射 - 例如 bind='textContent:name'

    • 在dataBind函数中读取它 . 观察元素和对象的更改 .

    • 发生更改时 - 更新相关元素 .

    解决方案

    这是 dataBind 函数,请注意它只有20行代码,可能更短:

    function dataBind(domElement, obj) {    
        var bind = domElement.getAttribute("bind").split(":");
        var domAttr = bind[0].trim(); // the attribute on the DOM element
        var itemAttr = bind[1].trim(); // the attribute the object
    
        // when the object changes - update the DOM
        Object.observe(obj, function (change) {
            domElement[domAttr] = obj[itemAttr]; 
        });
        // when the dom changes - update the object
        new MutationObserver(updateObj).observe(domElement, { 
            attributes: true,
            childList: true,
            characterData: true
        });
        domElement.addEventListener("keyup", updateObj);
        domElement.addEventListener("click",updateObj);
        function updateObj(){
            obj[itemAttr] = domElement[domAttr];   
        }
        // start the cycle by taking the attribute from the object and updating it.
        domElement[domAttr] = obj[itemAttr]; 
    }
    

    这是一些用法:

    HTML:

    <div id='projection' bind='textContent:name'></div>
    <input type='text' id='textView' bind='value:name' />
    

    JavaScript的:

    var obj = {
        name: "Benjamin"
    };
    var el = document.getElementById("textView");
    dataBind(el, obj);
    var field = document.getElementById("projection");
    dataBind(field,obj);
    

    这是working fiddle . 请注意,此解决方案非常通用 . 可以使用Object.observe和变异观察器填充 .

  • 34

    我想补充一下我的前言 . 我建议采用略有不同的方法,只需在不使用方法的情况下为对象分配新值即可 . 必须注意的是,特别是旧版浏览器不支持这一点,IE9仍然需要使用不同的界面 .

    最值得注意的是我的方法没有利用事件 .

    吸气员和二传手

    我的建议利用了getters and setters的相对年轻的特征,特别是仅限制定者 . 一般来说,mutators允许我们__804011_某些属性被赋值和检索的行为 .

    我将在这里使用的一个实现是Object.defineProperty方法 . 它适用于FireFox,GoogleChrome和 - 我认为 - IE9 . 没有测试过其他浏览器,但因为这只是理论...

    无论如何,它接受三个参数 . 第一个参数是您希望为其定义新属性的对象,第二个参数是类似于新属性名称的字符串,最后一个是“描述符对象”,提供有关新属性行为的信息 .

    两个特别有趣的描述符是 getset . 一个例子看起来像下面这样 . 请注意,使用这两个禁止使用其他4个描述符 .

    function MyCtor( bindTo ) {
        // I'll omit parameter validation here.
    
        Object.defineProperty(this, 'value', {
            enumerable: true,
            get : function ( ) {
                return bindTo.value;
            },
            set : function ( val ) {
                bindTo.value = val;
            }
        });
    }
    

    现在使用它变得略有不同:

    var obj = new MyCtor(document.getElementById('foo')),
        i = 0;
    setInterval(function() {
        obj.value += ++i;
    }, 3000);
    

    I want to emphasize that this only works for modern browsers.

    工作小提琴:http://jsfiddle.net/Derija93/RkTMD/1/

  • 7

    我认为我的答案将更具技术性,但并没有不同,因为其他人使用不同的技术呈现相同的东西 .
    所以,首先,这个问题的解决方案是使用一种称为"observer"的设计模式,它让's you decouple your data from your presentation, making the change in one thing be broadcasted to their listeners, but in this case it'做成了双向 .

    对于DOM到JS的方式

    要将数据从DOM绑定到js对象,您可以以 data 属性(或类,如果需要兼容性)的形式添加标记,如下所示:

    <input type="text" data-object="a" data-property="b" id="b" class="bind" value=""/>
    <input type="text" data-object="a" data-property="c" id="c" class="bind" value=""/>
    <input type="text" data-object="d" data-property="e" id="e" class="bind" value=""/>
    

    这样就可以通过js使用querySelectorAll(或者老友getElementsByClassName来兼容)来访问它 .

    现在,您可以将侦听更改的事件绑定到以下方式:每个对象一个侦听器或容器/文档的一个大侦听器 . 绑定到文档/容器将触发事件,对于它或它的子进行的每个更改,它将具有更小的内存占用但将产生事件调用 .
    代码看起来像这样:

    //Bind to each element
    var elements = document.querySelectorAll('input[data-property]');
    
    function toJS(){
        //Assuming `a` is in scope of the document
        var obj = document[this.data.object];
        obj[this.data.property] = this.value;
    }
    
    elements.forEach(function(el){
        el.addEventListener('change', toJS, false);
    }
    
    //Bind to document
    function toJS2(){
        if (this.data && this.data.object) {
            //Again, assuming `a` is in document's scope
            var obj = document[this.data.object];
            obj[this.data.property] = this.value;
        }
    }
    
    document.addEventListener('change', toJS2, false);
    

    对于JS do DOM方式

    您将需要两件事:一个元对象将保存女巫DOM元素的引用绑定到每个js对象/属性以及一种监听对象更改的方式 . 它基本上是相同的方式:您必须有一种方法来监听对象中的更改然后将其绑定到DOM节点,因为您的对象“不能拥有”元数据,您将需要另一个以某种方式保存元数据的对象属性名称映射到元数据对象的属性 . 代码将是这样的:

    var a = {
            b: 'foo',
            c: 'bar'
        },
        d = {
            e: 'baz'
        },
        metadata = {
            b: 'b',
            c: 'c',
            e: 'e'
        };
    function toDOM(changes){
        //changes is an array of objects changed and what happened
        //for now i'd recommend a polyfill as this syntax is still a proposal
        changes.forEach(function(change){
            var element = document.getElementById(metadata[change.name]);
            element.value = change.object[change.name];
        });
    }
    //Side note: you can also use currying to fix the second argument of the function (the toDOM method)
    Object.observe(a, toDOM);
    Object.observe(d, toDOM);
    

    我希望我有所帮助 .

  • 6

    昨天,我开始编写自己的绑定数据的方法 .

    玩它很有趣 .

    我认为它很漂亮,非常有用 . 至少在我使用firefox和chrome的测试中,Edge也必须正常工作 . 不确定其他人,但如果他们支持代理,我认为它会工作 .

    https://jsfiddle.net/2ozoovne/1/

    <H1>Bind Context 1</H1>
    <input id='a' data-bind='data.test' placeholder='Button Text' />
    <input id='b' data-bind='data.test' placeholder='Button Text' />
    <input type=button id='c' data-bind='data.test' />
    <H1>Bind Context 2</H1>
    <input id='d' data-bind='data.otherTest' placeholder='input bind' />
    <input id='e' data-bind='data.otherTest' placeholder='input bind' />
    <input id='f' data-bind='data.test' placeholder='button 2 text - same var name, other context' />
    <input type=button id='g' data-bind='data.test' value='click here!' />
    <H1>No bind data</H1>
    <input id='h' placeholder='not bound' />
    <input id='i' placeholder='not bound'/>
    <input type=button id='j' />
    

    这是代码:

    (function(){
        if ( ! ( 'SmartBind' in window ) ) { // never run more than once
            // This hack sets a "proxy" property for HTMLInputElement.value set property
            var nativeHTMLInputElementValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
            var newDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
            newDescriptor.set=function( value ){
                if ( 'settingDomBind' in this )
                    return;
                var hasDataBind=this.hasAttribute('data-bind');
                if ( hasDataBind ) {
                    this.settingDomBind=true;
                    var dataBind=this.getAttribute('data-bind');
                    if ( ! this.hasAttribute('data-bind-context-id') ) {
                        console.error("Impossible to recover data-bind-context-id attribute", this, dataBind );
                    } else {
                        var bindContextId=this.getAttribute('data-bind-context-id');
                        if ( bindContextId in SmartBind.contexts ) {
                            var bindContext=SmartBind.contexts[bindContextId];
                            var dataTarget=SmartBind.getDataTarget(bindContext, dataBind);
                            SmartBind.setDataValue( dataTarget, value);
                        } else {
                            console.error( "Invalid data-bind-context-id attribute", this, dataBind, bindContextId );
                        }
                    }
                    delete this.settingDomBind;
                }
                nativeHTMLInputElementValue.set.bind(this)( value );
            }
            Object.defineProperty(HTMLInputElement.prototype, 'value', newDescriptor);
    
        var uid= function(){
               return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                   var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
                   return v.toString(16);
              });
       }
    
            // SmartBind Functions
            window.SmartBind={};
            SmartBind.BindContext=function(){
                var _data={};
                var ctx = {
                    "id" : uid()    /* Data Bind Context Id */
                    , "_data": _data        /* Real data object */
                    , "mapDom": {}          /* DOM Mapped objects */
                    , "mapDataTarget": {}       /* Data Mapped objects */
                }
                SmartBind.contexts[ctx.id]=ctx;
                ctx.data=new Proxy( _data, SmartBind.getProxyHandler(ctx, "data"))  /* Proxy object to _data */
                return ctx;
            }
    
            SmartBind.getDataTarget=function(bindContext, bindPath){
                var bindedObject=
                    { bindContext: bindContext
                    , bindPath: bindPath 
                    };
                var dataObj=bindContext;
                var dataObjLevels=bindPath.split('.');
                for( var i=0; i<dataObjLevels.length; i++ ) {
                    if ( i == dataObjLevels.length-1 ) { // last level, set value
                        bindedObject={ target: dataObj
                        , item: dataObjLevels[i]
                        }
                    } else {    // digg in
                        if ( ! ( dataObjLevels[i] in dataObj ) ) {
                            console.warn("Impossible to get data target object to map bind.", bindPath, bindContext);
                            break;
                        }
                        dataObj=dataObj[dataObjLevels[i]];
                    }
                }
                return bindedObject ;
            }
    
            SmartBind.contexts={};
            SmartBind.add=function(bindContext, domObj){
                if ( typeof domObj == "undefined" ){
                    console.error("No DOM Object argument given ", bindContext);
                    return;
                }
                if ( ! domObj.hasAttribute('data-bind') ) {
                    console.warn("Object has no data-bind attribute", domObj);
                    return;
                }
                domObj.setAttribute("data-bind-context-id", bindContext.id);
                var bindPath=domObj.getAttribute('data-bind');
                if ( bindPath in bindContext.mapDom ) {
                    bindContext.mapDom[bindPath][bindContext.mapDom[bindPath].length]=domObj;
                } else {
                    bindContext.mapDom[bindPath]=[domObj];
                }
                var bindTarget=SmartBind.getDataTarget(bindContext, bindPath);
                bindContext.mapDataTarget[bindPath]=bindTarget;
                domObj.addEventListener('input', function(){ SmartBind.setDataValue(bindTarget,this.value); } );
                domObj.addEventListener('change', function(){ SmartBind.setDataValue(bindTarget, this.value); } );
            }
    
            SmartBind.setDataValue=function(bindTarget,value){
                if ( ! ( 'target' in bindTarget ) ) {
                    var lBindTarget=SmartBind.getDataTarget(bindTarget.bindContext, bindTarget.bindPath);
                    if ( 'target' in lBindTarget ) {
                        bindTarget.target=lBindTarget.target;
                        bindTarget.item=lBindTarget.item;
                    } else {
                        console.warn("Still can't recover the object to bind", bindTarget.bindPath );
                    }
                }
                if ( ( 'target' in bindTarget ) ) {
                    bindTarget.target[bindTarget.item]=value;
                }
            }
            SmartBind.getDataValue=function(bindTarget){
                if ( ! ( 'target' in bindTarget ) ) {
                    var lBindTarget=SmartBind.getDataTarget(bindTarget.bindContext, bindTarget.bindPath);
                    if ( 'target' in lBindTarget ) {
                        bindTarget.target=lBindTarget.target;
                        bindTarget.item=lBindTarget.item;
                    } else {
                        console.warn("Still can't recover the object to bind", bindTarget.bindPath );
                    }
                }
                if ( ( 'target' in bindTarget ) ) {
                    return bindTarget.target[bindTarget.item];
                }
            }
            SmartBind.getProxyHandler=function(bindContext, bindPath){
                return  {
                    get: function(target, name){
                        if ( name == '__isProxy' )
                            return true;
                        // just get the value
                        // console.debug("proxy get", bindPath, name, target[name]);
                        return target[name];
                    }
                    ,
                    set: function(target, name, value){
                        target[name]=value;
                        bindContext.mapDataTarget[bindPath+"."+name]=value;
                        SmartBind.processBindToDom(bindContext, bindPath+"."+name);
                        // console.debug("proxy set", bindPath, name, target[name], value );
                        // and set all related objects with this target.name
                        if ( value instanceof Object) {
                            if ( !( name in target) || ! ( target[name].__isProxy ) ){
                                target[name]=new Proxy(value, SmartBind.getProxyHandler(bindContext, bindPath+'.'+name));
                            }
                            // run all tree to set proxies when necessary
                            var objKeys=Object.keys(value);
                            // console.debug("...objkeys",objKeys);
                            for ( var i=0; i<objKeys.length; i++ ) {
                                bindContext.mapDataTarget[bindPath+"."+name+"."+objKeys[i]]=target[name][objKeys[i]];
                                if ( typeof value[objKeys[i]] == 'undefined' || value[objKeys[i]] == null || ! ( value[objKeys[i]] instanceof Object ) || value[objKeys[i]].__isProxy )
                                    continue;
                                target[name][objKeys[i]]=new Proxy( value[objKeys[i]], SmartBind.getProxyHandler(bindContext, bindPath+'.'+name+"."+objKeys[i]));
                            }
                            // TODO it can be faster than run all items
                            var bindKeys=Object.keys(bindContext.mapDom);
                            for ( var i=0; i<bindKeys.length; i++ ) {
                                // console.log("test...", bindKeys[i], " for ", bindPath+"."+name);
                                if ( bindKeys[i].startsWith(bindPath+"."+name) ) {
                                    // console.log("its ok, lets update dom...", bindKeys[i]);
                                    SmartBind.processBindToDom( bindContext, bindKeys[i] );
                                }
                            }
                        }
                        return true;
                    }
                };
            }
            SmartBind.processBindToDom=function(bindContext, bindPath) {
                var domList=bindContext.mapDom[bindPath];
                if ( typeof domList != 'undefined' ) {
                    try {
                        for ( var i=0; i < domList.length ; i++){
                            var dataTarget=SmartBind.getDataTarget(bindContext, bindPath);
                            if ( 'target' in dataTarget )
                                domList[i].value=dataTarget.target[dataTarget.item];
                            else
                                console.warn("Could not get data target", bindContext, bindPath);
                        }
                    } catch (e){
                        console.warn("bind fail", bindPath, bindContext, e);
                    }
                }
            }
        }
    })();
    

    然后,设置,只需:

    var bindContext=SmartBind.BindContext();
    SmartBind.add(bindContext, document.getElementById('a'));
    SmartBind.add(bindContext, document.getElementById('b'));
    SmartBind.add(bindContext, document.getElementById('c'));
    
    var bindContext2=SmartBind.BindContext();
    SmartBind.add(bindContext2, document.getElementById('d'));
    SmartBind.add(bindContext2, document.getElementById('e'));
    SmartBind.add(bindContext2, document.getElementById('f'));
    SmartBind.add(bindContext2, document.getElementById('g'));
    
    setTimeout( function() {
        document.getElementById('b').value='Via Script works too!'
    }, 2000);
    
    document.getElementById('g').addEventListener('click',function(){
    bindContext2.data.test='Set by js value'
    })
    

    现在,我刚刚添加了HTMLInputElement值bind .

    如果您知道如何改进它,请告诉我 .

  • 1

    在这个链接中有一个非常简单的准确的双向数据绑定实现"Easy Two-Way Data Binding in JavaScript"

    以前的链接来自knockoutjs,backbone.js和agility.js的想法,导致了基于jQuery的this light-weight and fast MVVM framework, ModelView.js,它与jQuery很好地兼容,而且我是谦虚(或者可能不那么谦逊)的作者 .

    下面重现示例代码(来自blog post link):

    Sample code for DataBinder

    function DataBinder( object_id ) {
      // Use a jQuery object as simple PubSub
      var pubSub = jQuery({});
    
      // We expect a `data` element specifying the binding
      // in the form: data-bind-<object_id>="<property_name>"
      var data_attr = "bind-" + object_id,
          message = object_id + ":change";
    
      // Listen to change events on elements with the data-binding attribute and proxy
      // them to the PubSub, so that the change is "broadcasted" to all connected objects
      jQuery( document ).on( "change", "[data-" + data_attr + "]", function( evt ) {
        var $input = jQuery( this );
    
        pubSub.trigger( message, [ $input.data( data_attr ), $input.val() ] );
      });
    
      // PubSub propagates changes to all bound elements, setting value of
      // input tags or HTML content of other tags
      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, textarea, select") ) {
            $bound.val( new_val );
          } else {
            $bound.html( new_val );
          }
        });
      });
    
      return pubSub;
    }
    

    对于JavaScript对象的问题,为了这个实验,用户模型的最小实现可能如下:

    function User( uid ) {
      var binder = new DataBinder( uid ),
    
          user = {
            attributes: {},
    
            // The attribute setter publish changes using the DataBinder PubSub
            set: function( attr_name, val ) {
              this.attributes[ attr_name ] = val;
              binder.trigger( uid + ":change", [ attr_name, val, this ] );
            },
    
            get: function( attr_name ) {
              return this.attributes[ attr_name ];
            },
    
            _binder: binder
          };
    
      // Subscribe to the PubSub
      binder.on( uid + ":change", function( evt, attr_name, new_val, initiator ) {
        if ( initiator !== user ) {
          user.set( attr_name, new_val );
        }
      });
    
      return user;
    }
    

    现在,每当我们想要将模型的属性绑定到一个UI时,我们只需要在相应的HTML元素上设置适当的数据属性:

    // javascript
    var user = new User( 123 );
    user.set( "name", "Wolfgang" );
    
    <!-- html -->
    <input type="number" data-bind-123="name" />
    
  • 1

    绑定任何html输入

    <input id="element-to-bind" type="text">
    

    定义两个功能:

    function bindValue(objectToBind) {
    var elemToBind = document.getElementById(objectToBind.id)    
    elemToBind.addEventListener("change", function() {
        objectToBind.value = this.value;
    })
    }
    
    function proxify(id) { 
    var handler = {
        set: function(target, key, value, receiver) {
            target[key] = value;
            document.getElementById(target.id).value = value;
            return Reflect.set(target, key, value);
        },
    }
    return new Proxy({id: id}, handler);
    }
    

    使用功能:

    var myObject = proxify('element-to-bind')
    bindValue(myObject);
    
  • 1

    更改元素的值可以触发DOM event . 响应事件的侦听器可用于在JavaScript中实现数据绑定 .

    例如:

    function bindValues(id1, id2) {
      const e1 = document.getElementById(id1);
      const e2 = document.getElementById(id2);
      e1.addEventListener('input', function(event) {
        e2.value = event.target.value;
      });
      e2.addEventListener('input', function(event) {
        e1.value = event.target.value;
      });
    }
    

    Here是代码和演示,它显示了DOM元素如何相互绑定或与JavaScript对象绑定 .

  • 1

    我已经使用onkeypress和onchange事件处理程序进行了一些基本的javascript示例,以便为我们的js和js创建绑定视图

    这里的示例plunker http://plnkr.co/edit/7hSOIFRTvqLAvdZT4Bcc?p=preview

    <!DOCTYPE html>
    <html>
    <body>
    
        <p>Two way binding data.</p>
    
        <p>Binding data from  view to JS</p>
    
        <input type="text" onkeypress="myFunction()" id="myinput">
        <p id="myid"></p>
        <p>Binding data from  js to view</p>
        <input type="text" id="myid2" onkeypress="myFunction1()" oninput="myFunction1()">
        <p id="myid3" onkeypress="myFunction1()" id="myinput" oninput="myFunction1()"></p>
    
        <script>
    
            document.getElementById('myid2').value="myvalue from script";
            document.getElementById('myid3').innerHTML="myvalue from script";
            function myFunction() {
                document.getElementById('myid').innerHTML=document.getElementById('myinput').value;
            }
            document.getElementById("myinput").onchange=function(){
    
                myFunction();
    
            }
            document.getElementById("myinput").oninput=function(){
    
                myFunction();
    
            }
    
            function myFunction1() {
    
                document.getElementById('myid3').innerHTML=document.getElementById('myid2').value;
            }
        </script>
    
    </body>
    </html>
    
  • 2
    <!DOCTYPE html>
    <html>
    <head>
        <title>Test</title>
    </head>
    <body>
    
    <input type="text" id="demo" name="">
    <p id="view"></p>
    <script type="text/javascript">
        var id = document.getElementById('demo');
        var view = document.getElementById('view');
        id.addEventListener('input', function(evt){
            view.innerHTML = this.value;
        });
    
    </script>
    </body>
    </html>
    
  • 0

    将变量绑定到输入(双向绑定)的简单方法是直接访问getter和setter中的input元素:

    var variable = function(element){                    
                       return {
                           get : function () { return element.value;},
                           set : function (value) { element.value = value;} 
                       }
                   };
    

    在HTML中:

    <input id="an-input" />
    <input id="another-input" />
    

    并使用:

    var myVar = new variable(document.getElementById("an-input"));
    myVar.set(10);
    
    // and another example:
    var myVar2 = new variable(document.getElementById("another-input"));
    myVar.set(myVar2.get());
    

    没有getter / setter的更好的方式:

    var variable = function(element){
    
                    return function () {
                        if(arguments.length > 0)                        
                            element.value = arguments[0];                                           
    
                        else return element.value;                                                  
                    }
    
            }
    

    使用:

    var v1 = new variable(document.getElementById("an-input"));
    v1(10); // sets value to 20.
    console.log(v1()); // reads value.
    
  • 7

    在vanilla javascript中它是非常简单的双向数据绑定....

    <input type="text" id="inp" onkeyup="document.getElementById('name').innerHTML=document.getElementById('inp').value;">
    
    <div id="name">
    
    </div>
    

相关问题