I have jQuery on my web page, and I want to remove it programmatically.
Meaning completely unload it, destroy the object and related functions, destroy its internal memory allocations, and event bindings.
How can this be done?
Related
The official UI5 documentation claims:
[...] We want to make sure that the memory allocated for this helper object is freed up when the component is destroyed. Otherwise our application may cause memory leaks.
To do so, we use the exit hook. The OpenUI5 framework calls the function assigned to exit when destroying the component. We call the destroy function of HelloDialog to clean up the helper class and end its lifecycle. Nevertheless, the instance itself would still exist in the browser memory. Therefore we delete our reference to the HelloDialog instance by calling delete this._helloDialog and the garbage collection of the browser can clean up its memory.
I've set a break point on this._helloDialog.destroy(); inside of
exit() {
this._helloDialog.destroy();
delete this._helloDialog;
}
And I expect that the break point will be triggered on leaving the view, which hosts this dialog. In fact, when I switched the view nothing happened, it looks like exit() wasn't executed at all.
Besides that, I've paid attention that after opening the dialog, typing a text there, closing and then reopening the dialog, the previously typed text remains untouched.
Such behavior forces me to assume that the dialog object is never released and it might be a potential place for the memory leak.
My questions:
Why the exit hook is not triggered when I leave the view, where this dialog had been defined?
Are there any best practices / common patterns for «resetting» the dialog's content in UI5?
Why the exit hook is not triggered when I leave the view, where this dialog had been defined?
Destroying a dialog has been always a somewhat questionable practice: when the user, for example, decides to open it again; the dialog with its entire tree of child nodes needs to be recreated and rerendered on the DOM, every single control there needs to be re-registered on the Core, as well as their events on the UIArea, models need to be propagated firing all kinds of events in the meantime, memory needs to be re-allocated, etc, etc.. To sum it up - it's quite costly.
The exit-hook, which is a low-level API, is however triggered automatically when the application calls destroy() on the ManagedObject. For example, the Fiori Launchpad automatically calls currentUIComponent.destroy() when the user navigates to Home triggering the exit-hook on every child element including the dialog.
Are there any best practices / common patterns for «resetting» the dialog's content in UI5?
Yes, see my answer on How to reset reset all screen's control values.
Is there a way in Ractive to destroy the instance (unrender event listeners, etc) but not remove the DOM changes that it made?
I'm building a one page app and the goal is that each view is its own instance of ractive. However, in some cases there is persistent data and I want to avoid a 'pop in' effect. By preserving what's in the DOM, the next ractive instance can load on page change and do its diff, removing anything that changed but preserve the things that didn't.
Thanks in advance.
If I make ajax requests that remove the body HTML and append new HTML, do I need to also remove any event handlers that were added to the previous HTML?
I noticed that if I don't, everything works fine. Does the browser free the memory and stuff? What if I do thousands of such ajax requests without refreshing the browser? Will I get memory leaks?
If you add jQuery event handlers :
they will be cleaned if you use jQuery removing/replacing functions
they won't be cleaned if you use direct DOM functions
On most function documentations, you have a comment similar to this one :
When .html() is used to set an element's content, any content that was
in that element is completely replaced by the new content.
Additionally, jQuery removes other constructs such as data and event
handlers from child elements before replacing those elements with the
new content.
If you're coherent, you'll have no memory leak, you don't have to manually remove data or event handlers. There's usually no problem in having a page kept open for days and issuing thousands of Ajax requests and changing the screen accordingly.
I'm trying to get rid of memory leak in IE8, caused by reference loops between event handlers and DOM elements during iframe navigation. I can not modify other scripts on the page.
So the idea was to walk through the DOM and window object and nullify all fields to ensure no DOM element references event handler.
Now the problem is I need to do it after all other unload handlers are run, because other handlers might depend on fields I'm going to nullify.
I tried to do store document object before navigation somewhere in parent window, and then, after navigation in my iframe completes (onload event), run cleanup on stored document object. However, apparently, you can't do it, because after old page is unloaded accessing this document becomes illegal (access error).
The other approach I tried was to find way to add window unload handler, which will be guaranteed to be called the last, however, I did not successed in it so far. To achieve that I tried to call all handlers for the unload event, clean them, and than run my code, but I did not found a way to trigger unload event manually.
Any ideas? Unfortunately, page uses jQuery and Microsoft Ajax, which have their own unload handlers. In particular, my nullification breaks MS Ajax unload handler, because it removes all library namespaces.
I think it's impossible to guarantee the removal of all event listeners if you don't know how they were attached in the first place.
IE 8 uses attachEvent, so if a listener is added like:
element.attachEvent('onclick', function(){...});
then you have no way of knowing that the listener is attached. Setting the onclick property to some new value (say '') will not remove the listener, nor will adding another by attachEvent. In DOM compliant browsers, replacing an element with a clone of itself will remove listeners, but not in IE where attachEVent has been used.
Strategies for avoiding memory leaks usually involve not creating circular references involving DOM elements.
Strategies for removing all listeners generally rely on keeping a list of what's been added, then explicitly removing them before the page unloads.
Both can be difficult and are impossible to guarantee if you don't have control of all code in the page.
I am creating a jQuery Mobile web app, which loads some pages.
For example, a.html is my main page. It may call b1.html,b2.html,...,b100.html (User may click on one of buttons). (The pages are loading with ajax navigation feature of jQuery Mobile)
And there is some events in each b[i].html page, and ids and many things are same in every b[i].html page. But the point is, at any time, just one of them may be in DOM. So there will be no id conflicts or such.
The problem
The problem is the conflict of the events. When user come back to a.html from b[i].html, the HTML will be removed, but events will remain. This will cause many problems if I first go to b[i].html, and then come back to a.html and then go to b[j].html. I mean, b[j].html will not work correctly... :(
What I have tried
I have putted this in a.html, to remove all events:
$("#mainpage").off("pagebeforeshow").on("pagebeforeshow",function() {
$("*").not("#mainpage").off();
//Other initialization codes...
});
But, problem not solved...
(mainpage is the id of data-role="page" of a.html)
Example
For example, I have this in each b[i].html:
$(window).resize(function () {
alert("Resized");
});
At the beginning (in a.html), If I resize the window, there will be no alerts, but after visiting b[i].html and then coming back to a.html, I'll see alerts if I resize the window, even with that line of code (What I have tried part.)...
So, How to remove those event handlers when users come back to a.html from b[i].html?
If you are using jQuery Mobile, more than one of said pages may exist in the dom at the same time, resulting in non-unique id conflicts.
I would ditch putting js on the individual pages and have it done from the primary page, or through a script loading system such as require.js. Then do all of the events through delegation from the document. Obviously that won't work with window.resize(), but it doesn't need to be delegated anyway.
"Can you please explain more?"
Basically, if you are including scripts on the child pages, you will need to have both setup and teardown for every page. setup adds the events, and teardown removes them. If you instead used a single global script that adds ALL of the events using event delegation from the document, all of the pages should work. Obviously that global script could get pretty big on a complex site, so you could instead use require.js to load in js that does the same thing as needed, preventing it from loading the same dependency more than once.
As far as removing all events, I've never tried this, but can you use $("*").off()? According to the docs it should work. I'm not sure how it will affect jQuery mobile. To remove events on the document and/or window, you will have to do it manually because $("*") will not select them.
$(document).on("vmousemove","#link",func) is how you delegate an event from the document.