How can I attach "meta data" to a DOM node?
Sometimes I want to attach some kind of meta data to a HTML node and I wondered, what's the best way to do this.
I can imagine the following:
- Non-standard attributes:
<div myattr1="myvalue1" myattr2="myvalue2" >
(Breaks validation) - Re-use an existing attribute
<div class="myattr1-myvalue2-mya开发者_如何学JAVAttr2-myvalue2">
(Requires parsing and some level of escaping)
Both solutions are really ugly!
Is there any way to do this more elegantly? I'm already using jQuery so any good Javascript solution, if any, is appreciated, too.
HTML5 introduces the notion of custom data attributes, that anyone can create in order to attach custom, hidden data to elements for scripting purposes. Just create an attribute using a data-
prefix, such as data-myattr1
or data-myattr2
, and fill it with your data.
<div data-myattr1="myvalue1" data-myattr2="myvalue2">Content</div>
The nice thing about this solution is that it already works in all major browsers; they will all parse unknown attributes, and expose them in the DOM for access by JavaScript. HTML5 adds a few convenience mechanisms for accessing them, which haven't been implemented yet, but you can just use the standard getAttribute
to access them for now. And the fact that they are allowed in HTML5 means that your code will validate, as long as you're willing to use a draft standard as opposed to an accepted one (I don't believe the data-
attributes are particularly controversial, however, so I'd be surprised if they were removed from the standard).
The advantage this has over namespaced attributes in XHTML is the IE doesn't support XHTML, so you would have to implement something that pretends to use namespace attributes but actually just uses invalid attributes with a :
in their name, which is how IE would parse them. It's nicer than using class
, because putting lots of data into a class
attribute overloads it quite a lot, and involves having to do extra parsing to pull out different pieces of data. And it's better than just making up your own (which will work in current browsers), because it's well defined that these attributes prefixed with data-
are private pieces of data for scripting, and so your code will validate in HTML5 and won't ever conflict with future standards.
Another little-known technique for adding custom data to HTML, which is valid even in HTML 4, is adding script
elements with type
attributes of something other than text/javascript
(or one of the couple of other types that can be used to specify JavaScript). These script blocks will be ignored by browsers that don't know what to with them, and you can access them via the DOM and do what you want with them. HTML5 explicitly discusses this usage, but there's nothing that makes it invalid in older versions, and it works in all modern browsers as far as I know. For example, if you would like to use CSV to define a table of data:
<div>
<script type="text/csv;header=present">
id,myattr1,myattr2
something,1,2
another,2,4
</script>
Content
</div>
This is the technique used by SVG Web to allow embedding of SVG in HTML, with emulation via Flash if the browser doesn't support native SVG. Currently, even browsers that do support SVG (Firefox, Safari, Chrome, Opera) don't support it directly inline in HTML, they only support it directly inline in XHTML (because the SVG elements are in a different namespace). SVG Web allows you to put SVG inline in HTML, using a script tag, and it then transforms those elements into the appropriate namespace and adds them to the DOM, so they can be rendered as XHTML. In browsers that don't support SVG, it then also emulates the function of the elements using Flash.
If it's not necessary to store the attribute in the markup, a great solution is jQuery's data
function: http://docs.jquery.com/Core/data.
One possible solution that maybe does not match exactly your requirements is to use a class as "metadata repository", with methods to get/set data based on the elements' id.
var metadataRepository = function(){
this.elements = [];
this.set = function(id,key,value){
this.elements[id][key] = value;
}
this.get = function(id,key){
if (typeof(this.elements[id]) == "undefined"){
return null;
}
if (key){
return (this.elements[id][key] != "undefined") ? this.elements[id][key] : null;
} else {
return this.elements[id];
}
}
}
var myMR = new metadataRepository();
myMR.set("myDiv1","attr1",232442);
myMR.set("myDiv2","attr1",{"id":23,"name":"testName"});
...
myMR.get("myDiv1","attr1"); //Returns only attr1
myMR.get("myDiv2"); //Returns all attributes
Assuming XHTML, just introduce a new namespace for your own metadata. That way you can have your attributes in their own namespace with arbitrary names and they won't conflict with any validation.
Basically all you need is to add
xmlns:myns="http://www.example.com/URI/to/myNamespaceDeclaration"
to the root node and then you can have nodes such as
<p myns:type="important">I'm a paragraph.</p>
anywhere under that root. That's essentially the whole purpose of namespaces, to allow the developer to add arbitrary data as layers to the whole model.
Edit:
Also a quick note, class
attribute is nowadays used a lot by microformats for metadata tagging purposes. You may want to take a look at the link above to see if someone has already invented the wheel you're trying to come up with :)
WeakMap might do the trick. It's a map-like data structure where keys are objects and values are arbitrary values.
const div = document.createElement("div")
const metadata = new WeakMap()
metadata.set(div, "div metadata")
metadata.get(div) // "div metadata"
If you're using xhtml, you can tweak your DTD so that your extra attributes validate.
If you're using html5, you can go with data-foobarbaz attributes.
精彩评论