Embedding Style Sheet Switcher

It is easiest for testing the code as you write it - it is hardest once the pages are finished and go live.

You should always separate out the tested CSS and JavaScript into separate files once you finish testing them - that way you can reuse them in other pages knowing that they already work. Also if you have more than one page you make them much faster to load as you are not forcing your visitors to download the same sytles and scripts for every single page instead of oncee

To set up a one file page with built in stylesheet switcher you need to delete all the CSS and then create JavaScript to generate all the CSS. This will involve about 20 times as much code as if you use the separate CSS files and will make it almost impossible to maintain. It will also be about 25 times slower to download as with separate CSS files for each style each visitor only needs to download at most two - the default and the one they switch to - instead of having to download the script to generate all the ones they donā€™t want.

If you insist on doing it then you will probably need to add at least a month of full time bug fixing before you will be able to get it to work. You might need to open a new thread to post the code once you finally figure it out as this one will automatically close three months after the last post. Donā€™t expect anyone to visit your pages to see it though - they will bee way too slow loading for anyone to be prepared to wait long enough.

Hey Felgall,

I used to have my webpages exactly as you say, everything separated, all style sheets, all js files, pictures, font files, etc. on their own, but I got tired managing them when was time to update. Then I said to myself I wll use WordPress or Joomla, and I tried them both, but it was even more headaches, more files to deal with, and so I made the decision that everything will go into one file, and that for me works wonders.

As I am looking now at one of my one page websites, the file size is 820.5Kb, and I know that seems very large, but the update that I will be putting up at some point right now is a file of 987.6Kb, so is even larger. I keep my websites as one page only, and each of those one page websites is a whole lot faster, when it comes to loading times, than many of the other websites that I visit on the internet. granted there are not a lot of graphics, just some backgrounds, but I like to keep them simple. They all have a ton of js, css, some fonts and few graphics, that hardly slows the page down.

I agree with you, the amount of code included is really large, but it works well.
I guess we all have different ways of doing things, various preferencesā€¦ likes.

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.