3 ms·
The solution in the post attaches a setInterval reference to the "this" object, a reference to the input box. This is neater than using a variable defined outs
by drewblaisdell 13y ago
The solution in the post attaches a setInterval reference to the "this" object, a reference to the input box.
This is neater than using a variable defined outside of the closure, but I was under the impression that attaching arbitrary data to DOM nodes was bad form.
Thoughts?
- TheZenPsycho 13y agoattaching arbitrary data to DOM nodes is fine in theory, except that older versions of IE have a bug in their garbage collector that doesn't cope with cyclical references. Cyclical references are very easy to unintentionally create in javascript because of closures. The typical situation is creating a variable to a dom node, and then attaching an event handler to that dom node. The event handler has a reference to the dom node through its closure object, and the dom node has the reference to the event handler creating the circular reference. You must be careful to null out the event handler when you are done with it. For arbitrary data, jquery provides a data() function that does the cleanup stuff for you. for event handlers, well, jquery's bind() (now renamed to on() ) is one of the first things you learn in jquery, and it (i assume) also deals with the IE bug.
- drewblaisdell 13y agoThanks for the reply. I'm mainly curious about this in terms of programming style/best practices. Beside implications to the namespace of that node (which could be avoided with data()), it seems to me that it would be best to separate the setTimeout ID to a variable defined outside of that closure simply to make the code maintainable, for if there ever was another reason to interrupt the setTimeout. However, due to the mention of closure at all by the interviewer, it seems that he felt that attaching it to the DOM node was the "best solution". Would you agree?
- dmbass 13y agoCapture the timeoutId in a closure. $(document).ready((function() { $('input').keypress((function() { var timeoutId = null; return function() { window.clearTimeout(timeoutId); timeoutId = window.setTimeout(function() { $.ajax({ /* ... * /}); }, 200); }; })()); }); // NB: this solution only works if there is one input. // If there are more than one, you need to create a new closure for each one.
- TheZenPsycho 13y agoAttaching an event handler to a dom node is the /only/ way to handle events. As for where to put the setTimeout variable, I would say best practice is just use underscore's debounce function, and you avoid the question of where to store that variable altogether. (it's kept inside the closure of a wrapped function. secretly. But you don't really care where it is since all we are interested in is the external observable behaviour of the debounced function) On a broader level, an interesting question is: when is it good practice to attach a variable to a DOM node? I would say very rarely except perhaps in the case where what you are doing is assembling some kind of interface widget out of HTML elements. Then storing information about the widget's state with the dom node is what makes the most sense. Since in the question, the task at hand is constructing an autocomplete widget, apparently from scratch without recourse to the many easily available robust and tested existing libraries, it does indeed make some sense to store the timeout state with the DOM node. If your goal is a robust reusable component, you want something that can cope with having many instances on a page. to do that and maintain the behavior of one on its own, you can either attach the state with the dom node, or create a framework that simulates doing that, but implements it in some other overly clever way.
- drewblaisdell 13y agoApologies for the ambiguous wording: I meant attaching the timeout ID specifically to the DOM node. dmbass posted a solution above that handles all style issues I had with the answer in the post.
- TheZenPsycho 13y agoyes I see. I suppose it's javascript's version of private instance properties, versus public properties. The timeout ID doesn't need to be public, and probably shouldn't be since what you actually want is an encapsulated self contained widget. You don't really want to have to reach inside of it to touch some implementation detail. So hiding it in a closure takes care of that. If you want the widget to have the capability of cancelling, you should add a cancel method to it.
- jdmichal 13y agoJust for clarification: Older versions of IE have two garbage collectors, one for DOM elements and one for JavaScript. The issues arise when the circular dependency loops across these two collectors, so that the DOM collector is waiting for the JavaScript object to free and vice versa. Unfortunately, this is really easy to do with closures. For instance, patterns like this are pretty common: var domElement = $("#someId"); domElement.click(function () { domElement.style.color = "red"; });