Skip to content

CSS and JavaScript issue

Featured Replies

Hi all,

I'm fighting a strange issue with CSS and Javascript currently. It might be something simple and I'm missing it as it's late, but we'll see!

At the moment, I've got a website with 2 style sheets, both of which work/can be applied via HTML. What I'm after is the option for the user to choose the sheet themselves, and that's where I hit my error.

I've got the sheet declaration as follows:


<head>
 <link href="default.css" rel="stylesheet" type="text/css" media="screen" title="default"/> <!-- Default, preferred-->
 <link href="lightweight.css" rel="alternate stylesheet" type="text/css" media="screen" title="lightweight"/>

In my (WIP) Javascript I've then got the following:


function changeActiveStyleSheet(previous, newCSS)
{

 //if(document.styleSheets) {
   for (var i = 0; i < document.styleSheets.length; i++) {
 alert(document.styleSheets[1].title);      
     if(document.styleSheets[i].title == "previous") {
       document.styleSheets[i].disabled = true;
     }
     if(document.styleSheets[i].title == "newCSS") {
       document.styleSheets[i].disabled = false;
     }
   }
// }
}

The issue is it's only seeing 1 CSS. When run currently, the alert box pops up with '1' in it. If I change it to read out the title of sheet[0].title

  • Author

Hi all,

I'm fighting a strange issue with CSS and Javascript currently. It might be something simple and I'm missing it as it's late, but we'll see!

At the moment, I've got a website with 2 style sheets, both of which work/can be applied via HTML. What I'm after is the option for the user to choose the sheet themselves, and that's where I hit my error.

I've got the sheet declaration as follows:


<head>
<link href="default.css" rel="stylesheet" type="text/css" media="screen" title="default"/> <!-- Default, preferred-->
<link href="lightweight.css" rel="alternate stylesheet" type="text/css" media="screen" title="lightweight"/>

In my (WIP) Javascript I've then got the following:


function changeActiveStyleSheet(previous, newCSS)
{
alert(document.styleSheets.length);
for (var i = 0; i < document.styleSheets.length; i++) {
  alert(document.styleSheets[1].title);
  if(document.styleSheets[i].title == "previous") {
    document.styleSheets[i].disabled = true;
  }
  if(document.styleSheets[i].title == "newCSS") {
    document.styleSheets[i].disabled = false;
  }
 }
}

