removeEventListener() while using anonymous function

ok, I decided to try to make a memory match game on my own and I am probably in way over my head. I just can’t let it go though. Can someone please tell me how to make a removeEventListener work when you have a function with parameters passed to the addEventListener?

This is my latest attempt and I don’t understand why it doesn’t work:

const flipCard = (cardPlace, arrayItem) => {
    if (cardPlace.src === cardBack.src) {
        cardPlace.src = arrayItem.src;
     }
    else {cardPlace.src = cardBack.src;
    }    
}
    

cardPlace1.addEventListener('click', function flip() {
    flipCard(cardPlace1, deck[1])
}); 

cardPlace1.removeEventListener('click', flip);

the addEventListener is working just fine, but the removeEventListener is not:(

Thanks for any help you can offer!!

It’s worth noting that some browser releases have been inconsistent on this, and unless you have specific reasons otherwise, it’s probably wise to use the same values used for the call to addEventListener() when calling removeEventListener()

I think it may be that you’re not using the same value here. flip is not defined within a scope that removeEventListener can access. So I’d look into that first (for example, you could define it before your eventListeners that wait you know you’re calling it uniformly).

Couple other things of note…

A callback is an anonymous function, for all intents and purposes…

el.addEventListener('click', function () {
    //
});

The place to remove the listener would be inside the handler. Recall that a function has a this property, and remove doesn’t need a callback…

el.addEventListener('click', function () {
    // ...
    this.removeEventListener('click')
});

Are you able to mount a REPL of the entire program so we can test this?

1 Like

Yes! Thank you! I just can’t seem to get past this point. Like I said though, I am most likely in way over my head, lol. If that is the case, feel free to tell me to come back to this problem later:) I just can’t seem to wrap my head around the logic. if you have any advice or good resources for learning how to solve complex problems, I’d appreciate it!

const cardBack = document.getElementById('card0');

const image1 = document.getElementById('image1');
const image2 = document.getElementById('image2');
const image3 = document.getElementById('image3');
const image4 = document.getElementById('image4');
const image5 = document.getElementById('image5');
const image6 = document.getElementById('image6');

const deck = [image1, image1, image2, image2, image3, image3, image4, image4, image5, image5, image6, image6];

