views:

268

answers:

7

I've got a couple books that I'm reading on ajax, but still quite new. All the tutorials and these books have the ubiquitous examples of: an auto-populating search bar and an asynchronous form validator. Those are both great, but not what I'm looking for. Specifically, I'm wanting to click a button and switch the external css file in my header include. Is this possible? Well...I know it's possible, but how do you do it?

If there is a tutorial I have missed out there, please feel free to point me to that link. Thanks! Joel

PS: I have jQuery in this project, so if there is something built in from there, even better!

PPS: I'm realizing I have not included important information (don't shoot me!):

1) The final goal of this will be to have a user settings section where the user can click a radio button and decide the color scheme they are wanting to use for our app. So we will eventually have something like 8 different css styles to choose from. Not sure if this will alter the best method to achieve this.

2) The user is logging into their account and changing their setting there. I want their changes to 'stick' until they decide to change the stylesheet again. I can do this manually in mySQL as we have a table called stylesheets with the various user stylesheets numbered...so in actuality, what I'm needing to do is change that mySQL value asynchronously so the css is immediately loaded.

Again, sorry for the initial lack of information.

A: 

To add a new css file to a page just create a new <link> tag:

function addCss (url) {
    var s = document.createElement('link');
    s.rel = 'stylesheet';
    s.type = 'text/css';
    s.href = url;
    document.getElementsByTagName('head')[0].appendChild(s);
}

addCss('http://path/to/stylesheet.css');

To remove a css file from a page just remove the <link> to it:

function removeCss (search) {
    var css = document.getElementsByTagName('link');
    for (var i=0;i<css.length;i++) {
        var c = css[i];
        if (c.rel === 'stylesheet' || c.type === 'text/css') {
            if (c.href && c.href.match(search)) {
                c.parentNode.removeChild(c);
            }
        }
    }
}

// Remove all css that contains 'mycss_', can use regexp if necessary:
removeCss(/mycss_.*\.css/);
slebetman
I would add it to the head and not body, other then that, looks good.
Itay Moav
There's no reason apart from stylistic to add it to head instead of body. And since this is a live DOM nobody will notice the stylistic aspect without something like Firebug. It's simply easier to append it to body. Since there is no such thing as `document.head`, appending it to head is tedious: `document.getElementsByTagName('head')[0].appendChild(s)`
slebetman
Link elements are not allowed in the body. Depending on browsers to perform error recovery is not a good idea (even if the current crop happen to do so consistently in this particular case).
David Dorward
@David: Oops, missed that. Fixed code to comply with HTML spec.
slebetman
+7  A: 

The trick is to put an ID on the link to your CSS. Then you can manipulate it with JavaScript.

<link id="css1" href="normal.css" type="text/css" rel="stylesheet">

...

document.getElementById('css1').href = 'new.css';

