0

I'm using plain old JavaScript (no jQuery or other).

I have an HTML5 app where I want to delete canvas from a section element:

<section id="thumbnails">
    <h1>My Bookmarks:</h1>
    <canvas height="60px" width="100px" data-time="2.1167500019073486"></canvas>
    <canvas height="60px" width="100px" data-time="3.60492205619812"></canvas>
    <canvas height="60px" width="100px" data-time="4.558893203735352"></canvas>
    <canvas height="60px" width="100px" data-time="5.411310195922852"></canvas>
</section>

The code that I use is the following:

document.getElementById('videos').addEventListener('change',function(e){
    var canvasElements=document.getElementById('thumbnails').getElementsByTagName('canvas');
    for(var i=0;i<canvasElements.length;i++){
        document.getElementById('thumbnails').removeChild(canvasElements[i]);
        console.log('removed canvas');
    }
},true);

However when the code runs, not all canvas are removed from that section and I don't even see the log statement in the console.

Go on this page, take some thumbnails, then switch to another video: http://laurent-tonon.com/VideoFinal/

I really don't get it...

0

4 Answers 4

7

The problem is that getElementsByTagName returns a dynamic nodeList that will change beneath you when you remove the items. You can work around that by processing the array in reverse order so the only part of the nodeList that changes when you remove an item is the part you've already processed, not the part you still have to go:

document.getElementById('videos').addEventListener('change',function(e){
    var canvasElements=document.getElementById('thumbnails').getElementsByTagName('canvas');
    for (var i = canvasElements.length - 1; i >= 0; i--) {
        canvasElements[i].parentNode.removeChild(canvasElements[i]);
        console.log('removed canvas');
    }
},true);

Processing it in forward order like you did causes you to remove every other item and leaves half the items left. You remove the first item. It gets removed from the nodeList moving every item in the nodeList down one slot. You then advance your index to [1] and you remove what was originally the third item (but is now in the 2nd slot in the array). The item that was originally the second item is now in the first slot in the array and you will never end up removing it.

Reversing the order you process the nodeList works around that problem since the ones that get removed from the array are at the end and don't change the position of the ones you still have to process.


I know this wasn't a jQuery question and is perfectly solvable with the above plain javascript, but it's times like this where I'm reminded how easy some things are in jQuery:

$("#thumbnails canvas").remove();
Sign up to request clarification or add additional context in comments.

Comments

3

Since you are already relying on html5 capable browser, why not use some updated methods:

[].slice.call( document.querySelectorAll( "#thumbnails canvas" ) ).forEach(
    function(v){
    v.parentNode.removeChild(v);
    }
);

Comments

2

I believe the problem is getElementsByTagName() returns a live nodeList, which causes problems when you are removing the elements and referring to them by their index.

Try this...

while(canvasElements.length) {
    canvasElements[0].parentNode.removeChild(canvasElements[0]);
    console.log('removed canvas');
}

jsFiddle.

Comments

1

Why so complicated? How about either

document.getElementById('videos').addEventListener('change',function(e){
    var canvasElements=document.getElementById('thumbnails');
    while (canvasElements.hasChildNodes())
    {
        canvasElements.removeChild(canvasElements.lastChild);
    }
});

or, even more simple

document.getElementById('videos').addEventListener('change',function(e){
    document.getElementById('thumbnails').innerHTML = '';
});

?

5 Comments

The canvasElements won't have a hasChildNodes() method.
@alex care to elaborate? Mind the fact that my variable naming is a bit confusing, though, since I simply copy/pasted chunks of the OP's code. canvasElements refers to the surrounding div.
This also removes the Bookmarks <h1> which the OP's code does not try to do.
@vzwick: Oh wait, yeah I see. That did indeed confuse me. Still, it looks like the OP wants to preserve the h1 element.
May I argue that just moving the <h1> outside the element would be much less a hassle?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.