const shuffleArray = (array) => {
    for (let i = array.length-1; i>0; i--) {
        let j = Math.floor(Math.random()*i);
        let temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

shuffleArray(deck);

let cardPlace1 = document.getElementById('card1');
let cardPlace2 = document.getElementById('card2');
let cardPlace3 = document.getElementById('card3');
let cardPlace4 = document.getElementById('card4');
let cardPlace5 = document.getElementById('card5');
let cardPlace6 = document.getElementById('card6');
let cardPlace7 = document.getElementById('card7');
let cardPlace8 = document.getElementById('card8');
let cardPlace9 = document.getElementById('card9');
let cardPlace10 = document.getElementById('card10');
let cardPlace11 = document.getElementById('card11');
let cardPlace12 = document.getElementById('card12');




const flipCard = (cardPlace, arrayItem) => {
    if (cardPlace.src === cardBack.src) {
        cardPlace.src = arrayItem.src;
     }
    else {cardPlace.src = cardBack.src;
    }    
}


    

cardPlace1.addEventListener('click', function flip() {
    flipCard(cardPlace1, deck[1])
}); 

cardPlace2.addEventListener('click', function flip() {
    flipCard(cardPlace2, deck[2]) 
});
cardPlace3.addEventListener('click', function flip() {
    flipCard(cardPlace3, deck[3]) 
});
cardPlace4.addEventListener('click', function flip() {
    flipCard(cardPlace4, deck[4]) 
});
cardPlace5.addEventListener('click', function flip() {
    flipCard(cardPlace5, deck[5]) 
});
cardPlace6.addEventListener('click', function flip() {
    flipCard(cardPlace6, deck[6]) 
});
cardPlace7.addEventListener('click', function flip() {
    flipCard(cardPlace7, deck[7]) 
});
cardPlace8.addEventListener('click', function flip() {
    flipCard(cardPlace8, deck[8]) 
});
cardPlace9.addEventListener('click', function flip() {
    flipCard(cardPlace9, deck[9]) 
});
cardPlace10.addEventListener('click', function flip() {
    flipCard(cardPlace10, deck[10]) 
});
cardPlace11.addEventListener('click', function flip() {
    flipCard(cardPlace11, deck[11]) 
});
cardPlace12.addEventListener('click', function flip() {
    flipCard(cardPlace12, deck[0]) 
});


/*
completed steps:
-create an array called deck of card images where there are 2 of each image
-use shuffleArray function to randomize the deck array
-create variables(i.e. cardPlace1) that point to numbered card elements that all contain the cardBack image to begin
-create a flipCard function that switches the cardBack image with an image from the randomized deck array
-add an event listener to each variable of the card elements that calls the flipCard function when clicked


next steps:
- if 2 cards have been flipped and the cards match: 
         -add 1 point to the innerHTML of the id='score' element
         -remove the event listener for those 2 cards so they are taken out of play and the flipped image shows for the rest of the game
- if the 2 cards don't match:
        -don't allow any other cards to be flipped until the two cards are flipped back over
- when all cards are matched:
        - change innerHTML of id='play-button' to change to 'You win! Click to play again!' 
        - make the 'play-button' start a new game if it's clicked again */

Slight segue, before I give a closer look to your code… Can we assume that all the images and cards are in a parent container? If that is the case then listeners can be made much simpler with event delegation. Attach the listeners to the parent element then wait for events to bubble up from their target locations. An event anywhere in the parent will trigger the handler.

The handler can be handed an event object, which object has a ‘target’ attribute that we can center around. We take eighteen listeners and whittle it down to two. The handlers are both focused on the target element, this in object context.

1 Like

Yes, I have them in a div with id=“game-board”. 12 of them display the cardBack image and are styled as flex items of the div container. the 6 card front images(image1, image2…) are also in the container but not displayed until I call the flipCard function.

Will this refer to the div element or the child that was clicked?
If I understand, my new function will have a parameter that takes an object. I will then use object.target to target the element that was clicked.

Something like:

const flipCard = (object, array) => {
if (object.target.src === cardBack.src) {
  ...}

how do I assign a different array item with each click?

It will be the target object (the child that is clicked).

TBH it’s been ages since I worked on this sort of project and have been digging for hours for a decent example. Best I could come up with is a jQuery thumbnail viewer. Copy these files to your machine. The images are hosted on CC’s CDN (I did this project in Codebits).

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>thumbViewer</title>
    <link href="style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <div id="thumbs">
      <img src="" alt="" title="Avonglen School marker" data="avonglen"/>
      <img src="" alt="" title="Empire School marker" data="empire"/>
      <img src="" alt="" title="Fabyan School marker" data="fabyan"/>
      <img src="" alt="" title="Jarrow School marker" data="jarrow"/>
      <img src="" alt="" title="Killarney School marker" data="killarney"/>
      <img src="" alt="" title="Passchendale School marker" data="passchendale"/>
      <img src="" alt="" title="Roseberry School marker" data="roseberry"/>
      <img src="" alt="" title="Sligo School marker" data="sligo"/>
      <img src="" alt="" title="St_Aubins School marker" data="staubins"/>
    </div>
    <div id="views"></div>
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
    <script src="script.js"></script>
  </body>
</html>
style.css
#thumbs {
  margin: 1em auto;
}
#views {
  width: 90%;
  height: 260px;
  margin: 15px auto;
  padding: 1em 0;
  border: 1px dotted black;
}
#thumbs{
  width: 92%;
}
#thumbs img {
  display: inline-block;
  width: 64px;
  height: 48px;
  border: 1px solid black;
  cursor: pointer;
}
#views img {
  display: block;
  width: 300px;
  height: 225px;
  margin: 0 auto;
  border: 1px solid black;
}
#thumbs,
#views {
    box-sizing: border-box;
}
script.js
const views = {
  avonglen: {
    thumb: '1/d/c/5/1dc5165a2266920e100cea9096ebffd682765056.jpg',
    view: '4/3/b/0/43b0f3e3121c6fb33117187fb206912c150297d6.jpg'
  },
  empire: {
    thumb: '1/0/3/6/1036002dc29de3e2d9a78de472482a1f77bc63a3.jpg',
    view: 'c/7/1/9/c719eb900ad799a7a21e812184121fcbf6c041d7.jpg'
  },
  fabyan: {
    thumb: 'f/d/a/6/fda6925d8d4c2eb8a7a996a77cc0cadb19825e1a.jpg',
    view: '4/8/3/e/483e9ebb2dc387dfd4fe6d6347bb152f798881e1.jpg'
  },
  jarrow: {
    thumb: '7/3/9/0/7390f0db3e4f347f506448be884ebf4f4d692e66.jpg',
    view: '0/f/3/0/0f3071b9a574800408db5163ff1aed734dc9dfa2.jpg'
  },
  killarney: {
    thumb: 'd/0/3/9/d039f4bc7829f464aeb45154de90cdfcc2da2aad.jpg',
    view: '2/5/c/7/25c72e6150b0cefaddd0600b78ced6b1e04c8859.jpg'
  },
  passchendale: {
    thumb: 'f/f/a/8/ffa814c3c869ecf673cb7aaf6a07e9480bc04631.jpg',
    view: 'e/7/e/6/e7e6e3aa1c17652b7895e7ca00db8a348bc7f4df.jpg'
  },
  roseberry: {
    thumb: '8/9/b/e/89be78b424f7e58d5af51fb5bd958cd0bd6e2f3f.jpg',
    view: '6/7/1/e/671e50470eaa571fe43cc052a0a78a662b729d56.jpg'
  },
  sligo: {
    thumb: '8/6/6/0/8660ff47f1abea83581391e33d37509bf2ff1113.jpg',
    view: '4/5/e/5/45e5cfb26a53a0cf2c427d4ba1d92a1d60877f9e.jpg'
  },
  staubins: {
    thumb: 'f/e/0/d/fe0d062198541169d9ce27795368c2701290c8a4.jpg',
    view: 'f/d/c/6/fdc6747437c6d6c12ce22921de445ee0a80b1721.jpg'
  }
};

