Как реализовать привязку данных DOM в JavaScript
Пожалуйста, рассматривайте этот вопрос как строго воспитательный. Мне все еще интересно услышать новые ответы и идеи для реализации этого
TL;DR
Как мне реализовать двунаправленную привязку данных с JavaScript?
Связывание данных с DOM
Посредством привязки данных к DOM я имею в виду, например, наличие объекта JavaScript a
со свойством b
. Затем, имея элемент <input>
DOM (например), когда элемент DOM изменяется, a
изменяется и наоборот (то есть я имею в виду двунаправленную привязку данных).
Вот диаграмма AngularJS о том, как это выглядит:
![two way data binding]()
Итак, у меня есть JavaScript, похожий на:
var a = {b:3};
Затем элемент ввода (или другой формы), например:
<input type='text' value=''>
Я хотел бы, чтобы значение ввода было a.b
(например), и когда текст ввода изменяется, я бы хотел, чтобы a.b
тоже изменился. Когда a.b
изменяется в JavaScript, вход изменяется.
Вопрос
Каковы основные методы для этого в простом JavaScript?
В частности, я хотел бы получить хороший ответ:
- Как связанная работа для объектов?
- Как может случиться изменение формы?
- Возможно ли простым образом изменить HTML на уровне шаблона? Я бы не стал отслеживать привязку в самом документе HTML, но только в JavaScript (с событиями DOM и JavaScript, содержащими ссылки на используемые элементы DOM).
Что я пробовал?
Я большой поклонник Усы, поэтому я попытался использовать его для шаблонов. Тем не менее, я столкнулся с проблемами при попытке выполнить привязку данных, поскольку Усест обрабатывает HTML как строку, поэтому после получения моего результата я не имею ссылки на то, где объекты в моей модели viewmodel. Единственным обходным решением, которое я мог бы подумать, было изменение строки HTML (или созданного дерева DOM) с помощью атрибутов. Я не против использовать другой шаблонный движок.
В принципе, у меня появилось сильное чувство, что я усложняю проблему, и есть простое решение.
Примечание.. Пожалуйста, не предоставляйте ответы, в которых используются внешние библиотеки, особенно те, которые являются тысячами строк кода. Я использовал (и вроде!) AngularJS и KnockoutJS. Мне действительно не нужны ответы в форме "use framework x". Оптимально, я хотел бы, чтобы будущий читатель, который не знает, как использовать множество фреймворков, чтобы понять, как реализовать двунаправленную привязку данных. Я не ожидаю полного ответа, но тот, который получает эту идею.
Ответы
Ответ 1
- Как связанная работа для объектов?
- Как может случиться изменение формы?
Абстракция, которая обновляет оба объекта
Я предполагаю, что есть другие методы, но в конечном итоге у меня будет объект, который содержит ссылку на связанный элемент 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
. И то же самое произойдет, когда вы вызываете .change()
в вашу программу JavaScript.
Используя код
Теперь вы просто создаете новый объект и позволяете ему выполнять обновления. На входе появятся обновления в 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/
Ответ 2
Итак, я решил бросить свое решение в банк. Вот рабочий скрипт. Обратите внимание, что это работает только в очень современных браузерах.
Что он использует
Эта реализация очень современна - для нее требуется (очень) современный браузер и пользователи двух новых технологий:
-
MutationObserver
s для обнаружения изменений в dom (также используются прослушиватели событий)
-
Object.observe
, чтобы обнаружить изменения в объекте и уведомить dom. Опасность, так как этот ответ был написан O.o был обсужден и решил против TCM ECCC, рассмотрите 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);
Вот рабочий скрипт. Обратите внимание, что это решение довольно общее. Доступна функция Object.observe и mutation observer shimming.
Ответ 3
Я хочу добавить к моему препоре. Я предлагаю несколько иной подход, который позволит вам просто назначить новое значение для вашего объекта без использования метода. Следует отметить, что это не поддерживается особенно старыми браузерами, и IE9 по-прежнему требует использования другого интерфейса.
Самое главное, что мой подход не использует события.
Getters and Setters
В моем предложении используется относительно молодая особенность getters and setters, в особенности сеттеры. Вообще говоря, мутаторы позволяют нам "настраивать" поведение того, как определенным свойствам присваивается значение и извлекается.
Одна реализация, которую я буду использовать здесь, это метод Object.defineProperty. Он работает в FireFox, GoogleChrome и - я думаю - IE9. Не тестировали другие браузеры, но поскольку это теория только...
В любом случае, он принимает три параметра. Первый параметр - это объект, для которого вы хотите определить новое свойство, второй - строку, напоминающую имя нового свойства, и последний "объект дескриптора", предоставляющий информацию о поведении нового свойства.
Два особенно интересных дескриптора: get
и set
. Пример будет выглядеть примерно так: Обратите внимание, что использование этих двух запрещает использование других 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);
Я хочу подчеркнуть, что это работает только для современных браузеров.
Рабочая скрипка: http://jsfiddle.net/Derija93/RkTMD/1/
Ответ 4
Я думаю, что мой ответ будет более техническим, но не другим, поскольку другие представляют одно и то же, используя разные методы.
Итак, во-первых, решение этой проблемы заключается в использовании шаблона проектирования, известного как "наблюдатель", который позволяет отделить ваши данные от вашей презентации, в результате чего изменение одной вещи будет передано их слушателям, но в этом случае он сделал двусторонний.
Для пути 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 scope
var obj = document[this.data.object];
obj[this.data.property] = this.value;
}
}
document.addEventListener('change', toJS2, false);
Для JS do DOM way
Вам понадобятся две вещи: один метаобъект, который будет содержать ссылки на элемент DOM DOM, привязан к каждому js-объекту/атрибуту и способу прослушивания изменений в объектах. Это в основном то же самое: у вас должен быть способ прослушать изменения в объекте, а затем привязать его к DOM node, так как ваш объект "не может иметь" метаданных вам понадобится другой объект, который содержит метаданные в способ, которым имя свойства сопоставляется с свойствами объекта метаданных.
Код будет примерно таким:
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);
Надеюсь, что я помог.
Ответ 5
Вчера я начал писать свой собственный способ привязки данных.
С ним очень весело играть.
Я думаю, что это красиво и очень полезно. По крайней мере, в моих тестах, использующих 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.
Дайте мне знать, если вы знаете, как его улучшить.
Ответ 6
В этой ссылке реализована очень простая реализация двухсторонней привязки данных на основе двух сторон: "Простое двустороннее связывание данных в JavaScript"
Предыдущая ссылка, а также идеи от knockoutjs, backbone.js и agility.js привели к этой легкой и быстрой структуре MVVM ModelView.js на основе jQuery, который отлично играет с jQuery, и я являюсь скромным (или, может быть, не очень скромным) автором.
Воспроизведение примера кода ниже (из ссылка для блога):
Пример кода для 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;
}
Теперь, когда мы хотим привязать свойство моделей к части пользовательского интерфейса, мы просто нужно установить соответствующий атрибут данных на соответствующий Элемент HTML:
// javascript
var user = new User( 123 );
user.set( "name", "Wolfgang" );
<!-- html -->
<input type="number" data-bind-123="name" />
Ответ 7
Привязать любой 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);
Ответ 8
Изменение значения элемента может вызвать событие DOM. Слушатели, отвечающие на события, могут использоваться для реализации привязки данных в 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;
});
}
Здесь - это код и демонстрация, показывающая, как элементы DOM могут быть связаны друг с другом или с объектом JavaScript.
Ответ 9
Я рассмотрел некоторые основные примеры javascript, используя обработчики событий onkeypress и onchange для создания привязки к нашим 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>
Ответ 10
<!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>
Ответ 11
Простой способ привязки переменной к входу (двусторонняя привязка) - просто получить прямой доступ к элементу ввода в методах получения и установки:
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.
Ответ 12
Это очень простая двусторонняя привязка данных в vanilla javascript....
<input type="text" id="inp" onkeyup="document.getElementById('name').innerHTML=document.getElementById('inp').value;">
<div id="name">
</div>
Ответ 13
Здесь идея использования Object.defineProperty
который напрямую изменяет способ доступа к свойству.
Код:
function bind(base, el, varname) {
Object.defineProperty(base, varname, {
get: () => {
return el.value;
},
set: (value) => {
el.value = value;
}
})
}
Использование:
var p = new some_class();
bind(p,document.getElementById("someID"),'variable');
p.variable="yes"
скрипка: здесь