Embedding Style Sheet Switcher

I threw this together as a “minimal as I could get it” POC. I’lll say one thing, by the time you get done you’ll be no stranger to selectors

<!DOCTYPE HTML>
<html lang="en">
<head>
<title>CSS Switch Test</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" >
<style type="text/css">
/* default styles */
body { 
  font-family: sans-serif;
}
#heading { 
  border: 1px solid #f00;
  background-color: #faa;
  padding: 0.2em;
}
p { 
  border: 1px solid #333;
  padding: 0.2em;
}
.programmer_ipsum {
  background-color: #000;
  color: #fff;
  font-family: monospace;
  font-weight: bold;
}
</style>
<script>
// script needed before the DOM is loaded here
</script>
</head>
<body>
<h1 id="heading">CSS Switch Test</h1>
<p class="cat_ipsum">
Lounge in doorway rub face on everything, so flee in terror at cucumber discovered on floor, meow meowzer! or paw at beetle and eat it before it gets away tuxedo cats always looking dapper.
</p>
<p class="kiwi_ipsum">
After the pinapple lump is flogged, you add all the hammered jerseys to the Jafa you've got yourself a meal.
</p>
<p class="piate_ipsum">
Avast chantey crack Jennys tea cup draft Yellow Jack heave to capstan lookout Letter of Marque loaded to the gunwalls pressgang stern long clothes furl mutiny Barbary Coast execution dock scallywag driver line man-of-war long boat jolly boat hail-shot piracy brig brigantine fathom smartly ballast black spot case shot measured fer yer chains main sheet spike parrel quarter gangway gabion grog Davy Jones' Locker deadlights hearties cackle fruit matey bring a spring upon her cable crimp walk the plank yard doubloon. 
</p>
<p class="programmer_ipsum">
Null pointer exception handling cpan null c# stdin subroutine hypertext markup language compiler parenthesis.
</p>
<button value="1">Style One</button>
<button value="2">Style Two</button>
<button value="3">Style Three</button>
<script>
// script that needs the DOM to be loaded here
function switch_style(option) {
  var style_tag = document.head.querySelector('style');
  var optional_styles = { 
  '1': "body {font-family: sans-serif;} #heading {border: 1px solid #f00; background-color: #faa; padding: 0.2em;} p { border: 1px solid #333; padding: 0.2em;} .programmer_ipsum {background-color: #000; color: #fff; font-family: monospace; font-weight: bold;}",

  '2': "body {font-family: serif;} #heading {border: 1px solid #0f0; background-color: #afa; padding: 0.2em;} p {border: 2px solid #666; padding: 1em;} .programmer_ipsum {background-color: #00f; color: #fff; font-size: 2em; font-family: monospace; font-weight: bold;}",

  '3': "body {font-family: sans-serif;} #heading {border: 1px solid #00f; background-color: #aaf; text-align: center;} p {border: 3px solid #999; padding: 0.2em;} .programmer_ipsum {  background-color: #000; color: #fff; font-family: monospace; font-weight: bold;}"
  };
  style_tag.textContent = optional_styles[option];
}

var buttons = document.body.querySelectorAll('button');
var button_count = buttons.length;

for (var i = 0; i < button_count; i++) {
 buttons[i].addEventListener('click', function(evt) {
   switch_style(evt.target.value);
 }, false);
}
</script>
</body>
</html>
2 Likes

My latest thought is to keep all the CSS in the style tag but to add an extra class to the front of ALL the selectors. Then the JavaScript applies the appropriate class to the HTML tag so as to select which part of the really huge CSS should be applied to the page.

That would transfer the complexity to the CSS all of which would need to be written and allow a simple 2 or 3 line JavaScript.

At least this would be consistent with the way you’d change the styling for when JavaScript isn’t supported rather than doing something unique to the page.

Still a really bad idea for a web page though as you are still forcing people to download all the styles even if the switcher is not used and are even forcing the download of styles in other options not selected even when the switcher is used.

Using this approach the page would probably only take about 400% of the time it would take to load if it were done properly with separate files for the first page and 4000% for each subsequent page.

1 Like

Nice and simple, I like it and will be using it sometime.

Online Demo

Edit:
I took the liberty of moving the buttons to the top to prevent them from jumping up and down :slight_smile:

2 Likes

I added some code that has JavaScript add a class attribute value. (and pirate flags on the ocean :wink: )
I do like that it keeps the CSS out of the JavaScript. But I still think it would be a bear to maintain.

