65.9K
CodeProject is changing. Read more.
Home

XHTML Alternative to HTML5 Data Attributes

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (4 votes)

Jan 25, 2011

CPOL

2 min read

viewsIcon

31064

downloadIcon

102

Use CSS Selectors as lightweight key value pairs; automatically bound to elements at runtime using the bindCssData method.

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:

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

This method is my W3C compliant (pre HTML5) 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:

var mydiv = document.getElementById("mydiv");
if (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:

var mydiv = document.getElementById("mydiv");
if (mydiv.data.usedialogs=="true")
{
    // do something...
}

and:

var mydiv = document.getElementById("mydiv");
if (mydiv.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 defense 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 use 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 there 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.