在设计一些设计页面应用时,特别是涉及到数据视图绑定或 Web Component 开发时,我们可能需要在 DOM 元素里设置一些,以存储一些信息。然而,这些存储在 DOM 属性里的值,可能形式较为复杂。一个常见的内置属性是 style,当然,浏览器会自动识别这个属性并作为样式的设定依据来进行处理。然而,如果我们也想自定义一些类似这样的 DOM 属性,我们通过常规的 DOM 访问所获取的值,却是一个字符串。但更多的情况下,我们更希望是一个 JSON 对象。
下面是一个可能的示例。
<sample-input data-config="type: 'select', label: 'Gender', require: true, dropdown: [ 'Male', 'Female' ]">
</sample-input>
我们想读取其中的 data-config 属性。假设我们给定一个传入的 DOM 和属性名,我们可以这么实现。
function attr(element, name) {
var ele = !!element && typeof element === "string" ? document.getElementById(element) : element;
if (!ele || !ele.tagName || !name) return undefined;
var attrStr = ele.getAttribute(name);
return eval("({" + attrStr + "})");
}
看似很轻松,如此就搞定了。最终函数返回的结果自然就如下所示了。
{
type: 'select',
label: 'Gender',
require: true,
dropdown: ['Male', 'Female']
}
可是,人们并非始终循规蹈矩。我们还希望能处理下面这种情况。
<sample-input data-config="function () { return null; }">
</sample-input>
我可能希望获得的是一个函数,显然上面的做法就挂了。因为外面多了一对大括号。同样,除了函数,里面直接是一个 JSON 对象也是不行的。
<sample-input data-config="{ type: 'select', label: 'Gender', require: true, dropdown: [ 'Male', 'Female' ] }">
</sample-input>
或者就直接是一个字符串、数字、布尔值、数组等等也都不行。那怎么办呢?把那个大括号去掉?不要这么简单粗暴,其实我们可以这么改。
- 先进行为空判断。
- 去掉左右的空格。
- 检查是否存在大括号和冒号是否出现在某些位置上,以确定是否要保留那对最外面的大括号。
于是我们得到以下代码。
if (!attrStr) return null;
attrStr = attrStr.replace(/(^\s*)|(\s*$)/g, "");
var indexA = attrStr.indexOf(":");
var indexB = attrStr.indexOf("{");
attrStr = indexA > 0 && (indexB < 0 || indexA < indexB) ? eval("({" + attrStr + "})") : eval("(" + attrStr + ")");
那简单的一组数据呢?
<sample-input data-config="a;b;c">
</sample-input>
好吧,我们还要做更多的判断和处理。
if (indexA < 0 && indexB < 0 && attrStr.indexOf("=") < 0 && attrStr.indexOf("'") < 0 &&
attrStr.indexOf("\"") < 0 && attrStr.indexOf("(") < 0 && attrStr.indexOf(")") < 0 && attrStr.indexOf(" ") > 0) {
attrStr = attrStr.indexOf(";") >= 0 ? attrStr.split(";") : attrStr;
}
嗯,貌似大功告成。等等,说好的绑定类型的呢?
<sample-input data-config="{{dataConfig}}">
</sample-input>
啊哈,你的要求还真多!一并满足你。
if (attrStr.indexOf("{{") === 0 && attrStr.lastIndexOf("}}") === attrStr.length - 2 && attrStr.length > 4) {
attrStr = eval("(" + attrStr.substring(2, attrStr.length - 2) + ")");
}
这下总该搞定了吧?嗯,是的,解决了各种情况。好了,我们回顾一下刚才写的,总结在一起,于是就变成了下面这样的一个函数。传入 DOM 元素和需要绑定的属性,我们就能解析其中的内容。
/**
* Parses the specific attribute object of an element.
* @param element the element to get attribute.
* @param name the attribute name.
*/
function attr(element, name) {
var ele = !!element && typeof element === "string" ? document.getElementById(element) : element;
if (!ele || !ele.tagName || !name)
return undefined;
var attrStr = ele.getAttribute(name);
if (!attrStr)
return null;
attrStr = attrStr.replace(/(^\s*)|(\s*$)/g, "");
if (attrStr === "")
return null;
if (attrStr.indexOf("{{") === 0 && attrStr.lastIndexOf("}}") === attrStr.length - 2 && attrStr.length > 4) {
return eval("(" + attrStr.substring(2, attrStr.length - 2) + ")");
}
var indexA = attrStr.indexOf(":");
var indexB = attrStr.indexOf("{");
if (indexA > 0 && (indexB < 0 || indexA < indexB))
return eval("({" + attrStr + "})");
if (indexA < 0 && indexB < 0 && attrStr.indexOf("=") < 0 && attrStr.indexOf("'") < 0 &&
attrStr.indexOf("\"") < 0 && attrStr.indexOf("(") < 0 && attrStr.indexOf(")") < 0 && attrStr.indexOf(" ") > 0) {
return attrStr.indexOf(";") >= 0 ? attrStr.split(";") : attrStr;
}
return eval("(" + attrStr + ")");
}
不过话说,人类难以获得100%的满足。虽然我们拥有一个这么强大的 DOM 属性解析器,但并不意味着我就停止于此。我还想有个更强大的东西,那就是能快速获取一些指定 DOM 属性值,甚至能在这些 DOM 属性值变更后,我的相关变量也被自动更新。这可怎么办呢?套用一些经典名著里的话:欲知后事如何,请听下回分解。
文章类型及复杂度:Web 进阶。
节选翻译自 MSDN 博文 Parse DOM attribute object,内容有所调整。
http://blogs.msdn.com/b/kingcean/archive/2016/03/18/parse-attribute-object-in-dom.aspx