<!DOCTYPE HTML>
<html lang="en">
<head>
<title>CSS Switch Test</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" >
<style type="text/css">
/* default styles */
body { 
  font-family: sans-serif;
}
#heading { 
  border: 1px solid #f00;
  background-color: #faa;
  padding: 0.2em;
}
p { 
  border: 1px solid #333;
  padding: 0.2em;
}
.programmer_ipsum {
  background-color: #000;
  color: #fff;
  font-family: monospace;
  font-weight: bold;
}
</style>
<style>
#heading.style_4, .style_4 {
  border: 1px solid #0ff;
  background-color: #aff;
  color: #033;
}
p.pirate_ipsum.style_4 {
  background-color: #bbb;
  color: #000;
  font-family: monospace;
  font-weight: bold;
  font-size: 2em;
  margin: 0.2em;
  padding: 0.2em;
  line-height: 2;
  text-decoration: underline wavy #00f;
}
p.pirate_ipsum.style_4:before, p.pirate_ipsum.style_4:after {
  content: "\1F571";
  font-size: 1.5em;
  color: #fff;
  padding: 0 0.5em;
  background-color: #000;
}
</style>
<script>
// script needed before the DOM is loaded here
</script>
</head>
<body>
<h1 id="heading">CSS Switch Test</h1>
<p class="cat_ipsum">
Lounge in doorway rub face on everything, so flee in terror at cucumber discovered on floor, meow meowzer! or paw at beetle and eat it before it gets away tuxedo cats always looking dapper.
</p>
<p class="kiwi_ipsum">
After the pinapple lump is flogged, you add all the hammered jerseys to the Jafa you've got yourself a meal.
</p>
<p class="pirate_ipsum">
Avast chantey crack Jennys tea cup draft Yellow Jack heave to capstan lookout Letter of Marque loaded to the gunwalls pressgang stern long clothes furl mutiny Barbary Coast execution dock scallywag driver line man-of-war long boat jolly boat hail-shot piracy brig brigantine fathom smartly ballast black spot case shot measured fer yer chains main sheet spike parrel quarter gangway gabion grog Davy Jones' Locker deadlights hearties cackle fruit matey bring a spring upon her cable crimp walk the plank yard doubloon. 
</p>
<p class="programmer_ipsum">
Null pointer exception handling cpan null c# stdin subroutine hypertext markup language compiler parenthesis.
</p>
<button value="1">Style One</button>
<button value="2">Style Two</button>
<button value="3">Style Three</button>
<button value="4">Style Four</button>
<script>
// script that needs the DOM to be loaded here
function switch_style(option) {
  function maybe_add_class(elem) {
    if (elem.nodeType === 1) {
      var class_attr = elem.hasAttribute("class");
      if (class_attr) {
        var elem_class_attr_val = elem.getAttribute("class");
        if (elem_class_attr_val.indexOf("style_4") == -1) {
          elem.setAttribute('class', elem_class_attr_val + " style_4");
        }
      } else {
        elem.setAttribute('class', "style_4");
      }
    }
  }
  function maybe_strip_class(elem) {
    if (elem.nodeType === 1) {
      var class_attr = elem.hasAttribute("class");
      if (class_attr) {
        var elem_class_attr_val = elem.getAttribute("class");
        if (elem_class_attr_val.indexOf("style_4") != -1) {
          var class_attr_rem = (elem_class_attr_val).replace(/( )?(style_4)/, '');
          elem.setAttribute('class', class_attr_rem);
        }
      }
    }
  }
  if (option == 4) {
	var body_tag = document.querySelector('body');
	var body_childnodes = body_tag.childNodes;
    var body_childnodes_count = body_childnodes.length;
	for (var j = 0; j < body_childnodes_count; j++) {
	  maybe_add_class(body_childnodes[j]);
	}
	maybe_add_class(body_tag);
	return;
  } else {
	var body_tag = document.querySelector('body');
	var body_childnodes = body_tag.childNodes;
    var body_childnodes_count = body_childnodes.length;
	for (var j = 0; j < body_childnodes_count; j++) {
	  maybe_strip_class(body_childnodes[j]);
	}
	maybe_strip_class(body_tag);
	
    var style_tags = document.head.querySelectorAll('style');
    var optional_styles = { 
'1': "body {font-family: sans-serif;} #heading {border: 1px solid #f00; background-color: #faa; padding: 0.2em;} p { border: 1px solid #333; padding: 0.2em;} .programmer_ipsum {background-color: #000; color: #fff; font-family: monospace; font-weight: bold;}",

'2': "body {font-family: serif;} #heading {border: 1px solid #0f0; background-color: #afa; padding: 0.2em;} p {border: 2px solid #666; padding: 1em;} .programmer_ipsum {background-color: #00f; color: #fff; font-size: 2em; font-family: monospace; font-weight: bold;}",

'3': "body {font-family: sans-serif;} #heading {border: 1px solid #00f; background-color: #aaf; text-align: center;} p {border: 3px solid #999; padding: 0.2em;} .programmer_ipsum {  background-color: #000; color: #fff; font-family: monospace; font-weight: bold;}"
};
    style_tags[0].textContent = optional_styles[option];
  }
}

