Introduction

I tend to write my own cross browser UI components and always strive to reduce the number of elements rendered in a page whilst also ensuring the mark-up validates. In situations where I need a key value pair but do not want to insert hidden fields, I use a custom CSS selector such as

<div id="mydiv" class="data-usedialogs-true">

prefix-key-value  

I then have a script that executes when the page loads and goes through all (or selected) elements extracting the key value pairs and binding them to the actual element within the DOM.

This allows me to read/test the value using JavaScript without any manual parsing:

if (document.getElementById("mydiv").prefix.property=="true") 
{
	// do something...
}

This method is my w3c compliant alternative to the new HTML5 data attribute, whereby you can add custom data attributes to elements e.g.

<div id="mydiv" data-usedialogs="true">.

The good thing about this method is you can change the attribute, so you could have

<div id="mydiv" class="settings-autohide-true"> 

resulting in:

if (document.getElementById("mydiv ").settings.autohide=="true")
{
	// do something...
}

How does the script work?

When the script executes it looks through the DOM for all elements with a class name matching the regular expression prefix-[A-Za-z0-9]{1,}- [A-Za-z0-9]{1,}. Once it finds an element, it then creates a custom attribute on that element and assigns an array containing the keys and values found within the matching selector(s).

This means you can have multiple key value pairs within the same element

<div id="mydiv" class="data-usedialogs-true data-autohide-true"> 

Allowing you to read the values back in JavaScript as:

if (element.data.usedialogs=="true")
{
	// do something...
}

and

if (element.data.autohide=="true")
{
	// do something...
}

The Script

//
// turns custom class names into key/value expando properties
//
// css class data-title-hello can be retrieved using element.data.title
//
function bindCssData(t /*tagName*/, p /*prefix*/)
{
    p = p || "data";
    t = t || "*";
    var els = document.getElementsByTagName(t);
    var reg = new RegExp("^(" + p + ")-([A-Z0-9]{1,})-([A-Z0-9]{1,})$", "i");
    for (var i=0, l=els.length; i<l; i++)
    {
        var cssNames = (els[i].className != "") ? new String(els[i].className).split(' ') : [];
        if (cssNames.length<=0) continue;
        for (var ii=0, ll=cssNames.length; ii<ll; ii++)
        {
            var m = reg.exec(cssNames[ii]);
            if (m && m.length >= 2) {
                els[i][p] = els[i][p] || new Array();
                els[i][p][m[2]] = m[3];
            }
        }
    }
}

To use the script call bindCssData() from within your window.onload function. If you do not pass any parameters to the function, it will parse all elements looking for a prefix of data. To make this faster, consider calling the function with only the element types (tagName) you want to affect: 

bindCssData("span");

or

bindCssData("span", "settings");

Anticipated Backlash -> Auto Response 

Of course, there will be many designers/developers out there that will disagree with the concept of using CSS selectors to carry data to client side scripts. However, in my defence and with progressive enhancement in mind, this is the cleanest solution I could come up with without inserting unnecessary bloat into the page. In addition, the ability to define your own prefix reduces the potential of a conflict with actual page styles.

For example, you could an unnatural CSS selector such as __cssdata-key-value.

Feedback

I am always stupidly busy and don't get much time to knock-up these articles so they might be areas lacking explanation. If you feel I need to expand/correct anything please leave a comment and I will update it as soon as I can.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"