JsonML 简化版 - Array.prototype.toDomNodes






4.50/5 (3投票s)
一种新的 JavaScript 方法,使 JsonML 成为您项目的可行选择
引言
本文介绍的代码允许通过一个函数调用,从格式化为标记的 JSON 对象(JsonML)创建 DOM 元素。这意味着您可以让您的 Ajax 调用返回一个 JSON 对象,并将该对象插入到 DOM 中,而无需手动解析或使用 .innerHTML
。
如果正确使用和复用,此代码可以显著减少您的 Web 应用程序的体积。
背景
多年来,我一直想将 JsonML 引入到我的一个项目中,不是模板或任何其他东西,只是从 Ajax 调用返回 JSON 对象,对其进行任何处理,然后在准备就绪时将其插入到文档中的概念。然而,我所见过的所有 JsonML 脚本都感觉有点臃肿,不是我真正想引入到我的应用程序中的东西。
最近我发现自己有一些空闲时间;所以我决定尝试编写一个更紧凑的、自包含的 JsonML 解析器。重要的是它要满足以下要求:
- 尽可能少地使用代码
- 完全自包含
- 仅使用符合 W3C 标准的 DOM 方法
- 不得使用任何浏览器嗅探
- 必须按照 JsonML 对象中定义的完全一样插入节点
- 必须允许我使用更少的代码在运行时创建对象
- 必须允许我指定一个父元素,将新节点插入到该元素中
- 必须允许我指定一个函数,以便在创建每个节点时调用
由于 JsonML 是使用数组定义的,因此我将我的方法构建为原生 Array
对象的原型,这意味着所有数组元素都继承了新的方法。
我还决定将我的方法命名为 toDomNodes
,而不是使用任何 JsonML 引用。这是因为与传统的 JsonML 解析器不同,我的版本不会尝试通过插入缺失的元素(如 Tbody 和 Thead(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 日 - 初始版本