Web Platform

DOM Attribute Nodes

4 min read

We are used to mutating element attributes as strings using the getAttribute and setAttribute methods.

tabs.setAttribute("role", "tablist");
tabs.getAttribute("role"); // tablist

The DOM Standard also specifies so-called Attr nodes. The standard is not actually strictly tied to JavaScript. PHP, for example, also defines getAttributeNode in its DOM extension.

These nodes can be created through the document.createAttribute method, and are also implicitly created whenever setAttribute is used. setAttributeNode, getAttributeNode, and removeAttributeNode are methods available on all elements which may be used to interact with attribute nodes.

<button id="coloredBtn" class="text-red-500">Background color added by JS</button>
<script>
  const coloredClass = coloredBtn.getAttributeNode("class");
  coloredClass.value = "bg-black text-white border-white";
</script>

Newly set attributes can also be mutated via their implicitly-created node:

<button id="boldBtn">Bolded by JS</button>
<script>
  boldBtn.setAttribute("class", "text-red-600");
  const boldClass = boldBtn.getAttributeNode("class");
  boldClass.value += " font-bold";
</script>

Even though Attr nodes inherit from the Node interface, they don’t have any interesting fields. Its ownerElement field points to the DOM element it is attached to. name, localName, and nodeName all refer to the attribute’s name. value is used for reading and writing its value.

The spec describes the situation as such:

If designed today they would just have a name and value. ☹

Attribute nodes can also be referenced and assigned using the attributes NamedNodeMap, with the getNamedItem, setNamedItem, and removeNamedItem methods. These methods are equivalent to xAttributeNode.

const disabled = document.createAttribute("disabled");
btn.attributes.setNamedItem(disabled);
btn.attributes.removeNamedItem(disabled);

Named attributes can also be retrieved as properties of attributes.

const disabled = document.createAttribute("disabled");
btn.attributes.setNamedItem(disabled);
btn.attributes.disabled.value = false;

Setting new properties on attributes does result in it being set on the object, however, the attribute is not reflected in the DOM.

btn.attributes.disabled = true;
btn.getAttribute("disabled"); // null

The most obvious use case would be to keep the same attribute on multiple nodes in sync.

const role = document.createAttribute("role");
role.value = "tab";
div1.setAttributeNode(role);
div2.setAttributeNode(role);
// This however fails, with the following error
// Uncaught DOMException: Attribute already in use

An error is thrown because a node can only have a single ownerElement. It’s a shame, as keeping attribute values in sync is especially useful when building accessible applications. Tabs, for example, should update tabindex and aria-selected attributes in accordance with what tab is currently selected.

attr.cloneNode() can be used to clone an attribute node, but these would obviously not be in sync.

Attribute nodes are not exactly a useful feature. It should be no surprise that this feature is on the long tail of little-used browser features, with almost no user ever hitting a page making use of it. The Chrome Platform Status feature rankings page, powered by anonymous usage statistics, claims this function is used on fewer than 0.000001% of page views.

Older versions of jQuery (before v1.10) actually made use of the getAttributeNode function in one place, but it was removed due to Firefox firing warnings. The warnings have been retired since. Presumably, the only usage of this method occurs in pages using old versions of jQuery.