var buttons = document.body.querySelectorAll('button');
var button_count = buttons.length;

for (var i = 0; i < button_count; i++) {
 buttons[i].addEventListener('click', function(evt) {
   switch_style(evt.target.value);
 }, false);
}
</script>
</body>
</html>
2 Likes

Hello guys!

Mittineague!!! that’s great, thank you for your efforts, a working example, and an improved version of it. Very kind of you to put such effort into it, and the second version even generates horizontal lines for the 4th theme, which are a nice touch, I know I will be using that at some point as well. I’m sure that your coding will not only help me, but many other people as well, because it works very nicely. Now I have something to study, and look into it so I can become familiar with it. If there will be any question, I will be coming back here, but for now, this is great, it works as a self-contained html file, which is exactly what I wanted!!

Again THANK YOU Mittineague!! and I appreciate everyone contributing to this thread, because… well is good to have ideas thrown around.

2 Likes

Updated:

Online Demo

1 Like

Just for fun and seeing as we are in the CSS forum here’s a CSS version.:slight_smile:

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Untitled Document</title>
<style>
.wrap {
	max-width:960px;
	margin:auto;
	background:#f9f9f9;
	padding:10px;
	border:1px solid #000;
	transition:all 1s ease;
}
#style1:checked ~ .wrap {
 	max-width:600px;
 	background:red;
 	color:#fff;
}
#style2:checked ~ .wrap {
 	max-width:1200px;
 	background:orange;
 	color:#fff;
 	display:flex;
}
#style2:checked ~ .wrap p, #style2:checked ~ .wrap h1 {
	border:1px solid #ccc;
	background:blue;
	padding:20px;
	margin:0;
}
#style3:checked ~ .wrap {
 	max-width:980px;
 	background:yellow;
 	color:#000;
 	margin:auto;
}
#style2:checked ~ .wrap p, #style2:checked ~ .wrap h1 {
	border:1px solid #ccc;
	background:blue;
	padding:20px;
}
</style>
</head>

<body>
<input type="radio" name="style" id="style1">
<label for="style1">Style 1</label>
<br>
<input type="radio" name="style" id="style2">
<label for="style2">Style 2</label>
<br>
<input type="radio" name="style" id="style3">
<label for="style3">Style 3</label>
<div class="wrap">
  <h1>This is a test</h1>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin faucibus, magna et feugiat ullamcorper, augue risus imperdiet nunc, ut placerat felis elit ac quam. Mauris accumsan vulputate eleifend. Nulla id sapien elit. Duis ac nibh pharetra, dignissim nisi eu, ullamcorper lectus. Vivamus vulputate ex vel augue venenatis egestas. Praesent ut hendrerit enim. Morbi id aliquam eros. Praesent vulputate ut nunc sit amet efficitur. Proin iaculis sit amet eros sed porttitor. Phasellus fermentum pretium viverra. Donec bibendum viverra faucibus.</p>
</div>
</body>
</html>
4 Likes

I like the smooth scrolling and the succinct script :smile:

Online Demo

2 Likes

Hi Paul, nice to have you here, your solution is indeed short and sweet, using only CSS, it is a very nice addition, and option to have. I did try your version, of course, and the idea of transition is a fine one, really nice.
That is why I like people, the more the better, everyone has something unique. The only thing was that you did not include an option of going back to the original style, when the page was loaded, so I hope you don’t mind, because I added that in, here it is:

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Untitled Document</title>
<style>
.wrap {
    max-width:960px;
    margin:auto;
    background:#f9f9f9;
    padding:10px;
    border:1px solid #000;
    transition:all 1s ease;
}

#style1:checked ~ .wrap {
    max-width:960px;
    margin:auto;
    background:#f9f9f9;
    padding:10px;
    border:1px solid #000;
}

#style2:checked ~ .wrap {
     max-width:600px;
     background:red;
     color:#fff;
}
#style3:checked ~ .wrap {
     max-width:1200px;
     background:orange;
     color:#fff;
     display:flex;
}
#style3:checked ~ .wrap p, #style3:checked ~ .wrap h1 {
    border:1px solid #ccc;
    background:blue;
    padding:20px;
    margin:0;
}
#style4:checked ~ .wrap {
     max-width:980px;
     background:yellow;
     color:#000;
     margin:auto;
}

</style>
</head>