const host = "https://codecademy-discourse.s3.amazonaws.com/original/5X/";

const main = () => {
  const $views = $('#views');
  const $thumbs = $('#thumbs');
  const $view = $('<img alt=""/>');
  
  $thumbs.children().each(function () {
    $(this).attr('src', host + views[$(this).attr('data')].thumb);
  });
  
  $view.attr({
      'src': host + views[$thumbs.children().eq(0).attr('data')].view,
      'title': $thumbs.children().eq(0).attr('title')
  });
  $views.append($view);
  
  const thumbViewer = function () {
    $view.attr({
        'src': host + views[$(this).attr('data')].view,
        'title': $(this).attr('title')
    });
  };
  $('#thumbs').on('click', 'img', thumbViewer);
};
$(document).ready(main);

I’m still digging around for something is native JS.

1 Like

Found this in the Chore Door project from a couple years ago.

Missing Task? Chore Door : Please Help!

It uses delegation.

ok- sorry for the delay. It took me a few days to pour over that thread!
I couldn’t get the jQuery thumbnail viewer to work but the files are copied to my computer now. I just pasted the code into new text editor files and saved them with the same names they have here. Let me know if there is a better way.

I attempted to use the code you arrived at at the end of the chore door thread as a jumping off point for my matching game project. It doesn’t work, but at least I could get some kind of logic onto the page! I’m calling that progress! :slight_smile:

I’ll just attach my code here so you can see the mess I’ve made…er, I mean, my progress. :slight_smile:

HTML

