Using Vanilla Javascript to implement a dark mode toggle button

I've been struggling with getting a button to properly switch a website to dark mode. I already have a night mode that works based on the user's location and time, so I know the issue lies within my JavaScript...

Initially, I tried adding 'lightMode' first and then removing 'darkMode', but that didn't solve the problem...

      <button
              type="button"
              class="btn btn-outline-secondary"
              id="buttonChangeMode"
            >
              <i class="far fa-moon"></i>
            </button>
.lightMode .app {
  border: 1px solid #0b0b35;
  padding: 20px 15px;
  max-width: 1000px;
  max-height: 2500px;
  margin: 30px auto;
  border-radius: 10px;
  background-color: white;
}
.darkMode .app {
  border: 1px solid #0b0b35;
  padding: 20px 15px;
  max-width: 1000px;
  max-height: 2500px;
  margin: 30px auto;
  border-radius: 10px;
  background-color: rgb(7, 8, 14);
}
function changeMode() {
  let mode = document.getElementById("body");
  if (mode.classList.contains("lightMode")) {
    mode.classList.add("darkMode").remove("lightMode");
  } else if (mode.classList.contains("lightMode")) {
    mode.classList.remove("darkMode").add("lightMode");
  }
}

let buttonChangeMode = document.querySelector("#buttonChangeMode");
buttonChangeMode.addEventListener("click", changeMode);

An error message is showing in the console: "Uncaught TypeError: Cannot read property 'remove' of undefined at HTMLButtonElement.changeMode"

Answer №1

.toggle() method of .classList is the simplest approach to achieve this functionality. It is advisable to use classes over IDs whenever possible for better code management. Further details are explained in the provided demo.

// Function to handle mode change
function switchMode(event) {
  // Event.target always refers to the element that triggered the event
  const clickedElement = event.target;
  
  // Selecting all elements with the 'all' class
  const allElements = document.querySelector('.all');

  // If the clicked element has the .mode class...
  if (clickedElement.matches('.mode')) {
    // Toggle classes on the button.mode element
    clickedElement.classList.toggle('fa-moon');
    clickedElement.classList.toggle('btn-outline-dark');
    clickedElement.classList.toggle('fa-sun');
    clickedElement.classList.toggle('btn-outline-light');
    
    // Toggle darkMode and lightMode classes on body.all
    allElements.classList.toggle('darkMode');
    allElements.classList.toggle('lightMode');
  }
  return false;
}

// Reference to the button.mode element
const modeButton = document.querySelector(".mode");

// Add a click event listener to the button.mode element
modeButton.addEventListener("click", switchMode);
.lightMode .app {
  border: 1px solid #0b0b35;
  padding: 20px 15px;
  max-width: 1000px;
  max-height: 2500px;
  margin: 30px auto;
  border-radius: 10px;
  background-color: white;
}

.darkMode .app {
  border: 1px solid #0b0b35;
  padding: 20px 15px;
  max-width: 1000px;
  max-height: 2500px;
  margin: 30px auto;
  border-radius: 10px;
  background-color: rgb(7, 8, 14);
}
<!DOCTYPE html>
<html>

<head>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
  <link href="https://use.fontawesome.com/releases/v5.9.0/css/all.css" rel="stylesheet" crossorigin="anonymous">
</head>

<body class='all lightMode'>
  <main class='app container'>
    <section class='row'>
      <article class='col-6'>
        <button class="mode btn btn-outline-dark far fa-sun" type="button"></button>
      </article>
      <figure class='col-6'>
        <img src='https://branditprintit.com/wp-content/uploads/2017/04/logo-degin-logo-design-design-art-free.png' style='width:80%'>
      </figure>
    </section>
  </main>
</body>

</html>

Answer №2

It seems like you forgot to include an HTML snippet - Without the <body> tag having an ID like this:

<html>
...
    <body id="body">
...

You may encounter an error because when using

let mode = document.getElementById("body");
, the result could be undefined since the getElementById() function requires a valid DOM element ID value as its argument.

For more information, visit https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById

Answer №3

To toggle between light and dark mode, you can add or remove classes like this:

<script>
function toggleMode() {
  let body = document.getElementsByTagName("body")[0]; // select the body tag
  if (body.classList.contains("lightMode")) {
    body.classList.add("darkMode");
    body.classList.remove("lightMode");
  } 
  else {
    body.classList.add("lightMode");
    body.classList.remove("darkMode");
  }
}

let buttonToggleMode = document.querySelector("#buttonToggleMode");
buttonToggleMode.addEventListener("click", toggleMode);
</script>

For more information on Element.classList, refer here

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

Generate fake data for all possible combinations using JSON faker

In my current project, I am in need of creating test data for a JSON schema. I came across this fantastic github resource that has been incredibly helpful: https://www.npmjs.com/package/json-schema-faker#overview Now, how can we expand it to generate all ...

