attr(node, key, val) {
			if (!this.elem(node))
				return null;

			let attr = null;

			if (typeof(key) === 'object' && key !== null)
				attr = key;
			else if (typeof(key) === 'string')
				attr = {}, attr[key] = val;

			for (key in attr) {
				if (!attr.hasOwnProperty(key) || attr[key] == null)
					continue;

				switch (typeof(attr[key])) {
				case 'function':
					node.addEventListener(key, attr[key]);
					break;

				case 'object':
					node.setAttribute(key, JSON.stringify(attr[key]));
					break;

				default:
					node.setAttribute(key, attr[key]);
				}
			}
		}
Sets attributes or registers event listeners on element nodes.

@instance
@memberof LuCI.dom
@param {*} node
The `Node` argument to set the attributes or add the event
listeners for. When the given `node` value is not a valid
DOM `Node`, the function returns and does nothing.

@param {string|Object<string, *>} key
Specifies either the attribute or event handler name to use,
or an object containing multiple key, value pairs which are
each added to the node as either attribute or event handler,
depending on the respective value.

@param {*} [val]
Specifies the attribute value or event handler function to add.
If the `key` parameter is an `Object`, this parameter will be
ignored.

When `val` is of type function, it will be registered as event
handler on the given `node` with the `key` parameter being the
event name.

When `val` is of type object, it will be serialized as JSON and
added as attribute to the given `node`, using the given `key`
as attribute name.

When `val` is of any other type, it will be added as attribute
to the given `node` as-is, with the underlying `setAttribute()`
call implicitly turning it into a string.