<!DOCTYPE html>

    <head>
        <meta charset="utf-8">
        <title>Memory Match Game</title>
        <meta name="description" content="memory game coding project">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link href="https://fonts.googleapis.com/css2?family=Open+Sans+Condensed:wght@300&display=swap" rel="stylesheet">
        <link rel="stylesheet" type="text/css"href="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\memory.css">
    </head>
    <body>
        <div class="title">Memory Matching Game</div>
        <div class="table-title">Instructions</div>
        <table class="instructions">
            <tr>
                
                <td class="instruction-text">Click on two cards to see if they match.</td>
            </tr>
            <tr>
                
                <td class="instruction-text">Find all of the matching pairs to win!</td>
            </tr>
        </table>

        <div id="cards">
            <img id="card0" class="card-back" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">

            <img id="card1" class="card" title="1" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card2" class="card" title="2" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card3" class="card" title="3" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card4" class="card" title="4" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card5" class="card" title="5" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card6" class="card" title="6" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card7" class="card" title="7" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card8" class="card" title="8" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card9" class="card" title="9" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card10" class="card" title="10" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card11" class="card" title="11" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card12" class="card" title="12" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card13" class="card" title="13" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card14" class="card" title="14" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card15" class="card" title="15" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card16" class="card" title="16" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card17" class="card" title="17" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card18" class="card" title="18" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card19" class="card" title="19" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card20" class="card" title="20" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card21" class="card" title="21" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card22" class="card" title="22" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card23" class="card" title="23" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            <img id="card24" class="card" title="0" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cardback.png">
            
            <img id="card-front1" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\sagitarius.png">
            <img id="card-front2" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\aquarius.png">
            <img id="card-front3" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\aries.png">
            <img id="card-front4" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\taurus.png">
            <img id="card-front5" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\capricorn.png">
            <img id="card-front6" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\pisces.png">
            <img id="card-front7" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\gemini.png">
            <img id="card-front8" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\cancer.png">
            <img id="card-front9" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\leo.png">
            <img id="card-front10" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\virgo.png">
            <img id="card-front11" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\scorpio.png">
            <img id="card-front12" class="card-front" src="C:\Users\nikki\Desktop\learning to code\projects\memory\resources\images\libra.png">
        </div>
        <div class="button-container">
          <div id="start">Good luck!</div>
        </div>
        
        <script src=".\resources\memory.js" async defer></script>
    </body>
</html>

CSS

body {
    background-color: #C4BFC1;
    font-family: 'Open Sans Condensed', sans-serif;
    padding-bottom: 20px;
    

}

.title {
    height: 65px;
    width: 400px;
    margin: 0 auto;
    font-size: 30px;
    text-align: center;
}
.table-title {
    height: 40px;
    width: 400px;
    margin: auto;
    text-align: center;
    font-size: 25px;
}
.instructions {
    height: 90px;
    width: 400px;
    margin: auto;
    text-align: center;
}
.instruction-text {
    font-size: 20px;
}

#cards {
    display: grid;
    grid-template-columns: 100px 100px 100px 100px 100px 100px;
    grid-template-rows: 100px 100px 100px 100px;
    padding-top: 15px;
    padding-left: 35px;
    gap: 25px 30px;
    background-color: #404E5A;
    height: 500px;
    width: 800px;
    margin: auto;
    
}
#card0 {
    display: none;
}
.card-front {
    display: none;
}
.button-container {
    position: relative;
    padding-top: 20px;
    margin-bottom: 20px;
}
#start {
   position: absolute;
   top: 50%;
   left: 42%;
   height: 50px;
   width: 200px;
   background-color: #704B59;
   margin: 0 auto;
   text-align: center;
   font-size: 20px;
}

JavaScript

var numCardsLeft, currentlyPlaying;
let targetHolder = [];
let cardsHolder = [];
const start = document.getElementById('start');
const cards = document.getElementById('cards');
const cardBack = document.getElementById('card-back');

