Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript jQuery Basics (2014) Creating a Spoiler Revealer Perform: Part 1

fabiopantano
fabiopantano
8,683 Points

Hide spoiler in pure Javascript

Hi all, I'm trying to replicate the hiding spoiler step in pure javascript with the following code:

var el = document.getElementsByTagName("span");
var container = el.parentNode;
container.removeChild(el);

It doesn't work. I get "TypeError: Cannot read property 'removeChild' of undefined".

Any help?

Thanks in advanced!

3 Answers

Sean T. Unwin
Sean T. Unwin
28,690 Points

Hi Fabio,

This is happening because you are using getElementsByTagName(). This returns an HTML Collection object.

Currently, el is that HTML Collection so the removeChild method fails as el is not an element, hence the TypeError - it is expecting one object type while you are giving a different type.

There are a few ways to deal with this. I will show you two three, with the last one two using a different method call altogether.

Edit: As Hugo Paz pointed out there is or will be more than one span throughout the course of this lesson, and through a fun little code challenge with him, I have added a third method at the end of this post.


The first method, while still using getElementsByTagName(), is to access the item in the Collection. You can do this just as you would access an array item. Since we know that there is only one span element the length (by checking el.length) is 1 so we can access that first item and grab it's parentNode with:

var container = el[0].parentNode;

Then to remove you would do:

container.removeChild(el[0]);

The second method is to use querySelector since we only have one element to grab. As an aside, if we were grabbing more than one, say all the spans, we could use querySelectorAll - this is like jQuery's element selector where you can grab a list of elements by type, class, or id or multiples of each.

By using querySelector, you will only have to change a portion of one line of code you have currently. That would be to change getElementsByTagName to querySelector, which would look like:

var el = document.querySelector('span');

This final method is the least amount of code and will remove each element in a list of elements (in this case an instance of a NodeList), regardless of parent.

var elList = document.querySelectorAll('span');

Array.prototype.forEach.call(elList, function(elListItem) {
    elListItem.parentNode.removeChild(elListItem);
  });

I hope this helps to clarify for you. :)

Hugo Paz
Hugo Paz
15,622 Points

Actually there are 2 span elements. I was working on a solution but for some reason the loop fails after running once.

var el = document.getElementsByTagName("span");

var size = el.length;

var container;

for(var i = 0; i < size;i++){
  container = el[i].parentNode;  
  container.removeChild(el[i]);
}

This deletes the first span but then i get an error on the second one "Cannot read property 'parentNode' of undefined".

Sean T. Unwin
Sean T. Unwin
28,690 Points

Hugo Paz,

I only had one span in the Workspace and the video is showing only one as well.

Have you confirmed the value of your size variable?

Also, if there are more than one, do they have the same parent?

Hugo Paz
Hugo Paz
15,622 Points

There are 2 span and the size variable value is 2.

Here is the html

<!DOCTYPE html>
<html>
<head>
    <title>Star Wars Spoilers</title>
    <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" title="no title" charset="utf-8">
</head>
<body>
    <img src="img/deathstar.png" />
    <p class="spoiler">
        <!--Spoiler:-->
        <span>Darth Vader is Luke Skywalker's Father!</span>
    </p>
    <p class="spoiler">
        <!--Spoiler:-->
        <span>Luke and Leia are siblings. Ew.</span>
    </p>
    <script src="http://code.jquery.com/jquery-1.11.0.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="js/app.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
Sean T. Unwin
Sean T. Unwin
28,690 Points

I see. They have different parents as well.

Hugo Paz
Hugo Paz
15,622 Points

After some tinkering i found a solution, though it might not be the simplest.

var el = document.getElementsByTagName("span");

var size = el.length;

var array = [];

for(var i=0;i<size;i++){

    array.push(el[i]);
}

function removeNode(value){

  parent = value.parentNode;
  parent.removeChild(value); 

}
array.forEach(removeNode);
Sean T. Unwin
Sean T. Unwin
28,690 Points

Nice. I started out with the same strategy and had practically the same code that you posted (you're not using the var keyword before the parent variable in your function which will cause that variable to enter the global namespace). I was about to post it until I saw you already had.

Like you, I felt it could be better. I have appended my original answer to include the following.

var elList = document.querySelectorAll('span');

Array.prototype.forEach.call(elList, function(elListItem) {
    elListItem.parentNode.removeChild(elListItem);
  });

By the way, it was fun bouncing around with you. Have an upvote! :)

pauloliva
pauloliva
7,889 Points

Awesome examples!

fabiopantano
fabiopantano
8,683 Points

Thanks Sean! I forgotten that getElementsByTagName() return an HTML collection object. It works now.

Thanks Hugo too! :)