Introduction
This article discusses how to integrate jQuery
library with Microsoft AJAX.NET, in particular with <asp:UpdatePanel/>
and to avoid Internet Explorer memory leak while doing so.
jQuery
becomes more and more popular. However Microsoft ASP.NET and AJAX.NET provide their own framework of communicating with the server. jQuery
AJAX mechanism seems clumsy and un-natural in ASP.NET environment. It's much easier to use ASP.NET UpdatePanels
for AJAX functionality and use jQuery
for their selectors
functionality - pretty much what I understood Microsoft is planning to do. However doing so is tricky and this article explains at least some of the shortfalls of doing so.
Iteration 1 - Sample Page
Let's imagine a sample calc - page that adds 2 numbers:
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ScriptMode=Debug>
<Scripts>
<asp:ScriptReference Path="~/jquery-1.2.6.debug.js" />
</Scripts>
</asp:ScriptManager>
<asp:UpdatePanel ID='up' runat="server"><ContentTemplate>
<asp:TextBox ID='i1' runat="server" CssClass='num' Width='50px'/>+
<asp:TextBox ID='i2' runat="server" CssClass='num' Width='50px'/>=
<asp:TextBox ID='res' runat="server" Width='50px'/>
<asp:Button ID='btn' runat="server" Text='...'/>
</ContentTemplate></asp:UpdatePanel>
</form>
</body>
<script>
function add() {
$get('res').value = parseInt($get('i1').value)
+ parseInt($get('i2').value);
}
$(document).ready(function() {
$('.num').change(add);
});
</script>
This page should not require much explanation if you have some familiarity with ASP.NET and jQuery
:
- We include
ScriptManager
and jquery
script. - Standard form
- We define 3 input controls, first two of which have
class='num'
- so we can easily find them with jQuery
- Contents are placed in
<asp:UpdatePanel>
and button is provided to refresh the panel - Function is defined to get 2 numbers, add them up and store the result in the third input box
- Standard
jQuery
mechanism is provided to attach change event so when one of the numbers gets changed, calculations are performed.
The resulting page looks like this:
The stuff initially seems to work. You can change the numbers and see the result on the screen. However try to refresh the UpdatePanel
- and suddenly calculations break.
Explanation
The reason for failure after the UpdatePanel
refresh is that the contents of the UpdatePanel
go away along with all the events attached to them.
To solve the problem, we need to use AJAX.NET provided way of attaching events to the page (here I'm attaching only script portion):
function add() {
$get('res').value =
parseInt($get('i1').value) + parseInt($get('i2').value);
}
Sys.Application.add_load(function() {
$('.num').change(add);
});
Iteration 2 - Memory Leak
The approach suggested in iteration 1 worked for me for a while. Except eventually, I started to notice that Internet Explorer slows down. A lot. And then looking at the process monitor, I noticed that Internet Explorer would leak memory on the moderate size form like a megabyte per UpdatePanel
refresh. Looking at the sIEve tool (God bless the heart of the creators), I saw that the input elements are not been destroyed:
You can see that every time I click refresh, it creates a new set of input elements without destroying the previous ones.
Explanation
Googling it for a while I noticed that Internet Explorer memory leak is a sore subject for a lot of developers. Most of the complaints were identified as either pseudo leak (memory is actually released but you need a complete page refresh to see it) or as closures.
The closure is an interesting topic. It refers to the JavaScript event attached to the DOM element. Apparently Internet Explorer uses 2 garbage collectors - one for DOM and another for JavaScript. There are some semantics involved but the bottom line is that if you attached an event to the DOM object, then neither garbage collector would be able to get rid of the involved objects/DOM elements. I strongly encourage you to read more about closures on the WEB.
Well the jQuery
is all about closures. They are nice enough to attach to window.unload
and cleanup when the page gets unloaded, but they have no idea regarding UpdatePanel
. Something needs to be done to cleanup jQuery
when UpdatePanel
is about to be refreshed.
Iteration 3 - jQuery Cleanup Plugin
UpdatePanel
does provide cleanup hooks. When it's about to throw an element away, it checks to see if there is a element.dispose
function. If such a function exists, then it will be executed.
So I ended up creating the following jQuery
plugin to take care of the situation:
(function($) {
$.fn.Disposable = function(cln) {
return this.each(function() {
var el = this;
if (!el.dispose) {
el.dispose = cleanup;
$(window).bind("unload", cleanup);
}
function cleanup() {
if (!el)
return;
$(el).unbind();
$(window).unbind("unload", cleanup);
el.dispose = null;
el = null;
};
});
};
})(jQuery);
function add() {
$get('res').value =
parseInt($get('i1').value) + parseInt($get('i2').value);
}
Sys.Application.add_load(function() {
$('.num').change(add).Disposable();
});
As you can see, this jQuery
plugin attaches a cleanup()
function to element.dispose
. Whenever element.dispose
is being called, all the jQuery
events will be unbound from the element and allow garbage collector to reclaim all the objects.
Summary
The code I'm discussing is actually part of another library I posted a while ago at jQuery Based Ajax.Net library. You are welcome to download that project and browse the code at your leisure.
History
- 20th March, 2009: Initial post