Iframes and memory management in Javascript - javascript

I have links that load pages into iframes. I have been monitoring the accumulation of data in memory using Google Chrome's memory heap profiler and I noticed some leaks in memory.
I loaded the page and took the first snapshot which added up to 2.69 MB. I clicked the link that opens a page into an iframe and took another snapshot giving me 14.58 MB in total. I removed the iframe using the following jquery snippet:
$('#myframe').unbind();
$('#myframe').remove();
/*
* By the way, I also tried $('#myframe > *') as a selector.
* It still didn't work. Even if it would, it doesn't look like a viable solution to me.
* It looks too resource intensive.
*
* I forgot to mention that I am not using Ajax to load my pages
*/
I took another snapshot and got 5.28 MB which indicated a deviation of 2.59 MB from the initial value, which according to my understanding indicates memory leackage.
Now, my question is: If I remove an iframe (which includes the document loaded in it) doesn't the garbage collector find it necessary to also remove all the objects contained in that document from memory? Or will I have to do this manually?
I thought that if I load a document into an iframe, it's size will not affect the memory use on the parent page. I though it will be considered a separate window, but obviously that wasn't a well informed assumption on my part.
Any suggestions on how to tackle this?
Thank you.

In the iframe, trigger a reload before removing it and then remove it.
Remove
<iframe src="url" />​
$('a').click(function(){
$('iframe')[0].contentWindow.location.reload();
setTimeout(function(){
$('iframe').remove();
}, 1000);
});​
DEMO here.
Addionally, you can do a manual cleaning up too - i.e. if you have data in your cookies or HTML5 localStorage.
window.onbeforeunload = function(){
$(document).unbind().die(); //remove listeners on document
$(document).find('*').unbind().die(); //remove listeners on all nodes
//clean up cookies
/remove items from localStorage
}

If any objects from the iframe is referenced in a object in the main window that object won't be removed from the DOM, so, if you have something like this:
Main window:
var object = {};
function iframe_call(data){
object.iframe_data = data.something
}
iframe:
function onClick(){
parent_object.iframe_call(this);
}
this happens especially if you refer DOM objects.

var frame = document.getElementById("myframe");
frame.src = "about:blank";
This worked from me and prevented memory leaks. Ig you must destroy the parent of the iframe, do it with some delay to prevent memory leak

Related

DOM Events - Progress Indicator for Iframe location change

I am looking for a solution to show a progress indicator when an iframe page changes, but prior to the iframe loading completely.
There are 100's of pages on Stack Overflow going over the differences between jQuery ready(), JavaScript window.load, document.load, DOMContentReady, on('pageinit'...) and after reading the differences on all these various techniques I'm now a bit stuck on how to accomplish trapping the event within the iframe.
So far I have only succeeded in capturing when the iframe has changed once the DOM is built. I would like to be able to detect when a page is about to load so I could have some sort of indicator/spinner in my header.
This is what I have so far (capturing the iframe change on the onload):
.....
<iframe id="rssID" src="http://feeds.reuters.com/reuters/oddlyEnoughNews"
onload="blerg()" style="width: 800px; height:600px"/>
......
$(document).on('pageinit','#index', function(){
alert('pageinit'); //gets called on first load
});
$(document).on('pageinit','#rssID', function(){
alert('pageinit rssFeed'); //nothing happens.
});
function blerg() {
var myIframe = document.getElementById("rssID");
myIframe.onload = func;
};
function func() {
alert("changed");
}
http://jsfiddle.net/Gdxvs/
It would appear that pageinit is the correct path but I have no idea on how to trap that within an iframe. I would prefer not using jQuery, but I'm not sure that is possible without huge amounts of code.
One final: Do I need to use a die("pageinit");

jquery: exclude external resources from $(window).load()

