Matching game setting 'innerHTML' of null

Question:
cards are not showing up

Repl link:
https://replit.com/@coding-for-web-summer-23/Portfolio-Project-Match-Game-bcespedes

code snippet

https://replit.com/@coding-for-web-summer-23/Portfolio-Project-Match-Game-bcespedes

Hey @bcespedes, welcome to the community!

This looks like a Teams for EDU project. Therefore, we cannot see your code and can’t help you unless you send us your code.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Matching Game</title>
    <link href="style.css" rel="stylesheet" type="text/css" />
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Kanit:wght@300&display=swap" rel="stylesheet">
  </head>
  <body>
    <header>
      <h1>Hiking Matching Game</h1>
    </header>
    <section id="game-container">

			<!-- This is where we display the flip and match counters -->
      <div id="ui-container">
        <h3>Flips: <span id="flip-count">0</span></h3>
        <h3>Matches: <span id="match-count">0</span></h3>
      </div>
			
			<!-- This is where our cards will be rendered -->
      <div id="card-container">  
      </div>
      
      
    </section>

		<div class="btn-container">
			<button class="reset-btn" onclick="resetGame()">Reset</button>
		</div>
		
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.1/underscore-min.js"></script>
    <script src="provided.js"></script>
    <script src="script.js"></script>
  </body>
</html>
function createNewCard() {
	/* Step 1: Create a new div element and assign it to a variable called cardElement. */
	

	/* Step 2: Add the "card" class to the variable 'cardElement' from the previous step. */
	

	/* Step 3: Write the HTML for the children of the card element (card-down and card-up) as a normal string and assign it as the innerHTML of cardElement. */
	

  /* Step 4: Return the cardElement. */
	const cardElement = document.createElement("div");

  cardElement.classList.add("card");

  cardElement.innerHTML = `
  <div class="card-down"></div>
  <div class="card-up"> 
  <img src="img/card.png" alt="card front" />
  </div>
  `;
  return cardElement;
}
function createNewCardTest() {
  const cardelement = createNewCard();
  console.log(cardelement)
}


function appendNewCard(parentElement) {
	/* Step 1: Create a new card by calling createNewCard() and assign it to a variable named cardElement. */
	const cardElement = createNewCard();
  
	/* Step 2: Append the card element to the parentElement (making the card element a "child").  */
	parentElement.appendChild(cardElement);

  /* Step 3: Return the card element. */
	return cardElement;

}
// appendNewCardTest();
function appendNewCardTest() {
  const parentElement = document.getElementById("card-container");
  const cardElement = appendNewCard(parentElement);
  console.log(cardElement);
}
appendNewCardTest();

function shuffleCardImageClasses() {
  /* Step 1: Create a new array that contains two of each image class string in order (e.g. "image-1", "image-1", "image-2", "image-2"...). Store the array in a variable called 'cardClasses'.  */
	const cardClasses = [
    "image-1.png", "image-1.png",
    "image-2.png", "image-2.png",
    "image-3.png", "image-3.png",
    "image-4.png", "image-4.png",
    "image-5.png", "image-5.png",
    "image-6.png", "image-6.png",
  ];

	/* Step 2: We're going to use a library to randomly "shuffle" the array we created. The library is called "underscore.js" because it uses an "_" character as an object to contain helper methods. Load underscore.js in your HTML via the CDN then open up the documentation linked below to learn how to use the 'shuffle' method.  
         
  CDN: https://cdnjs.com/libraries/underscore.js/1.4.1
  Shuffle: https://www.tutorialspoint.com/underscorejs/underscorejs_shuffle.htm 
 
  NOTE: Ignore the "require" syntax shown in the documentation as this is for non-browser environments. The '_' variable will already be available to you from loading the CDN. */
   const shuffledClasses = _.shuffle(cardClasses);
  
	/* Step 3: Return the shuffled array of class names. */
return shuffledClasses;
}
// shuffleCardImageClassesTest()
function shuffleCardImageClassesTest() {
  const shuffledClasses = shuffleCardImageClasses();
  console.log(shuffledClasses);
}
shuffleCardImageClassesTest();

function createCards(parentElement, shuffledImageClasses) {
	/* Step 1: Make an empty array to hold our card objects. */
  const cardObjects = [];

  /* Step 2: Write a for loop that loops 12 times to create the 12 cards we need. */
    for (let i = 0; i < 12; i++) {
      const newCard = appendNewCard(parentElement);

      newCard.classList.add(shuffledImageClasses[i]);

      const cardObject = {
        index: i,
        element: newCard,
        imageClass: shuffledImageClasses[i],
      };

      cardObjects.push(cardObject);
    }
    /* Step 2(a): Use appendNewCard to create/append a new card and store the result in a variable. */
    
		/* Step 2(b): Add an image class to the new card element using shuffledImageClasses[i]. */
    
    /* Step 2(c): Append a new object to the card object array. The object should contain the following properties:
			"index" -- Which iteration of the loop this is.
			"element" -- The DOM element for the card.
			"imageClass" -- The string of the image class on the card. */	
	

  /* Step 3: Return the array of 12 card objects. */
return cardObjects;
	
}
// createCardsTest();
function createCardsTest() {
  const parentElement = document.getElementById("card-container");
  const shuffledImageClasses = shuffleCardImageClasses();
  const cardObjects = createCards(parentElement, shuffledImageClasses);
  console.log(cardObjects);
}
createCardsTest();

