65.9K
CodeProject 正在变化。 阅读更多。
Home

JsonML 简化版 - Array.prototype.toDomNodes

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (3投票s)

2011 年 5 月 26 日

CPOL

3分钟阅读

viewsIcon

46937

downloadIcon

149

一种新的 JavaScript 方法,使 JsonML 成为您项目的可行选择

引言

本文介绍的代码允许通过一个函数调用,从格式化为标记的 JSON 对象(JsonML)创建 DOM 元素。这意味着您可以让您的 Ajax 调用返回一个 JSON 对象,并将该对象插入到 DOM 中,而无需手动解析或使用 .innerHTML

如果正确使用和复用,此代码可以显著减少您的 Web 应用程序的体积。

背景

多年来,我一直想将 JsonML 引入到我的一个项目中,不是模板或任何其他东西,只是从 Ajax 调用返回 JSON 对象,对其进行任何处理,然后在准备就绪时将其插入到文档中的概念。然而,我所见过的所有 JsonML 脚本都感觉有点臃肿,不是我真正想引入到我的应用程序中的东西。

最近我发现自己有一些空闲时间;所以我决定尝试编写一个更紧凑的、自包含的 JsonML 解析器。重要的是它要满足以下要求:

  • 尽可能少地使用代码
  • 完全自包含
  • 仅使用符合 W3C 标准的 DOM 方法
  • 不得使用任何浏览器嗅探
  • 必须按照 JsonML 对象中定义的完全一样插入节点
  • 必须允许我使用更少的代码在运行时创建对象
  • 必须允许我指定一个父元素,将新节点插入到该元素中
  • 必须允许我指定一个函数,以便在创建每个节点时调用

由于 JsonML 是使用数组定义的,因此我将我的方法构建为原生 Array 对象的原型,这意味着所有数组元素都继承了新的方法。

我还决定将我的方法命名为 toDomNodes,而不是使用任何 JsonML 引用。这是因为与传统的 JsonML 解析器不同,我的版本不会尝试通过插入缺失的元素(如 TbodyThead(Internet Explorer 需要))来克服浏览器的不一致性。这是一个重要的决定,因为我希望插入到 DOM 中的元素与我的 JSON 对象完全一致。

Using the Code

这是完成的、自包含的方法:

/* Convert JsonML Array form into nested dom nodes */
Array.prototype.toDomNodes = function(p /* parent */, 
f /* function to call on creation of each node */)
{
	if (typeof this[0] === 'string')
	{
		// create element
		var d = document, el = d.createElement(this[0]);

		// resolve parent
		p = (typeof p === 'string') ? d.getElementById(p) : p;

		// a list of attributes to map or 'set directly'
		var am = {id:"id","class":"className",rowspan:"rowSpan",
		colspan:"colSpan",cellpadding:"cellPadding",cellspacing:
		"cellSpacing",maxlength:"maxLength",readonly:"readOnly",
		contenteditable:"contentEditable"};

		// add attributes
		if (this[1] && this[1].constructor == Object) 
		{
			var n = this[1];
			
			for (var a in this[1])
			{
				if (typeof this[a] != 'function')
				{
					if (a === "style" && typeof el.style.cssText 
					!= "undefined") { el.style.cssText = n[a]; }
					else if (a === "style") { el.style = n[a]; }
					else if (am[a]) { el[am[a]] = n[a]; }
					else if (typeof this[a] != "string") { el[a] = 
					n[a]; } // not a string, set directly (expando)
					else { el.setAttribute(a, n[a]); alert(a); }
				}
			}
		}

		// insert element (must be inserted before function call, 
		// otherwise .parentNode does not exist)
		if (p) { p.appendChild(el); }
		
		// pass each node to a function (attributes will exist, 
		// but not innerText||children. can be used for adding events, etc.)
		if (typeof f === 'function') { f.apply(el); }
		
		for (var i=1,l=this.length;i<l;i++) 
		{
			var n = this[i], c = n.constructor;

			if (c === String) // add text node
			{
				el.appendChild(d.createTextNode(n));
			}
			else if (c === Array) // add children
			{
				n.toDomNodes(el, f);
			}
		}

		return el;
	}

	return null;
};

此版本包含一些注释,以帮助您了解其工作原理,并且需要 1.73k 的带宽才能使用。精简版(没有注释或格式)只有 936 字节。我不知道你怎么想,但我更乐意将 1k 的脚本引入到我的项目中,尤其是在它允许我减少应用程序其他区域的代码时,例如替换

var myDiv = document.createElement("div")
myDiv.id = "myDiv"
myDiv.style.border = "1px solid red";
myDiv.style.backgroudColor = "#cccccc";
myDiv.innerHTML = "Hello World!";
document.body.appendChild(myDiv);

与这样的代码:

["div",{"style":"background-color:#ccc; border: 1px solid red;"},
	"Hello World!"].toDomNodes(document.body);

这也意味着我现在可以从我的 Ajax 调用返回 JsonML。例如,假设 jsonMl 包含我的 Ajax 响应

var jsonMl =	["table",{"class":"MyTable","style":"background-color:yellow"},
		["tbody",
			["tr",
				["td",{"class":"MyTD","style":"border:1px solid black"},
				"#550758"],
				["td",{"class":"MyTD","style":"background-color:red"},
				"Example text here"]
			],
			["tr", 
				["td",{"class":"MyTD","style":"border:1px solid black"},
				"#993101"],
				["td",{"class":"MyTD","style":"background-color:green"},
				"127624015"]
			],
			["tr", 
				["td",{"class":"MyTD","style":"border:1px solid black"},
				"#E33D87"],
				["td",{"class":"MyTD","style":"background-color:blue"},
				"\u00A0",
					["span",{"id":"mySpan","style":
					"background-color:maroon;color:#fff 
					!important"},"\u00A9"],
					"\u00A0"
				]
			]
		]
	];

我可以通过调用将此对象直接插入到文档中

jsonMl.toDomNodes(document.body);

或者,我可以要求解析器每次创建新元素时都告诉我。这允许我应用一些客户端逻辑或基于属性分配事件等。

jsonMl.toDomNodes(document.body, function(){

	// this holds the new element
	alert(this.tagName);

});

函数中的 this 对象将包含新创建的对象。当调用您的函数时,每个新元素都将包含其所有属性和样式,但不包含其子元素。这是因为这是一个递归函数,并且在它调用您的函数时,尚未创建子节点。

如果将父元素作为第一个参数传递,则节点会自动插入到该元素中。如果您不想将元素插入到 DOM 中,只需传递 null 或不传递任何参数。在任何一种情况下,此方法都将返回对新创建的 DOM 节点的引用。

var myNewNode = jsonMl.toDomNodes();

当您准备好将新节点插入到 DOM 中时,您可以调用传统的 appendChild 方法。

document.body.appendChild(myNewNode);

关注点

这是第一个版本。我已经对其进行了测试,它可以在 PC、iPad 和 iPhone 上的 IE5.5/6/7/8/9、Chrome、Firefox、Opera 和 Safari 中运行。我包含了一个小型 zip 文件,其中包含其用法的示例以及精简版。如果出现任何问题,请发表评论,我会尽力帮助您。

历史

  • 2011 年 5 月 26 日 - 初始版本
© . All rights reserved.