1

I have got a working PHP of a theme change with a cookie attached that remembers the theme color when the user leaves the site. But I need to change this to javascript instead and still draws on the CSS file. How do I do this?

This is the php file I have

<html>
  <head>
    <?php
      //GET method to retrieve the theme for the user
      if (isset($_GET["theme"])) {
        if ($_GET["theme"] == 'blue') {
          // set theme to be blue by default unless the user has selected red or 
          yellow theme then change
          // once user has selected theme browser will remember for 1 year
          setcookie("theme", "blue", strtotime('+1 year'));
        } else if ($_GET["theme"] == 'red') {
          setcookie("theme", "red", strtotime('+1 year'));
        } else if ($_GET["theme"] == 'yellow') {
          setcookie("theme", "yellow", strtotime('+1 year'));
        }
        header("Location: index.php");
      }
      /* once user logs out. session is to rememebr the color chosen as above,
         logging out to location index.php */
      if (isset($_GET["logout"]) && $_GET["logout"] == 'true') {
        session_destroy();
        header("Location: index.php");
        die;
      }

      // use theme cookie 
      if (isset($_COOKIE["theme"])) {
        if ($_COOKIE["theme"] == "yellow") {
          /* if yellow theme is selected use stylesheet yellow
             otherwise if not select */
          red style sheet
          echo '<link rel="stylesheet" type="text/css" href="styles/styleyellow.css">';
        } else if ($_COOKIE["theme"] == "red") {
          echo '<link rel="stylesheet" type="text/css" href="styles/stylered.css">';
        } else {
          /* if yellow or red were not selected
             then set to default blue stylesheet theme */
          echo '<link rel="stylesheet" type="text/css" href="styles/styleblue.css">';
        }
      } else {
        echo '<link rel="stylesheet" type="text/css" href="styles/styleblue.css">';
      }
    ?>
  </head>
  <body>
    <?php
      /* once the user has logged in
         this shows options for the user to change theme colors. */
      if (isset($_SESSION["username"])) {
        echo '<div id="theme">';
        echo 'Set a Theme:<br>';
        echo '<a href="index.php?theme=blue">Blue (Default)</a><br>';
        echo '<a href="index.php?theme=red">Red</a><br>';
        echo '<a href="index.php?theme=yellow">Yellow</a><br>';
        echo '</div>';
      }
    ?>
  </body>
</html>
Fred Gandt
  • 4,217
  • 2
  • 33
  • 41
Nicola Court
  • 41
  • 3
  • 9

1 Answers1

0

With caveats

For JavaScript to access cookies, they MUST be set to httponly=false (the default). See PHP.net for more details; this is the basics:

httponly
When TRUE the cookie will be made accessible only through the HTTP protocol. This means that the cookie won't be accessible by scripting languages, such as JavaScript.

JS will not be able to read any previously set cookies that are set with httponly=true.

On page load

JavaScript cannot get the page ready before loading; it can only work on the content after it is parsed by the browser.

  • The first thing we need to do is watch for the readyState of the document; we need it to be at least "interactive" before we can set new or change existing content. Since we need to add a new stylesheet depending on user settings, we must wait for the browser to understand the DOM before trying to insert it.

  • Next we need to retrieve the cookies (if any) that are already set for that site. document.cookie returns a string of semicolon separated key=value pairs. We need to parse that string to find if one of them is a "theme", and if so get its value.

  • If no cookie has previously been set, and if there's no query-string giving us a theme=<color>, we need to default to "blue", and append a <link> to the document's <head>. We could use an XMLHTTPRequest for the resource and add the responseText to a <style> element instead, but the extra work would provide no benefit in this case.

  • Now we've set the stylesheet to the appropriate color, we just need to either set or update the cookie for the next time.

This script handles most of the work being done by your PHP above, but not anything to do with logging in or out, changing location or setting any HTML.

Simply add it to a <script> element in the <head> of the documents you wish to perform these actions, and you should be good to go.

Please feel free to ask about anything you don't understand, or tell me if anything doesn't work as desired; I'm happy to follow up with edits and/or discussion to assist.

( function( W, D ) {
    "use strict";
    const init = function() {
        var cookie_string = D.cookie,
            query_theme = /theme\=([a-z]+)/.exec( location.search ),
            style_sheet = D.createElement( "link" ),
            date = new Date,
            theme;
        if ( cookie_string ) {
            var cookie_array = cookie_string.split( ";" ), vs;
            cookie_array.forEach( function( v ) {
                vs = v.trim().split( "=" );
                if ( vs[ 0 ] === "theme" ) {
                    theme = vs[ 1 ];
                }
            } );
        }
        // prioritize: query string > cookie > default
        theme = !!query_theme ? query_theme[ 1 ] : theme || "blue";
        // add the stylesheet
        style_sheet.href = "styles/style" + theme + ".css";
        style_sheet.type = "text/css";
        style_sheet.rel = "stylesheet";
        D.querySelector( "head" ).appendChild( style_sheet );
        // set/update the cookie
        date.setDate( date.getDate() + 365 );
        D.cookie = "theme=" + theme +
            "; expires=" + date.toString().replace( /\+.*$/, "" );
    };
    if ( /^(?:interactiv|complet)e$/.test( D.readyState ) ) {
        init();
    } else {
        W.addEventListener( "DOMContentLoaded", init, false );
    }
} ( window, document ) );

