Sourabh Dev
Sourabh Dev

Reputation: 743

Why does the onclick event listener gets removed?

I am making a small matching game in javascript. Why does the onclick listener stop working after the first successful attempt. How do I rectify this issue.

I am expecting the listener to be attached to the last child element of the div with id "leftSide" even after I refresh all nodes by removing old nodes first.

<!DOCTYPE html>
<html>
    <head>
        <title>Matching Game</title>
        <style>
            img {
                position: absolute;
            }

            div {
                position: absolute;
                width: 500px;
                height: 500px;
            }

            #rightSide {
                left: 500px;
                border-left: 1px solid black;
            }                    
        </style>        
    </head>
    <body>
        <h2>Matching Game</h2>
        <p>Click on the extra smile on the left</p>
        <div id="leftSide"></div>
        <div id="rightSide"></div>
        <script>
            var numberOfFaces = 5;
            var theLeftSide = document.getElementById("leftSide");
            var theRightSide = document.getElementById("rightSide");

            function generateFaces() {
                for (var i=0; i<=numberOfFaces; i++) {
                    new_img = document.createElement("img");
                    new_img.src="smile.png";
                    new_img.style.top=String(Math.floor(Math.random()*400))+"px";
                    new_img.style.left=String(Math.floor(Math.random()*400))+"px";
                    theLeftSide.appendChild(new_img);                    
                }

                // Clone all images to right side
                leftSideImages = theLeftSide.cloneNode(true);
                leftSideImages.removeChild(leftSideImages.lastChild);
                theRightSide.appendChild(leftSideImages);
            }

            generateFaces()

            var theBody = document.getElementsByTagName("body")[0];

            theBody.onclick = function gameOver() {
                alert("Game Over!");

                theBody.onclick = null;
                theLeftSide.lastChild.onclick = null;
            };

            theLeftSide.lastChild.onclick = function nextLevel(event) {
                // remove child nodes
                while (theLeftSide.firstChild) {
                    theLeftSide.removeChild(theLeftSide.firstChild);
                }

                while (theRightSide.firstChild) {
                    theRightSide.removeChild(theRightSide.firstChild);
                }

                // new nodes
                event.stopPropagation();
                numberOfFaces += 5;
                generateFaces();
            };


        </script>
    </body>
</html>

Upvotes: 1

Views: 1453

Answers (4)

user4964433
user4964433

Reputation:

you could try something like this:

<!DOCTYPE html>
<html>
    <head>
        <title>Matching Game</title>
        <style>
            img {
                position: absolute;
            }

            div {
                position: absolute;
                width: 500px;
                height: 500px;
            }

            #rightSide {
                left: 500px;
                border-left: 1px solid black;
            }                    
        </style>        
    </head>
    <body>
        <h2>Matching Game</h2>
        <p>Click on the extra smile on the left</p>
        <div id="leftSide"></div>
        <div id="rightSide"></div>
        <script>
            var numberOfFaces = 5;
            var theLeftSide = document.getElementById("leftSide");
            var theRightSide = document.getElementById("rightSide");

            var clickLastChild = function clickLastChild() {
                // remove child nodes

                theLeftSide.lastChild.onclick = function (event) {
                    while (theLeftSide.firstChild) {
                        theLeftSide.removeChild(theLeftSide.firstChild);
                    }

                    while (theRightSide.firstChild) {
                        theRightSide.removeChild(theRightSide.firstChild);
                    }

                    // new nodes
                    event.stopPropagation();
                    numberOfFaces += 5;
                    generateFaces();
                };
            };

            function generateFaces() {
                for (var i = 0; i <= numberOfFaces; i++) {
                    new_img = document.createElement("img");
                    new_img.src = "smile.png";
                    new_img.style.top = String(Math.floor(Math.random() * 400)) + "px";
                    new_img.style.left = String(Math.floor(Math.random() * 400)) + "px";
                    theLeftSide.appendChild(new_img);
                }

                // Clone all images to right side
                leftSideImages = theLeftSide.cloneNode(true);
                leftSideImages.removeChild(leftSideImages.lastChild);
                theRightSide.appendChild(leftSideImages);
                clickLastChild();
            }

            generateFaces();

            var theBody = document.getElementsByTagName("body")[0];

            theBody.onclick = function gameOver() {
                alert("Game Over!");

                theBody.onclick = null;
                theLeftSide.lastChild.onclick = null;
            };
            
            clickLastChild();

        </script>
    </body>
</html>

Upvotes: 1

Buzinas
Buzinas

Reputation: 11725

The problem is when you attach an event handler to an element, when that element is destroyed, the event is gone too (when the GC cleans). The theLeftSide.lastChild doesn't creates a reference to whatever last child element of theLeftSide is. It creates a reference to the current last child.

You must change the place you add the event handler to inside your generateFaces function, so everytime you change the theLeftSide children, you get again the last child, and add the event.

Take a look at the example below:

<!DOCTYPE html>
<html>

<head>
  <title>Matching Game</title>
  <style>
    img {
      position: absolute;
    }
    div {
      position: absolute;
      width: 500px;
      height: 500px;
    }
    #rightSide {
      left: 500px;
      border-left: 1px solid black;
    }
  </style>
</head>

<body>
  <h2>Matching Game</h2>
  <p>Click on the extra smile on the left</p>
  <div id="leftSide"></div>
  <div id="rightSide"></div>
  <script>
    var numberOfFaces = 5;
    var theLeftSide = document.getElementById("leftSide");
    var theRightSide = document.getElementById("rightSide");

    function generateFaces() {
      for (var i = 0; i <= numberOfFaces; i++) {
        new_img = document.createElement("img");
        new_img.src = "http://www.webgranth.com/wp-content/uploads/2012/03/smile.png";
        new_img.style.top = String(Math.floor(Math.random() * 400)) + "px";
        new_img.style.left = String(Math.floor(Math.random() * 400)) + "px";
        theLeftSide.appendChild(new_img);
      }

      // Clone all images to right side
      leftSideImages = theLeftSide.cloneNode(true);
      leftSideImages.removeChild(leftSideImages.lastChild);
      theRightSide.appendChild(leftSideImages);
      
      theLeftSide.lastChild.onclick = nextLevel;
    }

    generateFaces()

    var theBody = document.getElementsByTagName("body")[0];

    theBody.onclick = function gameOver() {
      alert("Game Over!");

      theBody.onclick = null;
      theLeftSide.lastChild.onclick = null;
    };

    function nextLevel(event) {
      // remove child nodes
      while (theLeftSide.firstChild) {
        theLeftSide.removeChild(theLeftSide.firstChild);
      }

      while (theRightSide.firstChild) {
        theRightSide.removeChild(theRightSide.firstChild);
      }

      // new nodes
      event.stopPropagation();
      numberOfFaces += 5;
      generateFaces();
    };
  </script>
</body>

</html>


Note: I've simpled changed the theLeftSide.lastChild.onclick = function nextLevel(event) {, to a simple function declaration: function nextLevel(event) {, and then added the event handler inside the generateFaces function: theLeftSide.lastChild.onclick = nextLevel;.

Upvotes: 1

Gaurav Gandhi
Gaurav Gandhi

Reputation: 3201

Here i made fully working jsfiddle

You have to call generateFaces() from game over scenario also. And yes as @epascarello mentioned, you are losing click events as you are setting them to null.

Upvotes: 0

epascarello
epascarello

Reputation: 207537

Because you set the onclick to false

theBody.onclick = null;
theLeftSide.lastChild.onclick = null;

when you click the tbody.

Upvotes: -1

Related Questions