When creating a new Image element in javascript, Google Chrome's memory tool (Developer tools > Timeline > Memory) considers it as a new DOM element, naturally.
In my case, I'm ending up with 1500+ DOM elements, and I wish to get rid of them. I have tried to save all objects in an array and delete all of them in a loop when I'm ready creating all objects, resulting in the following error:
Uncaught TypeError: Cannot call method 'removeChild' of null
That indicates the Image objects doesn't appear in the actual DOM.
var images = [];
var i, image;
for( i = 0; i < urls.length; i++ ) {
image = new Image();
image.src = urls[i];
}
// other stuff happens
for( i = 0; i < images.length; i++ ) {
// apparently this doesn't work because I'm not adding the image to my DOM
// images[i].parentNode.removeChild( images[i] );
// delete images
}
Is there a way to remove/delete/unset/dispose the Image objects?
Setting images = null would remove your reference in code to the object. However, to implement its load event, Chrome has to have its own internal reference to the object.
That is, you could have code like this:
for( i = 0; i < urls.length; i++ ) {
image = new Image();
image.src = urls[i];
image.onload = function(){alert('Test');};
image = null;
}
This way you would still get a lot of "Test" alerts, even though you do not have a reference to these objects.
Hence, my guess is that it is a bug in Chrome, not in your code.
Update: looking through the Chromium source sort of proves that (I mean the comment on lines 67-71 of this file, especially the FIXME note http://code.google.com/searchframe#OAMlx_jo-ck/src/third_party/WebKit/Source/WebCore/bindings/v8/custom/V8HTMLImageElementConstructor.cpp ):
// Make sure the document is added to the DOM Node map. Otherwise, the HTMLImageElement instance
// may end up being the only node in the map and get garbage-ccollected prematurely.
// FIXME: The correct way to do this would be to make HTMLImageElement derive from
// ActiveDOMObject and use its interface to keep its wrapper alive. Then we would
// remove this code and the special case in isObservableThroughDOM.
If you are not adding them to the DOM (like using appendChild to a parent), then removeChild is useless. The Image objects are only in the memory.
And to dispose items in the memory, you only need to remove references to these objects (like set the referencing variable to null), and garbage collection will do the rest. If you can't null them all, they won't be GC'ed.
To get rid of the bug described by "naivists" of chrome and specilly IE and EDGE.
You can change the image source to empty so it take zero memory.
image.src = '';
image = null;
AFAIK, assigning null should clean it up: images[i] = null
I think only way is to do this:
for( i = 0; i < images.length; i++ )
images[i] = null;
}
// or just
images = null;
Related
first time posting.
I have an intermittent issue with IE8 where I get an:
"Object doesn't support this property or method"
This occurs when retrieving an iFrame into a variable using the following:
var wfw = window.frames[i];
The bit that is confusing me is that previous to this line of code I call:
var length = window.frames.length;
which returns the value 4. I guess my question is why can I not retrieve something that exists?
var length = window.frames.length;
for(var i = 0; i < length; i++){
// Grab the window.frames collection so that we can iterate through it.
var wfw = window.frames[i];
try {
// Attempt to push the BISCheckEvent code onto the child Window and execute it. This is to account for Iframes that have already loaded
wfw.eval("var BISCheckEvent = new " + BISCheckEventImpl.toString() + "; BISCheckEvent.attachHandler(); ");
} catch(e){
// Iframe has no source, so just capture the exception and move on.
}
}
Apologies for the late reply on this one however Sheikh Heera's suggestion of "Making sure all the frames loaded in the parent page" has addressed this issue.
I'm developing a chrome extension and having a problem with a nodelist type.
var compoentChange = document.getElementById("component_change");
var items = compoentChange.getElementsByTagName("option");
When I console.log(items), it shows [item: function]. When I expand it, it has all the option elements and length property.
The problem is that I can't access those elements. When I console.log(items.length), I get undefined.
How do I iterate through items variable?
for(i in items){} and for loop do not work.
NodeLists are array-like objects. You can iterate with regular for loop (not for..in):
for (var i = 0; i < items.length; i++) { ... }
Or you can convert this array-like object to a real array and use native array methods on it:
[].forEach.call(items, function(item) { ... });
You can still do items.length, so just make a for loop like this. I suggest pushing it into an array.
var myArray = [];
for(var i=0; i<items.length; i++){
myArray.push(items[i]);
}
Alright if this isn't an option maybe try something like this:
var myArray = [];
for(var i=0, e=1; i<e; i++ ){
if(items[i] != undefined){
e++;
myArray.push(items[i]);
}else{
break;
}
}
If you're logging the two during the pageload, the reason that you can console.log() the NodeList, but not the length attribute is because the NodeList is a "live" collection. Both are undefined until the DOM finishes loading, but because the NodeList is live, it will update in the Chrome console. The lengthattribute was undefined when it was logged, and because it's not live, it'll stay undefined in the console.
You can set a variable to reference the nodeList at any time, but wait until the DOM is ready before trying to use the data (using the document.ready function or perhaps document.addEventListener()).
I met similar problem with you. It turns out that it is because I should access data after DOM finishes loading.
document.addEventListener("DOMContentLoaded", function () {
divs = document.getElementsByTagName("div");
console.log(divs);
}, false);
for...of can be used for this situation. However due to a bug, this opportunity cannot be used on chromium.
I want to add an <img> to every <td> tag with a certain CSS Class in my page.
I used "querySelectorAll" to look them up, like this:
var painted = document.querySelectorAll('.painted');
Now I would like to add to each one of them a specific image with a unique ID. I assume I need to loop through the list somehow and edit each element's innerHTML, could anyone provide the syntax for that?
Thanks
Just run it like a normal for loop, and add properties to each element.
for (var i = 0, len = painted.length; i < len; i++) {
painted[i].id = "foo" + i;
painted[i].innerHTML = "<strong>Your content</strong>";
}
This uses innerHTML to create new content. If you need more complex content processing then there's no single syntax. You need to learn the DOM API and perform the needed manipulations.
For example, if you wanted to add an image, you can create one and append it directly.
for (var i = 0, len = painted.length; i < len; i++) {
var img = document.createElement("img");
img.id = "myimage" + i;
painted[i].appendChild(img);
}
Notice that I'm not using HTML markup. A DOM node doesn't have "HTML content". It is part of an object tree structure, so it has child nodes, which have their own child nodes, and so on.
So what you need to do, is perform some DOM selection with the current painted element as the root, and decide what should go where.
I have an 10 images,
each image represents digit 0-9 in a special font (thus the images)
in order to improve performance and delay, i pre-loaded the following images like the following:
function createDigit() {
for (var i = 0; i < 10; i++) {
var obj = new Image;
obj.src = 'digit' + i + '.png';
digitHash[i] = obj;
}
}
so in digit hash, i have keys indexed from 0 to 9, and each corresponding value is the image object reference, which src is mapped to the image file location.
now in my html, i have a div tag
<div id='digits'></div>
now say i want to display '2000'
so i have the following jquery
$('#digits').append(dightHash[2], dightHash[0], dightHash[0], dightHash[0]);
it only displays '20'
After some debugging and printing in firefox console, i notice that it happens when you are appending the SAME image reference more than once!
in other words, the second zero and third zero in '2000' are not appended and thus we only have '20'
if i append the following:
$('#digits').append(dightHash[2], dightHash[3], dightHash[4], dightHash[5]);
i get the full display of '2345', becauase there is no duplicate image reference in append
How can I resolve this issue?
is there any other than append method of jquery i can use??
Thanks
As had already been explained, .append() moves an object from wherever it is to the specified location. It does not make a copy of the object.
Because of that, I would suggest you just create the desired objects like this and then you don't have to worry about duplicate digits as they will each get their own image object this way:
// create an individual image
function makeDigit(n) {
var img = new Image();
img.src = 'digit' + n + '.png';
return(img);
}
// Force all images into browser memory cache for fast loading:
function cacheDigits() {
for (var i = 0; i < 10; i++) {
digitHash.push(makeDigit(i));
}
}
$('#digits').append(makeDigit(2), makeDigit(0), makeDigit(0), makeDigit(0));
Yes, append actually move around your DOM, instead of automatically making copies of the object you are appending.
You can call .clone() so that appends take the copy of your image and append it instead of moving around the ref
$('#digits').append(dightHash[2], $(dightHash[0]).clone(), $(dightHash[0]).clone(), $(dightHash[0]).clone());
Why doesn't this work?
var row = document.getElementById(currentRow);
var otherRow = document.getElementById(targetRow);
row.cells[0] = otherRow.cells[0];
This works with
row.cells[0].innerHTML = otherRow.cells[0].innerHTML;
However, there are attributes attached to the cell which I also want to move over without having to manually recreate them.
Solution (Note: more is being done in my actual implementation, but this is the framework):
for (var i = 0; i < 4; i++) {
var copyTo = row.cells[i];
var copyFrom = otherRow.cells[i].cloneNode(true);
copyTo.parentNode.replaceChild(copyFrom, copyTo);
}
You should be able to use cloneNode() to actually clone the node and its attributes.
Each entry in cells refers to a DOMElement. When you typed row.cells[0] = otherRow.cells[0], you are saying that you want row.cell[0] to reference the same DOMElement as otherRow.cells[0].
I'm guessing you want row.cells[0] to have the same text or HTML as otherRow.cells[0]; in which case, the second code snippet will do just that, since you are actually modifying the DOMElement, and not just changing which DOMElement you are referencing.