If you are familiar with HTML, and the valid attributes of most of the elements, you probably also know that if you provide an attribute that is not one of the official attributes of that element the browsers will disregard it.

What you might not know is that those attributes are still parsed and are still part of the DOM, the Document Object Model.

That is, they are still accessible by JavaScript running in the browser.

Let's see an example.

In the following HTML file we have an h1 element and some text. If you click on the 'try' link you'll be able to see how that is displayed in your brower. It shows the text within the h1 element with bigger letters, because that's what we expect from a header element.

examples/html/html_attribute_h1.html

<h1>Hello World</h1>
<p>And include some text as well</p>
Try!

We can add a style attribute and within that we can add CSS instructions. For example we can tell the browser to use smaller fonts for the h1 element:

examples/html/html_attribute_style.html

<h1 style="font-size: 12px">Hello World</h1>
<p>And include some text as well</p>

Try!

This works because style is a known attribute of h1 and for that matter for every HTML element.

Custom attribute

In the next example removed the style attribute and added a different attribute, called secret-sauce. If you open the html in a browser you'll see that the content of the h1 element returned to it original size, and you won't see any impact of the new attribute.

examples/html/custom_html_attribute.html

<h1 secret-sauce="This is JavaScript">Hello World</h1>
<p>And include some text as well</p>

Try!

No warnings, no errors. That's because the browser simply disregards those attributes.

Attributes are in the DOM

While the browser seems to disregard the existance of this attribute, it actually parses it and puts it on the DOM. Meaning we can access this attribute using JavaScript.

examples/html/custom_html_attribute_show.html

<h1 secret-sauce="This is JavaScript">Hello World</h1>
<p>And include some text as well</p>

<script>
var h1 = document.getElementsByTagName('h1')[0];

console.log(h1.getAttribute('secret-sauce'));
</script>
Try!

Using document.getElementsByTagName we fetch the list of all the h1 elements in the document and the we use the index [0] to access the first such element. On that element we use the getAttribute method to access the value of the given attribute.

Then we use console.log to print it. click on the 'Try' link and in the new window open the JavaScript console to see the value.

data-* attributes

HTML5 defined a set of attributes using the prefix data- that get special treatment. They are not used by the browser, but they, (the broswers) provide access to them through special syntax. The objects representing the HTML tags that we retreive from the DOM of modern browsers have a standard attribute called dataset that provides access to all the data-* attributes as in this example:

examples/html/custom_html_attribute_show_data.html

<h1 data-secret-sauce="This is JavaScript">Hello World</h1>
<p>And include some text as well</p>

<script>
var h1 = document.getElementsByTagName('h1')[0];

console.log(h1.dataset.secretSauce);
console.log(h1.getAttribute('data-secret-sauce'));
</script>
Try!

As you might have noticed however, the names of the attributes in the HTML and the names we use in JavaScript to access them are not exactly the same. That's because JavaScript cannot have dash - in attribute names when using the . syntax.

So the attribute name called data-secret-sauce in the HTML is accessible as dataset.secretSauce in the JavaScript code.

Another issue with this special syntax is that Internet Explorer started to support this only in version 11. For older version you'll still have to use the regular getAttribute syntax.

For further examples check out the guide from Mozilla.

ng-* custom attributes used by AngularJS

AngularJS uses the ng- prefix for all the custom attributes. This makes it clear to the reader that something is related to AngularJS.

Actually Angular will also work with attributes using data-ng- prefix, in case you'd like to stick to the HTML specifications.

Caveat

The problem with the arbitrary attribute names is that if two JavaScript libraries happen to rely on the same attribute name and we try to use the two libraries in the same HTML file, then those two will collide. Using some kind of project-specific prefix, such as the ng- in case of AngularJS, can reduce the risk of this problem.