Changing the background color of an answer box in React when the user clicks on it

I am currently developing a Quiz application that includes a question and four answer options. The goal is to modify the background color of the selected option when a user clicks on it. Essentially, when a user clicks on an answer, it should change color, and if another option is chosen within the same question, the previously selected option's color should revert to its original state.

However, I'm encountering an issue where all questions and options are affected by a single selection. Clicking on an option under one question causes changes in options from other questions as well. Additionally, the options within each question seem to switch positions every time an option is clicked.

If anyone has any insights or suggestions on what might be causing this problem, please let me know!

Code Sandbox

Below is the code snippet for the Quiz component:

import { useEffect, useState } from "react";

export default function Quiz() {

  // Relevant code here...

}

Answer №1

To change the color of a selected option using CSS, you can use option:checked { color: red; }. Additionally, you can define specific styles for the selected option by utilizing the :checked pseudo-class.

Answer №2

Finally, the solution came to me. The issue stemmed from using isClickedIndex as the state for monitoring all questions' options, causing any change in one option to affect the others. By moving the callbacks in the variable quizElements within the useEffect, I can now set the values once per render without needing to call them repeatedly like before. This adjustment prevents the options from swapping positions when clicked.

Updated and complete code:

import { useEffect, useState } from "react"

export default function Quiz() {

  const [quiz, setQuiz] = useState(null);
  const [playAgain, setPlayAgain] = useState(false);
  const [togglePlayAgain, setTogglePlayAgain] = useState(playAgain);

  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);
  const [userAnswer, setUserAnswer] = useState(() => []);
  const [showAnswer, setShowAnswer] = useState(false);
  const [showAnswerBtn, setShowAnswerBtn] = useState(true);
   
  // Fetches data only once
  useEffect(() => {
    fetch("https://opentdb.com/api.php?amount=10&category=18&difficulty=hard&type=multiple")
      .then(result => {
        if (!result.ok) {
          throw new Error("HTTP error occurred", result.status);
        }
        else {
          return result.json();
        }
      })
      .then(data => {
        const modifiedQuiz = data.results.map(eachQuiz => {
          const incorrectOptions = eachQuiz.incorrect_answers;
          const correctOption = eachQuiz.correct_answer;
          const options = incorrectOptions.concat(correctOption);
          const randomOptions = createRandomOptions(options);
          return {
            ...eachQuiz,
            options: randomOptions,
            correctOption: correctOption,
            clickedOptionIndex: -1,
          };
        });
        setQuiz(modifiedQuiz);
      })
      .catch(error => {
        console.error("Error occurred!", error);
        setQuiz(null);
        setError(true);
      })
      .finally(() => {
        setLoading(false);
      });
  }, [togglePlayAgain])

  function createRandomOptions(arr) {
      
    let copyOptions = [...arr];
    let randomOptionsArr = [];

    while (copyOptions.length > 0) {
      let randomIndex = Math.floor(Math.random() * copyOptions.length);
      randomOptionsArr.push(copyOptions[randomIndex]);
      copyOptions.splice(randomIndex, 1); 
    }

    return randomOptionsArr;
  }

  function handleClick(option, correctAnswer, position, questionIndex) {
    checkEachAnswer(option, correctAnswer, position);
    
    const updatedQuiz = quiz.map((eachQuiz, index) =>  
      index === questionIndex 
      ? {...eachQuiz, clickedOptionIndex: position} 
      : eachQuiz);
    setQuiz(updatedQuiz);
  }

  function checkEachAnswer(option, correctAnswer, optIndex) {
    if (option === correctAnswer) {
      if (userAnswer.includes(option)) {
        let userAnsArrCopy = [...userAnswer];
        let index = userAnsArrCopy.findIndex(elem => elem);
        userAnsArrCopy[index] = option;
        
        setUserAnswer(prevValue => {
          return userAnsArrCopy;
        }); 
      }

      else {
        setUserAnswer(prevValue => {
          return [...prevValue, option];
        });
      }
    }
  }

  const quizElements = quiz && quiz.map((eachQuiz, questionIndex) => {
    const {question, options, correctOption, clickedOptionIndex} = eachQuiz;

    return (
      <>
        <div className="quiz-wrapper">
          <p className="question">{question}</p>
          <ul>
            {quiz && options.map((option, index) => 
              {
                return (
                  <li 
                    className={
                      `option 
                      ${clickedOptionIndex === index 
                        ? "active" : null } 
                      ${showAnswer && option === correctOption ? "correct" : ""}
                      ${showAnswer && option !== correctOption ? "wrong" : ""}`
                    }
                    key={index}
                    onClick={() => 
                      handleClick(option, correctOption, index, questionIndex)}
                  >
                    {option}
                  </li>
                )
              })
            }
          </ul>
        </div>
        <div className="divider"></div>
      </>
    )
  });

  function displayAnswer() {
    setShowAnswer(true);
    setPlayAgain(true);
    setShowAnswerBtn(false);
  }

  function updatePlayAgain() {
    setTogglePlayAgain(!togglePlayAgain);
    setPlayAgain(false);
    setShowAnswer(false);
    setShowAnswerBtn(true);
    setUserAnswer([]);
  }

  return (
    <>
      {loading && <h3>Loading...</h3>}
      {error && <h3>An error occurred during data retrieval</h3>}
      {quiz && <h1 className="topic">Topic: Computer Science</h1>}
      
      {quiz && quizElements}

      {showAnswer && <p>You scored {userAnswer.length} / {quiz.length}</p>}

      {quiz && showAnswerBtn && 
        <button 
          onClick={() => displayAnswer()}
          className="main-btn"
        >
          Check Answer
        </button>
      }

      {quiz && playAgain && 
        <button 
          onClick={() => updatePlayAgain()}
          className="main-btn"
        >
          Play Again
        </button>
      }
    </>
  )
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Docusaurus font loading specifically optimized for body text, excluding headings