I need to execute some scripts when all the resources on my domain and subdomain are loaded, so I did this:
$(window).load(function(){
// al my functions here...
}
The problem is that there are some external resources (not on my domain and subdomain) that sometimes take longer to load. Is there a way to exclude external resources from the load event?
EDIT:
I was hoping to do something like:
$(window).not(".idontcare").load(function()
but it's not working
I guess your external resources rely on a src attribute.
If so, in your page source code you could set the src attribute of the resources you don't want to wait for, not as src but as external_src.
Then you could easily do:
$(document).ready(function(){
$(window).load(function(){
// all your functions here...
});
$('[external_src]').each(function() {
var external_src = $(this).attr("external_src");
$(this).attr("src", external_src); // now it starts to load
$(this).removeAttr("external_src"); // keep your DOM clean
//Or just one line:
//$(this).attr("src", $(this).attr("external_src")).removeAttr("external_src");
});
});
This way the external resources should start loading as soon as just the DOM is ready, without waiting for the full window load.
I have almost same case. But in my case, I want to exclude all iframes that load content from another site (e.g. youtube, vimeo etc). Found a work around, so the scenario is hide 'src' attribute from all iframes when DOM is ready and put it back when window is finish load all another content.
(function($){
//DOM is ready
$(document).ready(function(){
var frame = $('iframe'),
frameSrc = new Array();
if( frame.length ){
$.each( frame, function(i, f){
frameSrc[i] = $(f).attr('src');
//remove the src attribute so window will ignore these iframes
$(f).attr('src', '');
});
//window finish load
$(window).on('load',function(){
$.each( frame, function(a, x){
//put the src attribute value back
$(x).attr('src', frameSrc[a]);
});
});
}
});
})(jQuery);
You can mark all elements in your site that load external resources by adding a special class, and change the iframe with $('.special_class') or something like that. I dont know if this is the best way but at least it works great in my side :D
Unfortunately, the window.onload event is very strict. As you might know it will fire when all und every resource was transfered and loaded, images, iframes, everything. So the quick answer to your question is no, there is no easy-to-use way to tell that event to ignore external resources, it makes no difference there.
You would need to handle that yourself, which could be a tricky thing according to how those resources are included and located. You might even need to manipulate the source code before it gets delivered to accomplish that.
As far as I know, there is an async - tag for script tags. You can your includes to:
<script src="script_path" async="true"></script>
This will not include them to the event.
maybe
$(document).ready(...)
instead of $(window).load() will help?
The document ready event executes already when the HTML-Document is loaded and the DOM is ready, even if all the graphics haven’t loaded yet.

Check if children elements (images) are loaded before executing?

I have a div which contains a number of images. I would like to check if all the images in this div are loaded before executing some code. I can't work it off the entire document as there are other things on the page which may take much longer to load.
How can I do this? jquery is available for use.
You should sue the load event
$('img').load(function(){
//here do what you need to do when the img is loaded
});
there are some caveats
Caveats of the load event when used with images
A common challenge developers attempt to solve using the .load()
shortcut is to execute a function when an image (or collection of
images) have completely loaded. There are several known caveats with
this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache
You can do sth like this:
var files = ['a.jpg','b.png']; //defines all assets needed
$.each(files,function(){
var tmp = new Image();
tmp.src = this; //creates a new dummy object
tmp.on('load',function(){
var i = files.indexOf(this);
files.splice(i,1); //when dummy object has loaded it gets removed from array
if (!files.length){ //when array is empty we're all set
alert('Preloading done!');
}
});
});

How to access XUL Overlay's DOM

In my Firefox Addon's overlay.xul, can I access it's DOM in javascript? I can't figure out how.
Thanks in advance.
An overlay is merged with the DOM of the document that it applies to, it doesn't have a DOM of its own. So you don't access the DOM of "the overlay", you access the DOM of the document that you overlaid. And that is being done the usual way, e.g. via document.getElementById(). You have to consider one thing however: never access the DOM before the document finished loading, this will cause various issues (like other overlays failing to apply). So if your overlay includes a script you can write:
window.addEventListener("load", function() {
// Window finished loading, now we can do something
var button = document.getElementById("my-extension-button");
button.style.backgroundColor = "green";
}, false)

Preventing AJAX memory leaks

I am working on a web application that is designed to display a bunch of data that is updated periodically with AJAX. The general usage scenario would be that a user would leave it open all day and take a glance at it now and then.
I am encountering a problem where the browsers memory footprint is growing slowly over time. This is happening in both Firefox and IE 7 (Although not in Chrome). After a few hours, it can cause IE7 to have a footprint of ~200MB and FF3 to have a footprint of ~400MB.
After a lot of testing, I have found that the memory leak only occurs if the AJAX calls are being responded to. If the server doesn't respond to anything, I can leave the page open for hours and the footprint won't grow.
I am using prototype for my AJAX calls. So, I'm guessing there is an issue with the onSuccess callback creating these memory leaks.
Does anyone have any tips on preventing memory leaks with prototype / AJAX? Or any methods on how to troubleshoot this problem?
EDIT: found out the issue lies in a js graphing library I am using. Can be seen here.
The biggest thing you can watch out for is events, and how you assign them.
For instance, take this scenario (since you haven't provided one):
<div id="ajaxResponseTarget">
...
</div>
<script type="text/javascript">
$(someButton).observe('click', function() {
new Ajax.Updater($('ajaxResponseTarget'), someUrl, {
onSuccess: function() {
$$('#ajaxResponseTarget .someButtonClass').invoke('observe', 'click', function() {
...
});
}
});
});
</script>
This will create a memory leak, because when #ajaxResponseTarget is updated (internally, Prototype will use innerHTML) elements with click events will be removed from the document without their events being removed. The second time you click someButton, you will then have twice as many event handlers, and garbage collection can't remove the first set.
A way to avoid this is to use event delegation:
<div id="ajaxResponseTarget">
...
</div>
<script type="text/javascript">
$('ajaxResponseTarget').observe('click', function(e) {
if(e.element().match('.someButtonClass')) {
...
}
});
$(someButton).observe('click', function() {
new Ajax.Updater($('ajaxResponseTarget'), someUrl);
});
</script>
Because of the way DOM events work, the "click" on .someButtonClass will fire also on #ajaxResponseTarget, and Prototype makes it dead simple to determine what element was the target of the event. No events are assigned to elements within #ajaxResponseTarget, so there is no way for replacing its contents to orphan events from targets within.
I may be wrong but it sounds like you are creating closures around the response object. Each response object will be different which results in an increased memory footprint.

Categories