function doCardsMatch(cardObject1, cardObject2) {
	/* Step 1: Determine if two cards match. Remember the properties of our card objects from the createCards() function: index, element, and imageClass. */
return cardObject1.imageClass === cardObject2.imageClass;
	
}
// doCardsMatchTest();
function doCardsMatchTest() {
  const cardObject1 = {
    imageClass: "img/image-1.png",
  };
  const cardObject2 = {
    imageClass: "img/image-1.png",
  };
  const match = doCardsMatch(cardObject1, cardObject2);
  console.log(match); // Should print true
}
doCardsMatchTest();

/* The 'counters' object below is used as a dictionary to store our counter names and their respective values. Do you remember using objects as dictionaries? If not, go back to that video lesson in HQ to review. This object is empty for now but we'll fill it up in the following function. */
let counters = {};

let lastCardFlipped = null;

function incrementCounter(counterName, parentElement) {
  /* Step 1: If the 'counterName' property is not defined in the 'counters' object, initialize it with a value of 0. */
	if (counters[counterName] === undefined) {
    counters[counterName] = 0;
  }
  /* Step 2: Increment the counter for 'counterName'. */
	counters[counterName]++;

  /* Step 3: Change the HTML within 'parentElement' to display the new counter value. */
	parentElement.innerHTML = counters[counterName];

}
// incrementCounterTest();
function incrementCounterTest() {
  const parentElement = document.getElementById("flip-counter");
  incrementCounter("flips", parentElement);
}
  incrementCounterTest();
/* The 'onCardFlipped' function below will be called each time the user flips a card. The 'lastCardFlipped' variable is used to remember the first card flipped while we wait for the user to flip another card. We need to keep track of this value to determine if the two cards flipped match or not. 'lastCardFlipped' should be reset to 'null' each time a second card is flipped. */



function onCardFlipped(newlyFlippedCard) {
  /* Step 1: Use the 'incrementCounter' function to add one to the flip counter UI.  */
	incrementCounter("flips", document.getElementById("flip-counter"));

	/* Step 2: If 'lastCardFlipped' is null (this is the first card flipped), update 'lastCardFlipped' and return (nothing else to do) */
	if (lastCardFlipped === null) {
    lastCardFlipped = newlyFlippedCard;
    return;
  }

  /* If the above condition was not met, we know there are two cards flipped that should be stored in 'lastCardFlipped' and 'newlyFlippedCard', respectively. */
  

  /* Step 3: If the cards don't match, remove the "flipped" class from each, reset 'lastCardFlipped' to null, and use a 'return' to exit the function. Remember that newlyFlippedCard and lastCardFlipped are both objects made with the createCards function. This means that, to access each card's classList, you must access the card object's .element property first!  */
	if (!doCardsMatch(lastCardFlipped, newlyFlippedCard)) {
    lastCardFlipped.element.classList.remove("flipped");
    newlyFlippedCard.element.classList.remove("flipped");
    lastCardFlipped = null;
    return;
  }
  	
  /* Step 4: Now we have two matching cards. Increment the match counter and optionally add a "glow" effect to the matching cards. */
	incrementCounter("matches", document.getElementById("match-counter"));
  lastCardFlipped.element.classList.add("glow");
  newlyFlippedCard.element.classList.add("glow");
  /* Step 5: Play either the win audio or match audio based on whether the user has the number of matches needed to win. Both sounds have been loaded in provided.js as matchAudio and winAudio, respectively. */
	if (counters.matches === NUM_PAIRS_TO_WIN) {
    winAudio.play();
    } else {
    matchAudio.play();
    }

  /* Step 6: Reset 'lastCardFlipped' to null */
lastCardFlipped = null;

}

/* This function should remove all children from the #card-container, reset the flip and match counts displayed in the HTML, reset the counters dictionary to an empty object, reset lastCardFlipped to null, and set up a new game. */
function resetGame() {
	/* Step 1: Get the card container by its id and store it in a variable. */
const cardContainer = document.getElementById("card-container");
	
	/* Step 2: Clear all the cards by using a while loop to remove the first child of the card container as long as a first child exists.
	See "To remove all children from an element:" in the Examples section of the MDN removeChild documentation -> https://mzl.la/3bklFxP */
while (cardContainer.firstChild) {
  cardContainer.removeChild(cardContainer.firstChild);
}
	
	/* Step 3: Get the HTML elements that display the flip and match counts and reset their inner text to 0. */ 
const flipcounterelement = document.getElementById("flip-counter");
const matchcounterelement = document.getElementById("match-counter");
  flipcounterelement.textContent = "0";
  matchcounterelement.textContent = "0";
  /* Step 4: Reassign the value of the `counters` dictionary to an empty object  */
counters = {};
	
	/* Step 5: Set lastCardFlipped back to null. */
lastCardFlipped = null;
	
	/* Step 6: Set up a new game. */
setUpGame();

}

// ⛔️ Set up the game. Do not edit below this line! ⛔
setUpGame();

I’m new so please lmk if you need anything else I’m getting this error in the console

script.js:151 Uncaught TypeError: Cannot set properties of null (setting 'innerHTML')
    at incrementCounter (script.js:151:26)
    at incrementCounterTest (script.js:157:3)
    at script.js:159:3

@QwertyQwerty88 im getting this error in the console
script.js:151 Uncaught TypeError: Cannot set properties of null (setting ‘innerHTML’)
at incrementCounter (script.js:151:26)
at incrementCounterTest (script.js:157:3)
at script.js:159:3

to ease finding the cause of the error, open the website in a new tab and do CtrlJ and click on the link provided next to the error message shown. Then you can use the debugger.
I did that, then checked “pause on uncaught exceptions” and refreshed the page. Then, the exception occurred and I selected incrementCounterTest in the “Call Stack”, because it is parentElement that is null, and I want to find out what parentElement parameter had been passed in.
There, I found my answer: parentElement, which was set to a non-existent element, #flip-counter. You probably meant #flip-count.

3 Likes