What are the options to work with themes?

Are there better or smarter way to work with themes than this? No framework used.

With document.documentElement, I would use document.body instead.

Also with setAttribute to work with data attributes, there is a dataset protocol instead.

// document.documentElement.setAttribute('data-theme', localStorage.theme);
document.body.dataset.theme = localStorage.theme;

That is not for the purpose of shortening things, even though in this case it has that effect. Instead it is to help make it easier to understand what is happening.

There is a danger of the above being considered a train-wreck though (several names joined by a period between them) so I would assign document.body to a local variable instead.

const body = document.body;
body.dataset.theme = localStorage.theme;

There was also a mixing of quotes in the jsfiddle code. Sometimes single quotes are used, sometimes double quotes are used. As a majority of coding style guides prefer remaining consistent with double quotes, I have adjusted all of them to be that same type.

With the onclick method, that is an old event handling technique that has been improved on by using addEventListener. I would also name the event listener function, because anonymous functions are almost always improved by being named.

// document.querySelector(".theme").onclick = function() {
const theme = document.querySelector(".theme");
theme.addEventListener("click", function themeClickHandler() {
    ...
});

Applying these things to the rest of the code, and using triple equals instead of double, we end up with the following:

theme.addEventListener("click", function themeClickHandler() {
  if (localStorage.theme === "indigo") {
    document.body.dataset("theme", "barbie");
    localStorage.setItem("theme", "barbie");
  } else {
    document.body.dataset("theme", "indigo");
    localStorage.setItem("theme", "indigo");
  }
});

There is more from there though.

Usually if statements are preferred by moving code out of the if statement, so that there are only function calls in the if statement. We can achieve that by creating and using a changeTheme function.

//toggling between themes
function setTheme(name) {
    body.dataset.theme = name;
    localStorage.setItem("theme", name);
}

const theme = document.querySelector(".theme");
theme.addEventListener("click", function themeClickHandler() {
  if (localStorage.theme === "indigo") {
    setTheme("barbie");
  } else {
    setTheme("indigo");
  }
});

The if statements might now be improved by using a query selector. That can be achieved by dealing with the duplication of the changeTheme() function call, by putting the new theme name into a local variable.

theme.addEventListener("click", function themeClickHandler() {
  let themeName = "";
  if (localStorage.theme === "indigo") {
    themeName = "barbie";
  } else {
    themeName = "indigo";
  }
  setTheme(themeName);
});

Now that we have a clear separation between deciding the theme from changing the theme, we can use a ternary statement to help simplify the if else statement.

theme.addEventListener("click", function themeClickHandler() {
  const themeName = (
    localStorage.theme === "indigo"
    ? "barbie"
    : "indigo"
  );
  setTheme(themeName);
});

We are though mixing levels of abstraction in that function though. The work needed to figure out the theme name needs to be moved into a separate function too.

function nextThemeName(currentTheme) {
  return currentTheme === "indigo" ? "barbie" : "indigo";
}
...
theme.addEventListener("click", function themeClickHandler() {
  const themeName = nextThemeName(localStorage.theme);
  setTheme(themeName);
});

We now have several functions, so lets group those together. That ends up putting the code that sets things up, at the end of the code, where we can call setTheme in the initial load too.

const theme = document.querySelector(".theme");
theme.addEventListener("click", themeClickHandler);

//initial load from localstorage:
setTheme(localStorage.theme);

The code to this point is found at: https://jsfiddle.net/n1waL2dj/

Do we then move the code into a module, so that we can trigger it with one command?

const theme = (function themeManager() {
    function nextThemeName(currentTheme) {
      return currentTheme === "indigo" ? "barbie" : "indigo";
    }
    function setTheme(name) {
        const body = document.body;
        body.dataset.theme = name;
        localStorage.setItem("theme", name);
    }
    function themeClickHandler() {
      const themeName = nextThemeName(localStorage.theme);
      setTheme(themeName);
    }
    function initThemeButton() {
        const theme = document.querySelector(".theme");
	    theme.addEventListener("click", themeClickHandler);
    }
    function init() {
    	initThemeButton();
        setTheme(localStorage.theme);
    }

    return {
      init
    };
}());

theme.init();

That might be taking things too far https://jsfiddle.net/n1waL2dj/1/ , but that’s the kind of structure that helps to make more complex types of code easier to deal with.

1 Like