The issue is it's only seeing 1 CSS. When called currently, the alert box pops up with '1' in it. If I change it to read out the title of sheet[0].title then it shows 'default', which is great. If I set it sheet[1].title then it's blank (as you'd expect given it only knows of 1 sheet). My questions; where's the second sheet, and why can it see it?

As said, it's most likely something simple I'm overlooking, but it's throwing me!

Thanks!

Joe

Edited by TriggerFish

Whats the reason you want people to select a stylesheet? I only ask in case there is a better way of doing it..

The issue is it's only seeing 1 CSS. When called currently, the alert box pops up with '1' in it. If I change it to read out the title of sheet[0].title then it shows 'default', which is great. If I set it sheet[1].title then it's blank (as you'd expect given it only knows of 1 sheet). My questions; where's the second sheet, and why can it see it?

I'm seeing both in Firefox:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Test</title>

<link href="default.css" rel="stylesheet" type="text/css" media="screen" title="default" /> <!-- Default, preferred-->
<link href="lightweight.css" rel="alternate stylesheet" type="text/css" media="screen" title="lightweight" />
<script>
function changeActiveStyleSheet(previous, newCSS) {
var feedback = "";

if(document.styleSheets) {
for (var i = 0; i < document.styleSheets.length; i++) {
 if(document.styleSheets[i].title == previous) { document.styleSheets[i].disabled = true; }
 if(document.styleSheets[i].title == newCSS) { document.styleSheets[i].disabled = false; }

 (document.styleSheets[i].disabled) ? feedback += document.styleSheets[i].title + " is disabled\r\n" : feedback += document.styleSheets[i].title + " is enabled";

}

alert("You chose to change to '" + newCSS + "' from '" + previous + "'" + "\r\n\r\n" + feedback);

}

}

changeActiveStyleSheet("default","lightweight");

</script>

</head>
<body>
</body>
</html>

Safari and Chrome won't work with this as they don't support the styleSheet collection past the first stylesheet, so I'm guessing you're testing in Chrome?

Edited by Mort

  • Author

Whats the reason you want people to select a stylesheet? I only ask in case there is a better way of doing it..

Obligation. :( A piece of coursework I'm doing needs to have two different style sheets (although they don't have to be switchable, just have two included), but I thought I'd try and make them selectable by the user, just to see if I could really. As it's CW it's also why I'm not after help as to how to do it either. (New to HTML/JS/CSS)

I'm seeing both in Firefox:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Test</title>

<link href="default.css" rel="stylesheet" type="text/css" media="screen" title="default" /> <!-- Default, preferred-->
<link href="lightweight.css" rel="alternate stylesheet" type="text/css" media="screen" title="lightweight" />
<script>
function changeActiveStyleSheet(previous, newCSS) {
var feedback = "";

if(document.styleSheets) {
for (var i = 0; i < document.styleSheets.length; i++) {
 if(document.styleSheets[i].title == previous) { document.styleSheets[i].disabled = true; }
 if(document.styleSheets[i].title == newCSS) { document.styleSheets[i].disabled = false; }

 (document.styleSheets[i].disabled) ? feedback += document.styleSheets[i].title + " is disabled\r\n" : feedback += document.styleSheets[i].title + " is enabled";

}

alert("You chose to change to '" + newCSS + "' from '" + previous + "'" + "\r\n\r\n" + feedback);

}

}

changeActiveStyleSheet("default","lightweight");

</script>

</head>
<body>
</body>
</html>

Safari and Chrome won't work with this as they don't support the styleSheet collection past the first stylesheet, so I'm guessing you're testing in Chrome?

Cheers! That would explain an awful lot. I've just tried loading it in FF and it's working as expected. And yes, I was in Chrome. Hmmm, either I drop Chrome users (only needs to work in 3 different browsers) or go back to the drawing board!

It's also now working properly in FF. Now to add cookies etc in so it saves the choice between sessions.

Edited by TriggerFish

The problem is Webkit only 'sees' non-disabled stylesheets in the collection. So Safari will choke on it too. I think KHTML browsers (Konqueror) will also mess it up. You're only really safe with IE, Firefox/Gecko and newer versions of Opera.

Try this in Chrome:

alert(document.styleSheets.length);
document.styleSheets[0].disabled = true;
alert(document.styleSheets.length);

Helpful, hey?

Sorry to keep posting but this annoyed me so I've been looking into exactly what Webkit doesn't like about alternate stylesheets. Seems Webkit will let you use the styleSheet collection in this fashion IF you have no title attributes and do not declare it as an 'alternate stylesheet' ('stylesheet' only).

So a workaround that uses the styleSheets collection that's cross browser is simply not possible while sticking to W3C guidelines as far as I can tell (otherwise refer to your stylesheets by index, set them all as on and persistent, and turn off the extras onload (ugly) ).

So the only way I can see to do it would be to get the Elements manually into an array by checking link elements containing 'stylesheet' in the rel. Then manually disabling what you're not wanting:

function changeActiveStyleSheet(newCSS) {

  var links = document.getElementsByTagName('link');
  for (var i = links.length - 1; i >= 0; i--) {
   if (links[i].getAttribute('rel').indexOf('style')> -1 && links[i].getAttribute('title')) {
 links[i].disabled = true;
 if (links[i].getAttribute('title') == newCSS) links[i].disabled = false;
   }
  }

  alert("You chose to change to '" + newCSS + "'\r\nThere are " + document.styleSheets.length + " stylesheets according to your browser");
 }

Not much more code, but a little heavier on the CPU I guess?

Works in Webkit and Gecko. Not tried Opera or IE, but I assume it will work.

Personally though, I'd do it server-side so you only serve up the stylesheet the user has asked for and you never need to worry about browser compatibility.

Thanks for exercising my brain though! :D

  • Author

No need to apologise, that's awesome, thanks! I was unaware of the level of differences with WebKit etc, but I have got what you posted there working, although I changed a tiny thing to get it OK in Chrome.


alert("You chose to change to '" + newCSS + "'\r\nThere are " + /*document.styleSheets.length*/ links.length + " stylesheets according to your browser");

Before it used the styleSheets.length, which only reports 1 as found in Chrome earlier, but 2 in other browsers. By changing it to links.length, it showed 2 in Chrome as well.

I know it's not an ideal way of doing it, but for what I need, it's adequate (and above/beyond requirements too). Plus I don't think we can run any sort of scripting on the server itself anyway.

Again, thanks for the help! Just got the make the change to the CSS persistent between sessions.

  • Author

Sorry to keep flogging this horse, but I've run into another issue, which, again, is defeating me.

Using your method in conjunction with some of my own code, I can get it too allow me to choose a CSS file, and then save a cookie with that information in.

There's then another method that is called on page (body) load. This method looks at the cookie, takes the value of the contents and then disables/enables the relevant CSS files in the same way as shown below. This works, sort of.

In FF it's perfect, the selected CSS is applied to all pages etc. However, in IE (10, if it's any different) and Chrome it 'changes' the CSS, except it doesn't. It just disables all CSS when selecting the 'non-default' option. On going back to the default CSS it's fine.

I'm guessing it's not just the code as it works OK in FF, but maybe some Webkit style differences? If you've got time to voice any ideas I'd be grateful :)

If needed, here's what I've got for the setting on CSS.


