Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unbind inline javascript events from HTML elements in memory

How do I completely unbind inline javascript events from their HTML elements?

I've tried:

  • undelegating the event from the body element
  • unbinding the event from the element
  • and even removing the event attribute from the HTML element

To my surprise at least, only removing the onchange attribute (.removeAttr('onchange')) was able to prevent the event from firing again.

<input type="text" onchange="validateString(this)"></input>

I know this is possible with delegates and that's probably the best way to go, but just play along here. This example is purely hypothetical just for the sake of proposing the question.


So the hypothetical situation is this:

I'm writing a javascript validation library that has javascript events tied to input fields via inline HTML attributes like so:

<input type="text" onchange="validateString(this)"></input>

But, I'd like to make the library a little better by unbinding my events, so that people working with this library in a single-page application don't have to manage my event handlers and so that they don't have to clutter their code at all by wiring up input events to functions in my hypothetical validation library... whatever. None of that's true, but it seems like a decent usecase.

Here's the "sample" code of Hypothetical Validation Library.js:

http://jsfiddle.net/CoryDanielson/jwTTf/


To test, just type in the textbox and then click elsewhere to fire the change event. Do this with the web inspector open and recording on the Timeline tab. Highlight the region of the timeline that correlates to when you've fired the change event (fire the change event multiple times) and you'll see the event listeners (in the window below) increase by 100 on each change event. If managed & removed properly, each event listener would be properly removed before rendering a new input, but I have not found a way to properly do that with inline javascript events.

What that code does is this:

  1. onChange, the input element triggers a validation function
  2. That function validates the input and colors the border if successful
  3. Then after 1 second (to demonstrate the memory leak) the input element is replaced with identical HTML 100 times in a row without unbinding the change event (because I don't know how to do that.. that's the problem here). This simulates changing the view within a single-page app. This creates 100 new eventListeners in the DOM, which is visible through the web inspector.

    • Interesting Note. $('input').removeAttr('onchange'); will actually prevent the onchange event from being fired in the future, but does not garbage collect the eventListener/DOM stuff that is visible in the web inspector.

This screenshot is after change event fires 3 times. Each time, 100 new DOM nodes are rendered with identical HTML and I've attempted to unbind the onchange event from each node before replacing the HTML.

enter image description here


Update: I came back to this question and just did a quick little test using the JSFiddle to make sure that the answer was valid. I ran the 'test' dozens of times and then waited -- sure enough, the GC came through and took care of business.

enter image description here

like image 360
Cory Danielson Avatar asked Feb 09 '13 04:02

Cory Danielson


1 Answers

I don't think you have anything to worry about. Although the memory can no longer be referenced and will eventually be garbage collected, it still shows up in the Web Inspector memory window. The memory will be garbage collected when the GC decides to garbage collect it (e.g., when the browser is low on memory or after some fixed time). The details are up to the GC implementer. You can verify this by just clicking the "Collect Garbage" button at the bottom of the Web Insepctor window. I'm running Chrome 23 and after I enter text in your validation box about 5 or 6 times, the memory usage comes crashing down, apparently due to garbage collection.

This phenomenon is not specific to inline events. I saw a similar pattern just by repeatedly allocating a large array and then overwriting the reference to that large array, leaving lots of orphaned memory for GC. Memory ramps up for a while, then the GC kicks in and does its job.

like image 123
Will Nelson Avatar answered Nov 15 '22 09:11

Will Nelson