Upon clicking a table row, generate a div underneath containing mapped data

My dynamic table is currently being filled with rows based on an array, but I want to display more data in an expanded view when a button in a row is clicked. However, I'm facing challenges as it seems I can't have two table rows within the same ...

Discovering ways to access deeply nested JSON data in Vue JS

i am dealing with JSON data that includes payment information. I am facing issues retrieving the paid_amount and date for both cash_payment and installment_payment. { "response": [ { "status": "sold", "price": "1000 ...

"Setting the minimum length for multiple auto-complete suggestions in

How can I dynamically set the minLength of an input field based on whether a numeric value is coming from the "#lookup" field? Is there a way to achieve this using JavaScript and jQuery? <script type="text/javascript"> $(document).ready(function() ...

Storing a single JavaScript array in separate arrays depending on a specific condition

I have a JavaScript array variable where each element is an object with values like: {"ID":10075,"SPECIALIZATIONID":"17","PRESPCIALIZATIONID":11,"GROUPID":1} The entire JSON.stringify value looks like this: "[{"ID":10075,"SPECIALIZATIONID":"17","PRESP ...

What is the process for customizing the color and thickness of the active tab inkBarStyle in React Material-UI?

Check out this inquiry: How can I change the color of the active tab in MUI? And for a potential solution, have a look at this response: The provided answer seems to be effective: <Tabs inkBarStyle={{background: 'blue'}}>... However, I a ...

Error: Unable to return callback response from NodeJs function

I have a function that handles the process of reading a private key from the filesystem and generating a JWT token. The code successfully reads the file and creates a token, but it encounters an issue when attempting to callback to the calling function. De ...

"Utilizing AngularJS to asynchronously send an HTTP POST request and dynamically update

I've been working on an angularjs chat module and have come across a challenge. I developed an algorithm that handles creating new chats, with the following steps: Click on the 'New Chat' button A list of available people to chat with wil ...

Angular 2 - Changes in component properties not reflected in view

I'm currently delving into Angular 2 and as far as I know, interpolated items in the view are supposed to automatically update when their corresponding variable changes in the model. However, in the following code snippet, I'm not observing this ...

Submitting Multi-part forms using JQuery/Ajax and Spring Rest API

Recently, I started exploring JQuery and decided to experiment with asynchronous multipart form uploading. The form includes various data fields along with a file type. On the server side (using Spring), I have set up the code as follows: @RequestMapping ...

What are the best practices for utilizing databases with both Javascript and JSF frameworks?

I am currently following the recommendations provided by @BalusC, with additional guidance available here. (The reason I'm posting this here is because it's not related to my previous question). My goal is to retrieve data from my database and d ...

Execute the function within setInterval only one time

I have a setInterval function that calculates the time difference between a specified date and the current time. Once this difference is less than an hour, I want to execute some code only once. const countdownDate = new Date('March 15, 2021 11:30:00& ...

Launching the forEach function within an ng-repeat loop was it can be done by

I need to implement a function within the ng-repeat that will convert the value of Qprogress object in my JSON into a percentage. I already have the function written, but I am struggling with how to trigger it. I attempted to use a forEach loop inside the ...

Calculate the total price from a combination of HTML and JavaScript options without altering the values

I'm currently facing an issue in my form where the final price needs to be updated when a different option is selected. I've searched extensively for the problem without success. I've used similar code before, just with different variables a ...

I'm encountering a strange issue where Node JS is mistakenly claiming that the method doesn't exist, even though

Ah, it seems I've encountered an error in my test project. The issue lies with Node JS not being able to locate the getStr function within the Another object. Allow me to share the code with you: test.js var Another = require('./another.js&apo ...

JQuery Submission with Multiple Forms

Hey everyone! I have a jQuery form with multiple fieldsets that switch between each other using jQuery. Eventually, it leads to a submit button. Can someone assist me by editing my jfiddle or providing code on how I can submit this data using JavaScript, j ...

Exploring the utilization of type (specifically typescript type) within the @ApiProperty type in Swagger

Currently, I am grappling with a dilemma. In my API documentation, I need to include a 'type' in an @ApiProperty for Swagger. Unfortunately, Swagger seems to be rejecting it and no matter how many websites I scour for solutions, I come up empty-h ...

Issue encountered while attempting to set up react-native project

Whenever I attempt to create a new react-native project using the command: npx react-native init myproject I encounter the following errors: ERESOLVE is overriding peer dependency npm gives me a warning while trying to resolve: [email protected] ...

Saving Information from an HTML Form Input?

I am currently working on a form that collects information about employees including their name, age, position, and details. When the "Add Record" button is pressed, this information should be added to a designated div. Although I have set up the Object C ...

Only one instance of the Next.js inline script is loaded

Incorporating Tiny Slider into my Next.js application has been a success, but I am facing an issue with the inline script that controls the slider's behavior. The script loads correctly when I first launch index.js. However, if I navigate to another p ...