<body>
<input type="radio" name="style" id="style1">
<label for="style1">Style 1</label>
<br>
<input type="radio" name="style" id="style2">
<label for="style2">Style 2</label>
<br>
<input type="radio" name="style" id="style3">
<label for="style3">Style 3</label>
<br>
<input type="radio" name="style" id="style4">
<label for="style4">Style 4</label>
<div class="wrap">
  <h1>This is a test</h1>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin faucibus, magna et feugiat ullamcorper, augue risus imperdiet nunc, ut placerat felis elit ac quam. Mauris accumsan vulputate eleifend. Nulla id sapien elit. Duis ac nibh pharetra, dignissim nisi eu, ullamcorper lectus. Vivamus vulputate ex vel augue venenatis egestas. Praesent ut hendrerit enim. Morbi id aliquam eros. Praesent vulputate ut nunc sit amet efficitur. Proin iaculis sit amet eros sed porttitor. Phasellus fermentum pretium viverra. Donec bibendum viverra faucibus.</p>
</div>
</body>
</html>

And John is doing a nice job at keeping track of those versions, and placing those control switches at the top. Indeed is worth having them all in one place for reference, you never know when you might want to use one, two or all of them. Myself I will be using each version on a different website, just because I like variety, and different ideas.

Mittineague!! I did play quite a bit with your versions, and I think I’m going to have to ask for help about the second one, but not yet, I will try a bit more, and if I don’t figure it out I will be back with the question, or questions, because I have two of them on my mind right now. The first version works excellent, actually so does the second version, is just that I’m a bit lost, well… anyway let me poke around some more.

Thank you guys and have a good night!

3 Likes

@Diamax
Many thanks.

Demo Updated

1 Like

Yes I forgot about that :smile:

Hi guys,

John, it is flattering that you thought my name should be associated with that piece of coding, but the truth is I DO NOT deserve credit at all. My name should not even be mentioned there, I did not come up with a single line of code. All I have done is a copy and paste job, using Paul’s own lines, he’s name is the one that needs to be there. My contribution to his coding is like a comma in a long paragraph, and even that is overstating. It was just on oversight on his part to add that option in. My coding abilities are far below average, so please give credit to the wright person, not myself, I’m totally undeserving in this case.

I am here asking for help, because you guys know better. When I’m able to make enough sense for my purposes, of what you guys code, I’m happy because I can use that in my own webpages.

And to bring this to a conclusion I just want to mention that I am still “battling” to convert Mittineague’s second version, how can I have credit given to me for something that I am not even able to come up with? I’m glad I can understand it. So, that’s Paul’s creation.

Thank you John.

It is more involved than it could be because I added it to the previous instead of writing it as it’s own example.

Maybe some comments will help?

  function maybe_strip_class(elem) { 
// only bother with actual "tags" 
    if (elem.nodeType === 1) { 
// determine if the tag has a class attribute 
      var class_attr = elem.hasAttribute("class");
// if so, it's truthy   
      if (class_attr) { 
// get the attribute value as a strng 
        var elem_class_attr_val = elem.getAttribute("class"); 
// determine if the string has the text 
        if (elem_class_attr_val.indexOf("style_4") != -1) { 
// if there are other values there will be a space, but otherwise not 
// regex to deal with both to create a new value string 
          var class_attr_rem = (elem_class_attr_val).replace(/( )?(style_4)/, ''); 
// replace the value with the new one 
          elem.setAttribute('class', class_attr_rem); 
1 Like

Thank you Mittineague,
For today I’m pretty much done, at one point I got it working, not perfect but I was getting close, however I got distracted talking with someone and everything went wrong, not sure what I’ve done, I lost track of what I was changing… I will start working on it tomorrow, fresh! I would like to solve the rest of it myself… you gave me a start on it.

I was thinking one thing you might want to try is changing the had-coded
"style_4" lines to
'"style_" + option
to make the code more flexible.

@Diamax

John, it is flattering that you thought my name should be associated
with that piece of coding, but the truth is I DO NOT deserve credit at
all. My name should not even be mentioned there, I did not come up with a
single line of code. All I have done is a copy and paste job, using
Paul’s own lines, he’s name is the one that needs to be there.

Demos Amended (CTRL-F5 to refresh page.)

1 Like

Can’t help thinking this thread would make a great article on different ways of implementing a style sheet switcher.

2 Likes

I wouldn’t have thought that encouraging people to write switchers that require all the styles to be downloaded instead of just the one or two that are actually needed is a very good idea. It there are five alternate styles to switch to then this alternative is forcing the download of three or four of them to throw away.

Also the one the OP was trying to replace can work in many browsers even with the JavaScript turned off.

Hi Mittineague,

I couldn’t make anything work, actually I don’t think i was ever close to solve it. I tried so many times at some point I got a headache out of frustration. Well I do not know javascript at all, but I thought I might be able to get it working on my own.
If you won’t mind posting your second version by itself it, with the separate CSS, it would be an extra bonus.

I thank you for all the help Mittineague!!

It isn’t that hard to get the basics down. It’s trying to get to the “expert” level that is an endless task.

Post the JavaScript code you have and I’ll be happy to look at it.