104

I want to associate a JavaScript object with an HTML element. Is there a simple way to do this?

I noticed HTML DOM defines a setAttribute method and it looks like this is defined for arbitrary attribute name. However this can only set string values. (You can of course use this to store keys into a dictionary.)

Specifics (though I'm mostly interested in the general question):

Specifically, I have HTML elements representing nodes in a tree and I'm trying to enable drag-and-drop, but the jQuery drop event will only give me the elements being dragged and dropped.

The normal pattern for getting information to event handlers seems to be to create the HTML elements at the same time as you are creating JavaScript objects and then to define event handlers by closing over these JavaScript objects - however this doesn't work too well in this case (I could have a global object that gets populated when a drag begins... but this feels slightly nasty).

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
user47741
  • 2,905
  • 4
  • 21
  • 14

2 Answers2

125

JavaScript objects can have arbitrary properties assigned to them, there's nothing special you have to do to allow it. This includes DOM elements; although this behaviour is not part of the DOM standard, it has been the case going back to the very first versions of JavaScript and is totally reliable.

var div= document.getElementById('nav');
div.potato= ['lemons', 3];
bobince
  • 528,062
  • 107
  • 651
  • 834
  • 6
    @tat.wright - The arbitrary properties on HTML elements he's speaking of are called Expando properties. Also note that div.potato does not mean div.getAttribute("potato") – nickytonline Sep 10 '09 at 03:24
  • 2
    It's not totally reliable at all. For example, one call to `document.expando = false` in IE prevents all expando properties on HTML elements within the document. – Tim Down Sep 10 '09 at 08:35
  • 39
    @Tim Down: then just don't do it. I fail to see why it isn't reliable - it's like saying that the String object is unreliable, because you can set it to null. – simon Sep 10 '09 at 10:52
  • @simon - not really. There are good reasons for setting `document.expando = false` (e.g. preventing accidental misspellings of DOM properties being silently accepted). Also, IE doesn't allow expando properties on all objects (for example, ActiveX objects such as XMLHttpRequest in IE6, at least). – Tim Down Sep 11 '09 at 08:48
  • ahem - this answer talks about expando being bad as it creates leaks: http://stackoverflow.com/q/4798255/849004 . So - who is right? – Stann Nov 24 '11 at 04:50
  • 5
    @TimDown: But won't setting `document.expando = false` break [jQuery.data](http://api.jquery.com/jQuery.data/), too? – Joey Adams Apr 04 '12 at 16:44
  • @JoeyAdams: I believe so. I'm not recommending that either :) – Tim Down Apr 04 '12 at 22:28
  • 11
    From my experience the reason this can be bad is possible memory leaks. There might be an easy way to avoid it, but when I made a design that used this heavily it had many memory leaks. I think you have to be extra careful because reference counting isn't handled for you (not cleaned up) if the element is removed from the page, and probably visa-versa. – eselk Jan 29 '13 at 16:32
  • 1
    @TimDown: `$.data()` uses this technique, so if this doesn't work, neither will jquery - and jQuery is used rather widely :-). – Eamon Nerbonne Jan 30 '14 at 13:35
  • 3
    @eselk: this was an IE issue that was [fixed in IE8 (!)](http://msdn.microsoft.com/en-us/library/dd361842(v=vs.85).aspx) and is therefore unlikely to affect you anymore; unless, of course, you still support IE7. Also note that even in IE7 you're fairly safe since leaks are per-page, so unless you constantly make new objects and the page is open a long time, this won't get out of hand. – Eamon Nerbonne Jan 30 '14 at 13:40
  • 2
    `var cloned = div.cloneNode()` makes `potato` go away on `cloned`, while other native attributes does get cloned. – Yuval A. Dec 05 '14 at 21:49
  • 9
    Fair warning: I was happily doing what the answer describes, but instead of the field name "potato" I was using "scope". Turns out "scope" is an attribute of table cell elements (``) and the object I assigned to this field was #toString'ed. I was very confused seeing td.scope === '[object Object]'. – David Groomes Nov 05 '15 at 21:53
43

If you're already using jQuery, you can use its data() method for this. This allows you to assign complex objects to the element if you want or you can leverage that method to hold a reference to an object (or some other data) at the very least.

It's worth noting that, under the hood, jQuery implements data() in precisely the way that bobince described in his answer, so you always use that directly, whether or not you're using jQuery.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Phil.Wheeler
  • 16,748
  • 10
  • 99
  • 155
  • 8
    Thank you: This is pretty much what I was looking for. Interestingly to define this method jquery stores keys into a global dictionary in a string attribute on each element. The name of this string attribute is randomly generated when jquery is first loaded. (Suggesting that there isn't a good way to do there isn't a nicer way to do this only using the DOM) – user47741 Sep 10 '09 at 00:14