Using the localStorage API

  • localStorage doesn't expire, so there's no need to handle the date as with cookies.

  • localStorage is ONLY accessible to the browser; the server serving the content will not have access to the values unless they are explicitly passed to it. The server can be aware implicitly which theme is being used by paying attention to the request for the relative .css from this PHP session.

This will prioritize the theme value if present in a query-string, then if not present, will check localStorage for a "theme" string, and if not found, will use the default string "blue".

( function( W, D ) {
    "use strict";
    const init = function() {
        var style_sheet = D.createElement( "link" ),
            query_theme = /theme\=([a-z]+)/.exec( location.search ),
            // prioritize: query string > localStorage > default
            theme = !!query_theme ? query_theme[ 1 ] :
                W.localStorage.getItem( "theme" ) || "blue";
        // set the current theme in localStorage
        W.localStorage.setItem( "theme", theme );
        // add the stylesheet
        style_sheet.href = "styles/style" + theme + ".css";
        style_sheet.type = "text/css";
        style_sheet.rel = "stylesheet";
        D.querySelector( "head" ).appendChild( style_sheet );
    };
    if ( /^(?:interactiv|complet)e$/.test( D.readyState ) ) {
        init();
    } else {
        W.addEventListener( "DOMContentLoaded", init, false );
    }
} ( window, document ) );

The localStorage API has a sister sessionStorage for temporary storage of session data.

Both localStorage and sessionStorage can store stringyfied JSON and, as such, handle complex data far more easily than cookies.

Fred Gandt
  • 4,217
  • 2
  • 33
  • 41
  • Hi, thanks so much for the help. I was half way there but seeing it complete with explanation has helped a lot. This works I just needed to make some minor tweaks with the folder path to tailor it to what I was doing – Nicola Court Jun 18 '17 at 21:25
  • Glad I could help. – Fred Gandt Jun 18 '17 at 23:34
  • @NicolaCourt - BTW: Make sure to add [the `async` attribute](https://developer.mozilla.org/en/docs/Web/HTML/Element/script#Attributes) to the ` – Fred Gandt Jun 18 '17 at 23:45
  • @NicolaCourt - I updated the code above to *fix* my erroneous logic. Previously the code prioritized any existing cookie to set the theme. I have changed it to prioritize the query string if present, since otherwise another mechanism must exist to rewrite the cookie if the user changes their mind. – Fred Gandt Jun 19 '17 at 04:20
  • This has been very helpful, but I've been told that using localStorage is a better way to go. I was wondering if it would be possible if you could please write the cookie so it uses local storage. I'm still learning lots about the coding world, your explanation made sense and helped me understand the cookie side of things better. If it is not too much trouble, It would be good for me to see the difference between this and the localstorage version. – Nicola Court Jun 20 '17 at 06:22
  • @NicolaCourt - added. Please let me know if I need to explain anything better. If my answer has proven useful, I would appreciate it if you could mark it as "accepted". Thanks. – Fred Gandt Jun 20 '17 at 07:29
  • This has worked perfectly, explanation makes perfect sense. Works in Chrome and firefox. It does not work in internet explorer though brings up a message 'Unable to get property 'setItem' of undefined or null reference'. Is this a common thing that can happen when using localstorage? – Nicola Court Jun 20 '17 at 07:56
  • Cross browser compatibility is the bane of web devs lives. My personal feelings are that we should develop for the future, and not the lowest denominator (historically IE, but Safari for desktop kinda sucks badly). However, my opinion doesn't help you, so I'll make adjustments to provide the fix. Sorry for the inconvenience. Will be ready in 30 mins or so. I have to do a little housework first. – Fred Gandt Jun 20 '17 at 08:09
  • @NicolaCourt - I just tested in IE (v. 11.0.9...) (won't work at all in IE v. < 9 ) and it worked fine. Are you running the script as part of a page being read as a file on your computer, or via HTTP? See [this question](https://stackoverflow.com/questions/23391353/localstorage-on-internet-explorer-not-working)'s accepted answer for why that might fail. What version of IE did you try? – Fred Gandt Jun 20 '17 at 08:19
  • All good I updated the browser and it works now. I never use IE, I was just as part of the testing. Thank you for helping me. I appreciate it. – Nicola Court Jun 20 '17 at 11:29
  • @NicolaCourt - NP :-) – Fred Gandt Jun 20 '17 at 11:30