热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

简易实现JavaScript中的双向数据绑定

双向数据绑定技术使得对象属性与用户界面之间可以相互影响,即对象属性的更改能即时反映到界面上,同时用户的界面操作也能同步更新对象状态。本文将介绍如何利用简单的JavaScript代码实现这一功能。

双向数据绑定是一种机制,它确保当对象属性发生变化时,这些变化会立即反映在用户界面上,反之亦然。例如,若存在一个包含name属性的user对象,当我们修改user.name的值时,界面上显示的名字也会相应更新。同样,如果界面上有一个用于输入用户名的文本框,用户在此输入的新值也会导致user对象的name属性更新。

虽然像Ember.js、Angular.js或KnockoutJS这样的现代Javascript框架提供了内置的双向数据绑定功能,但这并不意味着没有它们就无法实现这一功能。实际上,实现双向数据绑定的基本原理相当简单,可以通过三个步骤来概括:

  • 定义一种方式,使UI元素能够与对象属性相绑定;
  • 监控这些属性和UI元素的状态变化;
  • 确保所有相关联的对象和元素都能接收到并响应这些变化。

一个有效的实现方法是采用发布/订阅(PubSub)模式。在这种模式下,HTML元素通过特定的数据属性与Javascript对象关联,所有相关的对象和DOM元素都订阅同一个PubSub实例。当检测到数据变化时,会触发PubSub实例上的事件,通知所有订阅者进行相应的更新。

以下是使用jQuery简化DOM事件处理的一个示例:

function DataBinder(objectId) {
const pubSub = $( {} );
const dataAttr = `bind-${objectId}`;
const message = `${objectId}:change`;

$(document).on('change', `[data-${dataAttr}]`, function(event) {
const $input = $(this);
pubSub.trigger(message, [$input.data(dataAttr), $input.val()]);
});

pubSub.on(message, function(event, propName, newVal) {
$(`[data-${dataAttr}='${propName}']`).each(function() {
const $bound = $(this);
if ($bound.is('input, textarea, select')) {
$bound.val(newVal);
} else {
$bound.html(newVal);
}
});
});

return pubSub;
}

基于上述DataBinder,我们可以创建一个简单的User模型:

function User(uid) {
const binder = new DataBinder(uid);
const user = {
attributes: {},
set(attrName, val) {
this.attributes[attrName] = val;
binder.trigger(`${uid}:change`, [attrName, val, this]);
},
get(attrName) {
return this.attributes[attrName];
},
_binder: binder
};

binder.on(`${uid}:change`, function(event, attrName, newVal, initiator) {
if (initiator !== user) {
user.set(attrName, newVal);
}
});

return user;
}

为了将User模型的属性绑定到UI上,只需在HTML元素上添加适当的数据属性即可。例如,设置用户名称的输入框如下所示:

此外,也可以不依赖jQuery来实现相同的功能。通过使用原生Javascript编写自定义的PubSub实现,并处理DOM事件,可以达到同样的效果。这种方法虽然稍微复杂一些,但完全可行,尤其适用于那些不想引入额外库的项目。

function DataBinder(objectId) {
const pubSub = {
callbacks: {},
on(msg, callback) {
(this.callbacks[msg] = this.callbacks[msg] || []).push(callback);
},
publish(msg, ...args) {
(this.callbacks[msg] || []).forEach(cb => cb(...args));
}
};

const dataAttr = `data-bind-${objectId}`;
const message = `${objectId}:change`;
const changeHandler = event => {
const target = event.target || event.srcElement;
const propName = target.getAttribute(dataAttr);
if (propName) {
pubSub.publish(message, propName, target.value);
}
};

if (document.addEventListener) {
document.addEventListener('change', changeHandler, false);
} else {
document.attachEvent('onchange', changeHandler);
}

pubSub.on(message, (event, propName, newVal) => {
document.querySelectorAll(`[${dataAttr}='${propName}']`).forEach(element => {
if (['input', 'textarea', 'select'].includes(element.tagName.toLowerCase())) {
element.value = newVal;
} else {
element.innerHTML = newVal;
}
});
});

return pubSub;
}

通过这种方式,即使在没有jQuery的情况下,也能够以简洁且高效的方式实现双向数据绑定。


推荐阅读
  • Redux入门指南
    本文介绍Redux的基本概念和工作原理,帮助初学者理解如何使用Redux管理应用程序的状态。Redux是一个用于JavaScript应用的状态管理库,特别适用于React项目。 ... [详细]
  • 本文详细介绍了Java中org.w3c.dom.Text类的splitText()方法,通过多个代码示例展示了其实际应用。该方法用于将文本节点在指定位置拆分为两个节点,并保持在文档树中。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文介绍如何使用阿里云的fastjson库解析包含时间戳、IP地址和参数等信息的JSON格式文本,并进行数据处理和保存。 ... [详细]
  • 本文探讨了领域驱动设计(DDD)的核心概念、应用场景及其实现方式,详细介绍了其在企业级软件开发中的优势和挑战。通过对比事务脚本与领域模型,展示了DDD如何提升系统的可维护性和扩展性。 ... [详细]
  • 本文介绍了多个关于JavaScript的书籍资源、实用工具和编程实例,涵盖从入门到进阶的各个阶段,帮助读者全面提升JavaScript编程能力。 ... [详细]
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
  • 本文详细介绍了如何通过RPM包在Linux系统(如CentOS)上安装MySQL 5.6。涵盖了检查现有安装、下载和安装RPM包、配置MySQL以及设置远程访问和开机自启动等步骤。 ... [详细]
  • 反向投影技术主要用于在大型输入图像中定位特定的小型模板图像。通过直方图对比,它能够识别出最匹配的区域或点,从而确定模板图像在输入图像中的位置。 ... [详细]
  • 本文介绍如何使用 Android 的 Canvas 和 View 组件创建一个简单的绘图板应用程序,支持触摸绘画和保存图片功能。 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 深入理解Vue.js:从入门到精通
    本文详细介绍了Vue.js的基础知识、安装方法、核心概念及实战案例,帮助开发者全面掌握这一流行的前端框架。 ... [详细]
author-avatar
手机用户3312丿075454
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有