(from http://www.webmasterworld.com/forum91/4554.htm)

eliah
+1 for simplicity!
Renesis
+1  A: 

It has nothing to do with Ajax. It has everything to do with JS and DOM manipulation (Some key words to search for tutorial).
I am using Mootools, which is a JS library and it has a built in function for that.
If doing it manually is your thing, then I would simply add a <link> element to the <head> or adjust the href attribute of an existing <link> element.

 <link rel="stylesheet" href="http://sstatic.net/so/all.css?v=6063" id='bobo'>
...
...
...
<script>document.getElementById('bobo').href="http://my.doamin.com/new.css";&lt;/script&gt;
Itay Moav
Will the getElementById act event if the element is in the header?
Ciwee
@Andre: yes it works even in the `<head>`
slebetman
@slebtman: thanks!
Ciwee
+2  A: 

Here's an example that uses jQuery.

<!DOCTYPE html>
<html>
    <head>
    <link rel="stylesheet" type="text/css" href="style1.css" />
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"
            type="text/javascript"></script>
        <script type="text/javascript">
            $(function(){
                $('#change-css').click(function(e){
                    e.preventDefault();
                    $('link[rel="stylesheet"]').attr('href', 'style2.css');
                });
            });
        </script>
    </head>
    <body>
        <a id="change-css" href="#">change css</a>
    </body>
</html>

The operative line is $('link[rel="stylesheet"]').attr('href', 'style2.css');. This finds any <link> tag that has rel="stylesheet" and changes its href attribute to style2.css.

Danny Roberts
A: 

A List Apart has a great (if not old) article explaining a good technique;

http://www.alistapart.com/articles/bodyswitchers/

Lachlan McDonald
+4  A: 

Stylesheet Switcher in jQuery.

In response to the 'newbie followup' comment, I will try to make it a little more instructional.

The page I was playing with to test on while writing can be found here.

Page Display

You're going to want to have your current stylesheet displayed in a <link> tag in the <head> of each of your pages. The <link> tag will need an id for reference later in JavaScript. Something like:

<?php 
  // Somewhere in the server side code, $current_stylesheet is read from the user's 
  // "preferences" - most likely from a database / session object
  $current_stylesheet = $user->stylesheet;
?>
<link href='<?php echo $current_stylesheet ?>' rel='stylesheet' type='text/css' id='stylelink' />

Changing the preference

Once you are displaying the users stylesheet, you need a way to change it. Create a <form> that will send a request to the server when the user changes their stylesheet:

<form method="GET" id="style_form" >
  <select name="stylesheet" id="styleswitch">
    <option value="css1.css">Black &amp; White</option>
    <option value="css2.css" selected="selected">Shades of Grey</option>
   </select>
   <input value='save' type='submit' />
</form>

Server Side

Now, without jQuery, submitting this form should GET (you could change it to POST if you like) stylesheet={new stylesheet} on the current page. So somewhere in your bootstrap / sitewide include file, you do a check for it, a php sample:

$styles = array(
  'css1.css' => 'Black &amp; White',
  'css2.css' => 'Shades of Grey',
);

if (!empty($_GET["sytlesheet"]) {
  // VALIDATE IT IS A VALID STYLESHEET - VERY IMPORTANT
  // $styles is the array of styles:
  if (array_key_exists($_GET["stylesheet"], $styles)) {
    $user->stylesheet = $_GET["stylesheet"];
    $user->save();
  }
}

Live Preview

At this point, you have a functioning styleswitcher for the lame people without javascript. Now you can add some jQuery to make this all happen a little more elegantly. You'll want to use the jQuery Form Plugin to make a nice ajaxForm() function, that will handle submitting the form. Add the jQuery and jQuery Form library to the page:

<script type='text/javascript' src='/js/jquery.js'></script>
<script type='text/javascript' src='/js/jquery.form.js'></script>

Now that we have the libraries included -

$(function() {
  // When everything has loaded - this function will execute:


  $("#style_form").ajaxForm(function() {
    // the style form will be submitted using ajax, when it succeeds:
    // this function is called:
    $("#thediv").text('Now Using: '+$('#styleswitch').val());
  });

  $("#styleswitch").change(function() {
    // When the styleswitch option changes, switch the style's href to preview
    $("#stylelink").attr('href', $(this).val());
    // We also want to submit the form to the server (will use our ajax)
    $(this).closest('form').submit();
  });

  // now that you have made changing the select option submit the form,
  // lets get rid of the submit button
  $("#style_form input[type=submit]").remove();
});
gnarf
Apologies for my newbee followup questions 1) So to complete that, I would need a button for the styleswitch, right? Can you give me a simple button example with the code? 2) Can I combine that saving ability with that first button? IE: ideally, the user will just click the button, the css will change, and they are done (or they click another color and then they're done).
Joel
I believe I have covered all the questions there... If you have any other followups, it might be worth asking a new question
gnarf
Thank you. I will spend some time with this tonight.
Joel
+1  A: 

You could also load both CSS files and preface the all of the selectors on the second file with a body classname.

body.secondsheet {}
body.secondsheet a {}
body.secondsheet hr {}

Then all you have to do is add/remove the "secondsheet" class to the body tag to switch stylesheets.

ChiperSoft