function changeActiveStyleSheet(newCSS) {

  var links = document.getElementsByTagName('link');

  for (var i = 0; i<links.length; i++) {
       if (links[i].getAttribute('rel').indexOf('style')> -1 && links[i].getAttribute('title')) {
        links[i].disabled = true;
        if (links[i].getAttribute('title') == newCSS) {
         links[i].disabled = false;
         createCookie('style', newCSS, '700');  
         //createCookie('style2', newCSS, '700');
        // location.reload();
         }
      }
  }
  //alert(links[0].disabled)
  // alert(links[1].disabled)
  // alert("You chose to change to '" + newCSS + "'\r\nThere are " + links.length + " stylesheets according to your browser"); 
}

Do you have the createCookie() and the cookie reading functions? That block looks fine; it'll call the cookie function successfully in any browser, so it must be the setting or reading of the cookies that's causing an issue?

  • Author

Thanks, I do indeed.

That's what I don't get though, when checking the cookies stored (only within FF, granted) it's showing up as you'd except.

As I'm sure you can tell by the quality of this code, coding isn't my forté! (My degree is meant to be in low level networks)


function createCookie(name,value,days) {
     if (days) {
       var date = new Date();
       date.setTime(date.getTime()+(days*24*60*60*1000));
       var expires = "; expires="+date.toGMTString();
     }
     else expires = "";
     document.cookie = name+"="+value+expires+"; path=/";
}

function getCookie() {
 var links = document.getElementsByTagName('link');
//  alert(links[0].title)
// var nameEQ = name + "=";
 var ca = document.cookie.split(';'); 
//  alert(ca.length)////////////////////////////////////////////////////////////////////////
 for(var i=0;i < ca.length;i++) {
   var c = ca[i];
//    alert(c)
//    alert(c == " style=lightweight" )/////////////////////////////////////////////////
   if (c == " style=lightweight") {
     links[0].disabled = true;  //default = 0
     links[1].disabled = false; //lightweight = 1
//      alert("lightweight selected");   
   }  
   if (c == " style=default") {
     links[1].disabled = true;
     links[0].disabled = false;   //default = 0
//     alert("default selected");
   }
 }
}

I'm aware it's not a good, scalabile bit of code, but for what I need it suffices.

Again, thanks for your help :)

You have a function for changing the CSS already, so I wouldn't be setting it in the getCookie function - call to the changeActiveStyleSheet function instead.

Not sure why your cookie is starting with a space (when checking var c)? As the createCookie() call just passes 'style' so there shouldn't be a space.

I'd probably use three separate functions:

1 to set the CSS to whaever you want

1 to set the cookie after it's chosen (however it's chosen)

1 to read the cookie onload (and then call the first function to set it if you have a cookie already)

Something like:

 function changeActiveStyleSheet(newCSS) {

  var links = document.getElementsByTagName('link');
  for (var i = links.length - 1; i >= 0; i--) {
   if (links[i].getAttribute('rel').indexOf('style')> -1 && links[i].getAttribute('title')) {
 links[i].disabled = true;
 if (links[i].getAttribute('title') == newCSS) {
  links[i].disabled = false;
  createCookie(newCSS, 7);
 }
   }
  }
 }

 function createCookie(chosenCSS, expireInDays) {

  if (expireInDays) {
   var expireLength = new Date();
   expireLength.setTime(expireLength.getTime() + (expireInDays*24*60*60*1000));
   var expire = "; expires="+expireLength.toGMTString();
  } else {
   var expire = "";
  }
  document.cookie = "styleChoice=" + escape(chosenCSS) + expire +"; path=/";

 }

 function setupStyles() {
  if (document.cookie) {
   var myCookies = document.cookie.split(';');
   for(var i=0;i < myCookies.length;i++) {
 if (myCookies[i].indexOf("styleChoice") == 0) changeActiveStyleSheet(myCookies[i].substring(12, myCookies[i].length));
   }
  }

 }

 window.onload = setupStyles();

Then all you need to do is call:

changeActiveStyleSheet('default');

When they select a stylesheet (obviously, change 'default' to whatever the choice is).

Not sure I can debug your code without seeing the context I'm afraid. But it looks like your getCookie() is altering the enabled/disabled stylesheets directly, so may be crossing wires with changeActiveStyleSheet() depending on where the calls are? Or it may be that Firefox is stripping your spaces when checking the c variable, where Chrome is doing it properly and looking for a space that isn't there (ie both IFs are returning false so nothing is happening).

  • Author

Just to update this, I got it working. :)

It was as you pointed out down to the leading space in the cookie name. Not sure what was going on when I decided the space was needed, but there we go. I also had to add a reference back into document.styleSheets[*].disabled to keep IE happy, but otherwise it's working again now.

Again, thanks for your help, it was/is appreciated! :D

Create an account or sign in to comment

Recently Browsing 0

  • No registered users viewing this page.

Important Information

Welcome to BRISKODA. Please note the following important links Terms of Use. We have a comprehensive Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.

Account

Navigation

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.