I've added the following CSS code to my Docusaurus custom stylesheet: @import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap"); :root { --ifm-color- ...

Placing an icon directly beside two paragraphs, ensuring it is vertically centered and positioned to the right of the text

I am looking to add an icon next to two paragraphs that will be positioned exactly in the center (vertically) on the left side of the paragraphs. Here is the desired outcome: https://i.sstatic.net/RmjOR.png Currently, here is what I have: https://i.ssta ...

What is the best way to design a polygon-shaped responsive div?

I've been attempting to design a unique layout with a pentagon-shaped div, however, I'm encountering some challenges. Despite using SVG for the shape, it does not display correctly in Firefox. Additionally, I need the div to be responsive and sca ...

Animation of lava effect in Angular 7

I have a unique Angular 7 application featuring a homepage with a prominent colored block at the top, along with a header and various images. My goal is to incorporate lava effect animations into the background similar to this CodePen example. If the link ...

Can anyone guide me on creating a Mapbox map layer using React.js?

I'm currently in the process of creating a polygon layer on top of my Mapbox map using ReactMapboxGL. I have some Mapbox Javascript code that I need to convert into React.js format: map.on('load', function () { map.addLayer({ ...

Developed a visually stunning image gallery using both Bootstrap Grid and Flexbox, though there are a few images that do not completely fill the flex container

I'm currently exploring web development and experimenting with creating a responsive image gallery using Bootstrap Grid and flexbox. However, I've encountered an issue where some of the images are not filling the entire height of the flex contain ...

Unlocking the style within a .css file during an Angular unit test - here's how to do

I have been working on unit testing for an Angular app, specifically trying to access styles from a .css file within the unit test. I will share with you what I have attempted so far. component.listedIps.length=0; fixture.detectChanges(); let whitelis ...

Issue with Angular: Mobile view toggle button in the navbar is unresponsive

While the dropdowns are functioning correctly in web mode, the navbar toggle isn't working as expected in small screens or mobile mode. I've been trying to figure out the issue by referring to code from CodePen that I am learning from. Despite i ...

A guide on accessing header response information in Vue.js

Currently, I am operating on my localhost and have submitted a form to a remote URL. The process unfolds in the following sequence: Submission of a form from localhost Being redirected to a remote URL Sending a response back from the Remote URL to localh ...

Unable to assign a boolean value to a data attribute using jQuery

I have a button on my page that has a special data attribute associated with it: <button id="manageEditContract" type="button" class="btn btn-default" data-is-allow-to-edit="@(Model.Contract.IsAllowToEdit)"> @(Model.Contract.IsAllowToEdit ? " ...

Access the angular controller using the radio button option

I am looking to showcase data in an HTML table retrieved from an Angular controller. I have the controller set up but I am unsure about how to display the data when a radio button is selected. <div class="radio"> <label class="radio-inline" ...

Restrict the input area of a text field

Apologies for the basic question, but I couldn't find an answer online. I have a textarea that I want to be resizable, but with specific dimensions when the page loads. The initial size should be the minimum size of the textarea. Additionally, I woul ...

Updating the color of HTML button text dynamically using JavaScript function

I am attempting to create a function that will change the text color of a button in a sequence - red on the first click, green on the second click, and back to black on the third click (and so forth). I thought this would be straightforward to achieve with ...

Adding attributes dynamically to input elements in a React render function

I currently have an input tag inside my render function which looks like this: <input id="time" type="time"> I am now looking to dynamically add the value attribute to it. Is there a way to achieve this in React? Thank you for your help in advance ...

Script fails to load upon accessing page through index hyperlink

I am facing an issue with my JavaScript elements not loading when I navigate to a form page from a link in the index. However, if I enter the URL manually or redirect from the login page, the JavaScript loads perfectly fine. The JavaScript code is consolid ...

`Is it possible to animate the rotation of an image within an input control?`

After coming across this helpful answer, I successfully added a rotating GIF image as an icon inside an INPUT control on its right side. Simply applying the "busy" class to my control did the trick, and now it looks like it's in progress. input.busy { ...

Node JS server terminated due to invalid arguments: string and undefined

Currently, I am in the process of developing a logging application using node JS. Unfortunately, there seems to be an issue with the password authentication functionality within the app. Every time I enter a username and password, it triggers an error that ...

Is there a way to prevent Chrome from automatically opening whenever I save a file?

I have a question regarding using vscode with an app created by create-react-app. Whenever I save a file while the webpack server is running, Chrome automatically opens. I've searched online but couldn't find any solution to this issue. ...

Encountering a 503 application error code while trying to load the Angular

I am new to deploying my application in Heroku for the first time. Although my deployment was successful, I encountered an error. https://i.sstatic.net/EDB66.png Upon running heroku logs --tail, This is the error message that I am seeing Despite tryin ...

Unable to retrieve valid inputs from text fields by utilizing jQuery selectors

Extracting information from text fields is my goal. Specifically, these fields contain dates which consist of three parts: the year, month, and day. The dates are divided into two sections - the first date and the last date, serving as boundaries for a sea ...