const cardFront1 = document.getElementById('card-front1');
const cardFront2 = document.getElementById('card-front2');
const cardFront3 = document.getElementById('card-front3');
const cardFront4 = document.getElementById('card-front4');
const cardFront5 = document.getElementById('card-front5');
const cardFront6 = document.getElementById('card-front6');
const cardFront7 = document.getElementById('card-front7');
const cardFront8 = document.getElementById('card-front8');
const cardFront9 = document.getElementById('card-front9');
const cardFront10 = document.getElementById('card-front10');
const cardFront11 = document.getElementById('card-front11');
const cardFront12 = document.getElementById('card-front12');

const deck = [cardFront1, cardFront1, cardFront2, cardFront2, cardFront3, cardFront3, cardFront4, cardFront4, cardFront5, cardFront5, cardFront6, cardFront6, cardFront7, cardFront7, cardFront8, cardFront8, cardFront9, cardFront9, cardFront10, cardFront10, cardFront11, cardFront11, cardFront12, cardFront12];

let card1 = getElementById('card1');
let card2 = getElementById('card2');
let card3 = getElementById('card3');
let card4 = getElementById('card4');
let card5 = getElementById('card5');
let card6 = getElementById('card6');
let card7 = getElementById('card7');
let card8 = getElementById('card8');
let card9 = getElementById('card9');
let card10 = getElementById('card10');
let card11 = getElementById('card11');
let card12 = getElementById('card12');
let card13 = getElementById('card13');
let card14 = getElementById('card14');
let card15 = getElementById('card15');
let card16 = getElementById('card16');
let card17 = getElementById('card17');
let card18 = getElementById('card18');
let card19 = getElementById('card19');
let card20 = getElementById('card20');
let card21 = getElementById('card21');
let card22 = getElementById('card22');
let card23 = getElementById('card23');
let card24 = getElementById('card24');

cardContainers = [card1, card2, card3, card4, card5, card6, card7, card8, card9, card10, card11, card12, card13, card14, card15, card16, card17, card18, card19, card20, card21, card22, card23, card24];



const isNotClicked = card => card.src === cardBack.src;


const gameProgress = () => {
    numCardsLeft--;
    if (numCardsLeft === 0) {return gameOver('win');}
}

const gameOver = (str) => {
    start.textContent = `${str === `win` ? `You win! Play again?` : `Find a match!`}`; 
    currentlyPlaying = false;
}


const shuffleArray = (array) => {
    for (let i = array.length-1; i>0; i--) {
        let j = Math.floor(Math.random()*i);
        let temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

function startGame() {
    cardContainers.forEach(x =>cards.children[x].src = cardBack.src);
    start.textContent = 'Focus!';
    numCardsLeft = 24;
    currentlyPlaying = true;
    shuffledDeck = shuffleArray(deck);
}

function getEventTarget(e) { //credit sitepoint.com
    e = e || window.event;
    return e.target || e.srcElemet;
}

function handler (e) {
    gameProgress();
    let target = getEventTarget(e);
    targetHolder.push(target);
    if (isNotClicked(target) && currentlyPlaying) {
        target.src = shuffledDeck[+target.title];
        cardsHolder.push(target.src);
    if (cardsHolder.length<2 && targetHolder.length<2) {
        return
    }
    else if (cardsHolder.length === 2 && cardsHolder[0] === cardsHolder[1]) {  
        targetHolder[0].style.backgroundImage = 'URL("cardsHolder[0]") !important';
        targetHolder[1].style.backgroundImage = 'URL("cardsHolder[1]") !important';

    }
    else {
        targetHolder[0].style.backgroundImage = 'cardBack.src';
        targetHolder[1].style.backgroundImage = 'cardBack.src';
        numCardsLeft += 2;
    }
    targetHolder.pop();
    targetHolder.pop();
    cardsHolder.pop();
    cardsHolder.pop();
    }
}

start.onclick = () => currentlyPlaying ? false: startGame();
cards.addEventListener('click', handler);


startGame();

I need to add something to my handler function that allows the 2 flipped cards to display for a fixed amount of time (like 3 seconds) before automatically flipping back to the cardBack image OR I need to include code that allows them to be flipped back to the cardBack image by click. I’m sure that is the least of my problems at this point though, lol! Just wanted to get it in the thread before I forget…