Search code examples
javascripthtmlcssdomdom-manipulation

HTML, CSS and normal JS Code is not giving correct output


Making a sliding button to switch website theme using a CSS variable and javascript. It is working properly except there is a small bug that I am unable to fix. If I reload the page is light theme, the functionality of the button is being reversed. The "On" state of the button turns on light mode and off state toggles dark mode. However, the initial configuration is entirely opposite.

As you can see in the executable code snippet below, I tried solving this using click() function. This problem arises only when the value of num is 0 and page is reloaded. So, I thought if I store a variable in localStorage as false and check its value at the beginning of the function and if its false, then click the button and dont execute function, if its not false, then execute normally.

But it is not working for some reason. Please check this code:

if (!localStorage.getItem('thisvarisgud4me')) {
    localStorage.setItem("thisvarisgud4me", "1")
}

document.getElementById("btn").addEventListener("click", change);
var c = "true";
if (!localStorage.getItem("clickc"))
{
    localStorage.setItem("clickc", c);
}

function change() {
    if (localStorage.getItem("clickc") == "false") {
        localStorage.setItem("clickc","true");
        document.getElementById("btn").click();
    }
    else if (localStorage.getItem("clickc") == "true") {
        if (localStorage.getItem('thisvarisgud4me') == '1') {
            localStorage.setItem("thisvarisgud4me", '0')
        } else {
            localStorage.setItem("thisvarisgud4me", '1')
        }

        var num = Number(localStorage.getItem('thisvarisgud4me'));
        let root = document.documentElement;
        root.style.setProperty("--numvar", num);
        console.log(num);
        if (num == 0) {
            window.addEventListener("beforeunload", function (event) {
                console.log("The page is redirecting")
                alert("Reload");
                localStorage.setItem("clickc", "false");
                // document.getElementById("btn").click();
                // debugger;
            });
        }
    }
}
var num = Number(localStorage.getItem('thisvarisgud4me'));
let root = document.documentElement;
root.style.setProperty("--numvar", num);
:root {
    --numvar: 0;
}

html {
    filter: invert(var(--numvar));
}


body {
    background: #fff;
}

.outer-button {
    display: inline-block;
    height: 28px;
    width: 28px;
    position: relative;
    margin: 0 3px;
}

.inner-button {
    display: inline-block;
    position: absolute;
    width: 100%;
    height: 100%;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), inset 0px 0px 1px 2px white;
    border-radius: 20px;
    background: #f5f5f5;
}

span {
    display: inline-block;
    vertical-align: middle;
}

.status-text {
    color: white;
    text-transform: uppercase;
    font-size: 11px;
    font-family: Futura, sans-serif;
    transition: all 0.2s ease;
}

.sliding-switch {
    height: 28px;
    width: 72px;
    position: relative;
}

.outer-switch-box {
    overflow: hidden;

    height: 100%;
    width: 100%;
    display: inline-block;
    border-radius: 20px;
    position: relative;
    box-shadow: inset 0 1px 3px 0px #818181, 0px 1px 2px 1px white;
    transition: all 0.3s ease;
    transition-delay: 65ms;
    position: absolute;
    z-index: 1;
}

.inner-switch-box {
    position: relative;
    width: 175px;
    transition: all 0.3s ease;
}

/* .switch-checkbox:checked+.outer-switch-box .unchecked-text {
    color: transparent;
}

.switch-checkbox:not(:checked)+.outer-switch-box .checked-text {
    color: transparent;
} */

.switch-checkbox:checked+.outer-switch-box .inner-switch-box {
    left: -27px;
    /*OFF*/
}

.switch-checkbox:not(:checked)+.outer-switch-box .inner-switch-box {
    left: 20px;
    /*ON*/
}

.switch-checkbox:checked+.outer-switch-box {
    /* background-image: linear-gradient(#b6d284, #b6d284); */
    background: #492d7b;
    /* background: #b6d284; */
}

.switch-checkbox:not(:checked)+.outer-switch-box {
    /* background-image: linear-gradient(#cbcbcb, #dbdbdb); */
    background: #dbdbdb;
}

[type="checkbox"] {
    margin: 0;
    padding: 0;
    appearance: none;
    width: 100%;
    height: 100%;
    border: 1px solid black;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 100;
    opacity: 0;
}

.unchecked-text {
    color: black !important;
    font-weight: 700;
}

.btn-heading {
    color: black;
    font-family: 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Open Sans', 'Helvetica Neue', 'sans-serif';
    padding: .4vw 0;
}

body {
    float: left;
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
   
<body>
    <div class="btn-heading">Dark Mode</div>
    <div class="sliding-switch">
        <input type="checkbox" id="btn" class="switch-checkbox" />
        <div class="outer-switch-box">
            <div class="inner-switch-box">
                <span class="status-text checked-text" id="textp1">on</span>
                <span class="outer-button">
                    <span class="inner-button"></span>
                </span>
                <span class="status-text unchecked-text" id="textp2">off</span>
            </div>
        </div>
    </div>
</body>

</html>

As you might have noticed, I also tried manipulating CSS pseudo class properties using JS. But that was a complete mess. Then, I thought of this approach and I was quite confident that it is correct but looks like I was wrong :(


Solution

  • Just adding a condition to setting "clickc" to "true" will probably do the trick. Here I've used a similar condition to that you've already used for the "thisvarisgud4me" key.

    I took the opportunity to test out a utility I created that essentially implements the Storage API (that's what <script src="https://heretic-monkey.link/FauxStorage.js"></script> is in the HTML, and why all of your localStorage references now say localStore).

    So if you decide to copy and paste this into your own code, just do a search and replace of localStore with localStorage.

    if (!localStore.getItem('thisvarisgud4me')) {
      localStore.setItem("thisvarisgud4me", "1")
    }
    
    document.getElementById("btn").addEventListener("click", change);
    var c = "true";
    if (!localStore.getItem("clickc")) {
      localStore.setItem("clickc", c);
    }
    
    function change() {
      if (localStore.getItem("clickc") == "false") {
        document.getElementById("btn").click();
        localStore.getItem("clickc") = "true";
      } else if (localStore.getItem("clickc") == "true") {
        if (localStore.getItem('thisvarisgud4me') == '1') {
          localStore.setItem("thisvarisgud4me", '0')
        } else {
          localStore.setItem("thisvarisgud4me", '1')
        }
    
        var num = Number(localStore.getItem('thisvarisgud4me'));
        let root = document.documentElement;
        root.style.setProperty("--numvar", num);
        console.log(num);
        if (num == 0) {
          window.addEventListener("beforeunload", function(event) {
            console.log("The page is redirecting")
            alert("Reload");
            localStore.setItem("clickc", "false");
            // document.getElementById("btn").click();
            // debugger;
          });
        }
      }
    }
    var num = Number(localStore.getItem('thisvarisgud4me'));
    let root = document.documentElement;
    root.style.setProperty("--numvar", num);
    :root {
      --numvar: 0;
    }
    
    html {
      filter: invert(var(--numvar));
    }
    
    body {
      background: #fff;
    }
    
    .outer-button {
      display: inline-block;
      height: 28px;
      width: 28px;
      position: relative;
      margin: 0 3px;
    }
    
    .inner-button {
      display: inline-block;
      position: absolute;
      width: 100%;
      height: 100%;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), inset 0px 0px 1px 2px white;
      border-radius: 20px;
      background: #f5f5f5;
    }
    
    span {
      display: inline-block;
      vertical-align: middle;
    }
    
    .status-text {
      color: white;
      text-transform: uppercase;
      font-size: 11px;
      font-family: Futura, sans-serif;
      transition: all 0.2s ease;
    }
    
    .sliding-switch {
      height: 28px;
      width: 72px;
      position: relative;
    }
    
    .outer-switch-box {
      overflow: hidden;
      height: 100%;
      width: 100%;
      display: inline-block;
      border-radius: 20px;
      position: relative;
      box-shadow: inset 0 1px 3px 0px #818181, 0px 1px 2px 1px white;
      transition: all 0.3s ease;
      transition-delay: 65ms;
      position: absolute;
      z-index: 1;
    }
    
    .inner-switch-box {
      position: relative;
      width: 175px;
      transition: all 0.3s ease;
    }
    
    
    /* .switch-checkbox:checked+.outer-switch-box .unchecked-text {
        color: transparent;
    }
    
    .switch-checkbox:not(:checked)+.outer-switch-box .checked-text {
        color: transparent;
    } */
    
    .switch-checkbox:checked+.outer-switch-box .inner-switch-box {
      left: -27px;
      /*OFF*/
    }
    
    .switch-checkbox:not(:checked)+.outer-switch-box .inner-switch-box {
      left: 20px;
      /*ON*/
    }
    
    .switch-checkbox:checked+.outer-switch-box {
      /* background-image: linear-gradient(#b6d284, #b6d284); */
      background: #492d7b;
      /* background: #b6d284; */
    }
    
    .switch-checkbox:not(:checked)+.outer-switch-box {
      /* background-image: linear-gradient(#cbcbcb, #dbdbdb); */
      background: #dbdbdb;
    }
    
    [type="checkbox"] {
      margin: 0;
      padding: 0;
      appearance: none;
      width: 100%;
      height: 100%;
      border: 1px solid black;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 100;
      opacity: 0;
    }
    
    .unchecked-text {
      color: black !important;
      font-weight: 700;
    }
    
    .btn-heading {
      color: black;
      font-family: 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Open Sans', 'Helvetica Neue', 'sans-serif';
      padding: .4vw 0;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <script src="https://heretic-monkey.link/FauxStorage.js"></script>
    </head>
      <body>
        <div class="btn-heading">Dark Mode</div>
        <div class="sliding-switch">
          <input type="checkbox" id="btn" class="switch-checkbox" />
          <div class="outer-switch-box">
            <div class="inner-switch-box">
              <span class="status-text checked-text" id="textp1">on</span>
              <span class="outer-button">
                        <span class="inner-button"></span>
              </span>
              <span class="status-text unchecked-text" id="textp2">off</span>
            </div>
          </div>
        </div>
      </body>
    
    </html>

    Here's how I would refactor it. This is more of an object-oriented way of doing things; it might not appeal to everyone and it certainly isn't meant to. It works for me and I'm the only one I need to make happy with it :).

    class ThemeStore {
      _darkModeKey = "thisvarisgud4me";
      _darkMode = null;
      get darkMode() {
        if (this._darkMode === null) {
          if (!localStore.getItem(this._darkModeKey)) {
            localStore.setItem(this._darkModeKey, 0);
          }
          this._darkMode = JSON.parse(localStore.getItem(this._darkModeKey));
        }
        return this._darkMode;
      }
      set darkMode(value) {
        this._darkMode = value;
      }
      persist() {
        localStore.setItem("thisvarisgud4me", JSON.stringify(this.darkMode));
      }
    }
    
    var themeStore = new ThemeStore();
    document.getElementById("btn").addEventListener("click", change);
    
    function change(e) {
      themeStore.darkMode = e.target.checked ? 0 : 1;
      let root = document.documentElement;
      root.style.setProperty("--numvar", themeStore.darkMode);
      console.log(themeStore.darkMode);
      if (themeStore.darkMode === 0) {
        window.addEventListener("beforeunload", function(event) {
          console.log("The page is redirecting")
          themeStore.persist();
        });
      }
    }
    
    document.getElementById("btn").dispatchEvent(new CustomEvent("change"));
    :root {
      --numvar: 0;
    }
    
    html {
      filter: invert(var(--numvar));
    }
    
    body {
      background: #fff;
    }
    
    .outer-button {
      display: inline-block;
      height: 28px;
      width: 28px;
      position: relative;
      margin: 0 3px;
    }
    
    .inner-button {
      display: inline-block;
      position: absolute;
      width: 100%;
      height: 100%;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), inset 0px 0px 1px 2px white;
      border-radius: 20px;
      background: #f5f5f5;
    }
    
    span {
      display: inline-block;
      vertical-align: middle;
    }
    
    .status-text {
      color: white;
      text-transform: uppercase;
      font-size: 11px;
      font-family: Futura, sans-serif;
      transition: all 0.2s ease;
    }
    
    .sliding-switch {
      height: 28px;
      width: 72px;
      position: relative;
    }
    
    .outer-switch-box {
      overflow: hidden;
      height: 100%;
      width: 100%;
      display: inline-block;
      border-radius: 20px;
      position: relative;
      box-shadow: inset 0 1px 3px 0px #818181, 0px 1px 2px 1px white;
      transition: all 0.3s ease;
      transition-delay: 65ms;
      position: absolute;
      z-index: 1;
    }
    
    .inner-switch-box {
      position: relative;
      width: 175px;
      transition: all 0.3s ease;
    }
    
    
    /* .switch-checkbox:checked+.outer-switch-box .unchecked-text {
        color: transparent;
    }
    
    .switch-checkbox:not(:checked)+.outer-switch-box .checked-text {
        color: transparent;
    } */
    
    .switch-checkbox:checked+.outer-switch-box .inner-switch-box {
      left: -27px;
      /*OFF*/
    }
    
    .switch-checkbox:not(:checked)+.outer-switch-box .inner-switch-box {
      left: 20px;
      /*ON*/
    }
    
    .switch-checkbox:checked+.outer-switch-box {
      /* background-image: linear-gradient(#b6d284, #b6d284); */
      background: #492d7b;
      /* background: #b6d284; */
    }
    
    .switch-checkbox:not(:checked)+.outer-switch-box {
      /* background-image: linear-gradient(#cbcbcb, #dbdbdb); */
      background: #dbdbdb;
    }
    
    [type="checkbox"] {
      margin: 0;
      padding: 0;
      appearance: none;
      width: 100%;
      height: 100%;
      border: 1px solid black;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 100;
      opacity: 0;
    }
    
    .unchecked-text {
      color: black !important;
      font-weight: 700;
    }
    
    .btn-heading {
      color: black;
      font-family: 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Open Sans', 'Helvetica Neue', 'sans-serif';
      padding: .4vw 0;
    }
    <script src="https://heretic-monkey.link/FauxStorage.js"></script>
    <div class="btn-heading">Dark Mode</div>
    <div class="sliding-switch">
      <input type="checkbox" id="btn" class="switch-checkbox" />
      <div class="outer-switch-box">
        <div class="inner-switch-box">
          <span class="status-text checked-text" id="textp1">on</span>
          <span class="outer-button">
            <span class="inner-button"></span>
          </span>
          <span class="status-text unchecked-text" id="textp2">off</span>
        </div>
      </div>
    </div>