Javascript cookie for className's that =="shown"

I’m working on a menu script so what I got now is a script that is changing the class of an element on my page. I’ve got it working just the way I want it but now I’m having a hard time making a working cookie for it. I want to save all element with the class set to shown in the cookie. As I can’t use getElementByclassName I’m not sure how to pull this all together to save the ones that are set to shown. Any help would be great!

[code]
<style type="text/css">
.hidden {
      display:none;
}

.shown {
      display: block;
}
</style>
<script language="JavaScript" type="text/javascript">
function viewset(FieldID)
{
      layer = document.getElementById(FieldID);
      if (layer.className=="shown")
      {
            layer.className="hidden";
      }
      else
      {
            layer.className="shown";
      }
}
</script>
<body>
<a href="javascript:viewset('help1')">Help1</a><br />
<div class="hidden" id="help1">This is Help1!<br /></div>
<a href="javascript:viewset('help2')">Help2</a><br />
<div class="hidden" id="help2">This is Help2!<br /></div>
</body>
[/code]
Scan_25Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

SnowFlakeCommented:
there are a few ways you can go,
1) you can have a pre-ready list of id's and just scan throught them
    checking the value of className
2) you can just go over all the elements (using I think you can use getElementsByTagName('*')  or document.all in IE)

3) or scan elements recursively staring from a known parent element.

it all depelends on what you know about the structure of your menu.

also the above code fragment
     if (layer.className=="shown")
     {
          layer.className="hidden";
     }
     else
     {
          layer.className="shown";
     }

can be re-written
     layer.className=((layer.className=="shown")?"hidden":"shown");

SnowFlake
Scan_25Author Commented:
Well I would like to be able to NOT work from a pre list. I'm trying to come up with a way to get the elements by className. Your number two is what I've been trying but can't get to work right. Thanks for the short hand on that fragment. :)

Scan_25
Scan_25Author Commented:
If this helps this is what I've got trying to make and use the cookie now.

I think I have these two functions working right -

function readCookie(cname)
{
  var cookieValue = "";
  var search = cname + "=";
  if(document.cookie.length > 0)
  {
    offset = document.cookie.indexOf(search);
    if (offset != -1)
    {
      offset += search.length;
      end = document.cookie.indexOf(";", offset);
      if (end == -1) end = document.cookie.length;
      cookieValue = unescape(document.cookie.substring(offset, end))
    }
  }
  return cookieValue;
}

function getElementsByClassName(classname)
{
    var a = [];
    var re = new RegExp('\\b' + classname + '\\b');
    var els = document.all?document.all:document.getElementsByTagName("*");

    for(var i=0,j=els.length; i
        if(re.test(els[i].className))a.push(els[i]);
    return a;
}

now it this function I can't seem to get right

function writeCookie(cname)
{
        field = getElementsByClassName(shown);
        for (i=0; i <= field.length; i++)
        {
              if (field != null)
            openones = field + i
        }
  document.cookie=cname+"="+openones
}

But I don't understand how it needs to be written to the cookie so I’m sure that last function is all messed up.

Scan_25
Your Guide to Achieving IT Business Success

The IT Service Excellence Tool Kit has best practices to keep your clients happy and business booming. Inside, you’ll find everything you need to increase client satisfaction and retention, become more competitive, and increase your overall success.

SnowFlakeCommented:
function writeCookie(cname)
{
       field = getElementsByClassName(shown);
       for (i=0; i <= field.length; i++)
       {
            if (field != null)
          openones = field + i
       }
  document.cookie=cname+"="+openones
}

has a few errors in it:

1. assuming 'shown' is the actual class and not a variable with the class's name in it
the line
       field = getElementsByClassName(shown);
should have been
       field = getElementsByClassName('shown');

you can verify that by adding
alert(field.length);
in the line after it.

2.because field is an array of ELEMENTS (not IDs) as can be seen from:
   if(re.test(els[i].className))a.push(els[i]);
   you can not store them directly in the cookie.
   you can store thair IDs or Indai.
   
   this calls for some thinking:
   a. how will you later restore it ?
   b. what happens if the list of such elements in the page changes between save and restore.

  It is my oppinion that both Q's lead to the conclusion that you should use an ID for any such element
  and the its the ID's that should be stored.
  (which will also make restoring them more efficiant).

also note that  
   for(var i=0,j=els.length; i
        if(re.test(els[i].className))a.push(els[i]);
 
seems to be missing a ++)
SnowFlake.

Scan_25Author Commented:
I am using an ID as is but I want to be able to add to the easily. I don’t want to have the ID hard coded in the javascript. In my first viewset function I change the class by the ID then I want to save all id’s that have there class set to shown in the cookie. I guess it’s best to save the cookie with the window.onunload command then remove the cookie with something like window.close. You see I would like to save the ID’s class settings just for the visit. Your right it is missing the ++) sorry. I also want to say thanks for your help on this your really great.

--
Scan_25
SnowFlakeCommented:
you got me a bit confused with the whole window.close issue.
accress what event do you want to save the cookie ?
are you trying to do a "client side session cookie" namely maintain the cookie
only while its the same window even if the page navigates but clear it if the window is closed
and the reopened.

I can think of two approaches to this:
1) use expando properties of the window object ( has to be tested )
2) use a cookie with a short lifespan (say 1 min.)
    which you write onunload and read onload
    this will work as long as you don't leave pages that are aware of this cookie
    for more then that duration. but will also go across windows if you have a
    few open at the same time or close and open a new window withing the 1 min. period.

I think the first one is much better then the second. I will play with it and post soon.

have you got the extraction of the ID's to work ?
or do you still need help with that code as well ?

SnowFlake.

Scan_25Author Commented:
This is the java I have now to make and read the cookie but I’m just not smart enough to make it work.
function readCookie(cname)
{
  var cookieValue = "";
  var search = cname + "=";
  if(document.cookie.length > 0)
  {
    offset = document.cookie.indexOf(search);
    if (offset != -1)
    {
      offset += search.length;
      end = document.cookie.indexOf(";", offset);
      if (end == -1) end = document.cookie.length;
      cookieValue = unescape(document.cookie.substring(offset, end))
    }
  }
  return cookieValue;
}

function getElementsByClassName(classname)
{
    var a = [];
    var re = new RegExp('\\b' + classname + '\\b');
    var els = document.all?document.all:document.getElementsById("*");

    for(var n=0; n <= els.length; n++)
    {
        if(re.test(els[n].className))a.push(els[n]);
    }
    return a;
}

function writeCookie(cname)
{
      field = getElementsByClassName('shown');
      for (i=0; i <= field.length; i++)
      {
      if (field != null)
            openones = field + i;
      }
  document.cookie=cname+"="+openones
}

window.onunload=writeCookie(savethis)

*/ I really think I’m getting this all wrong but it’s ok I’m looking in to a brain transplant soon. ;) /*

if (readCookie(savethis) != '')
{
    var els = document.all?document.all:document.getElementsById("*")
      for (x=0; x<els.length; x++)
      {
            idarray[c]=els[x]
            c++;
      }

    var openresults=get_cookie(savethis).split(" ")
      for (i=0 ; i <= openresults.length ; i++)
      {
                idarray[openresults[i]].className="shown";
      }
}


Scan_25

thanks for your help and I'll up the points some more as I didn't know how far off I was.
SnowFlakeCommented:
o.k.
lets go over the functions and see what each of them does.

readCookie gets a cookie name and returns the value stored in that cookie,
this function seems o.k.
writeCookie mixes two things - actually writing a cookie and deciding what to write.
lets split the two (and I'll throw in another GetCookie as well):

function setCookie(name, value, expires, path, domain, secure) {
  var curCookie = name + "=" + escape(value) +
      ((expires) ? "; expires=" + expires.toGMTString() : "") +
      ((path) ? "; path=" + path : "") +
      ((domain) ? "; domain=" + domain : "") +
      ((secure) ? "; secure" : "");
  document.cookie = curCookie;
}

function getCookie(name) {
  var dc = document.cookie;
  var prefix = name + "=";
  var begin = dc.indexOf("; " + prefix);
  if (begin == -1) {
    begin = dc.indexOf(prefix);
    if (begin != 0) return null;
  } else
    begin += 2;
  var end = document.cookie.indexOf(";", begin);
  if (end == -1)
    end = dc.length;
  return unescape(dc.substring(begin + prefix.length, end));
}

getElementsByClassName returns an array of all the elements in the document that have
a specific class as part of thair className (I liked the usage of the regexp to handle elements
with multiple classes).


now lets look at the code that is just lying there:

   |  window.onunload=writeCookie(savethis)

this was probably trying to say:
"when the doument unloads store all the ids of the elements that have classname 'shown'"
it has a few problems:
1) savethis - trying to be the name of the cookie is missing the quotes around it.
    so it would be evaluated to null and passed as the cookie name.
2) onload should be assigned a function not a function result (unless the result is a function) is it is here.
    as formulated it will execute the writeCookie which will return null and thus will not set any handler
   to the onunload.

so it should have been:
     window.onunload=function(){writeCookie('savethis')}
only that we changes write cookie so that it now requires the value to be passed to it so lets create another helpfull function.

function getIdsOfElements(els){
    ids=[];
    for (var i=0; i<els.length; i++)
     {
          ids[i]=els[i].id;
     }
     return ids;
}

so now we can write
     window.onunload=function(){
          var els= getElementsByClassName('shown');
          var ids= getIdsOfElements(els);
          var strIDs =  ids.join(','); // to transform the array of ids to a comma separated string of ids
          alert('storing ' + strIDs); // this is to see what is about to be written and
                                             // can be removed when working o.k.
          setCookie('savethis',strIDs)
     }




*/ I really think I’m getting this all wrong but it’s ok I’m looking in to a brain transplant soon. ;) /*
// in many cases a regular brain + good eyes and ears are enough - I hope you have
// your eyes and ears open - maybe you will save the costs of the transplant ;)

now as for the following code that was trying to set the classname of the
elements with the previously stored ids to 'shown':

if (readCookie(savethis) != '')
{
    var els = document.all?document.all:document.getElementsById("*")
     for (x=0; x<els.length; x++)
     {
          idarray[c]=els[x]
          c++;
     }

    var openresults=get_cookie(savethis).split(" ")
     for (i=0 ; i <= openresults.length ; i++)
     {
              idarray[openresults[i]].className="shown";
     }
}

I will rewrite it as well:
// now using getCookie instead of readCookie and with quotes around the cookie name
// I will also first assign it to a variable so I will not have to run though getCookie more then once.
var strIDs=getCookie('savethis');
alert('restoring ' + strIDs); // this is to see what is about to be written and
                                      // can be removed when working o.k.
if (strIDs!='') {
   arrIds=strIDs.split(',');
   for (var i=0;i <= arrIds.length ; i++) {
      id=arrIds[i];
      el=document.getElementById(id);
      if (el) {
          // the following is better then
          // el.className='shown';
          // because it also handles elements that have multiple classes.
          el.className=el.className.replace('shown','');
          el.className=el.className + ' shown';
      }
   }
}

this should be all.
Note:
1) I did not run this code so it might have syntax errors
2) This code might have a problem with scenarios where multiple elements have the same id.

SnowFlake
Scan_25Author Commented:
with your code it doen't seem to be making the cookie i just keep getting resoring null and the error in explorer is strIDs is null or an object. I'll also show you what i have now after your changes.

<style type="text/css">
.hidden {
     display:none;
}

.shown {
     display: block;
}
</style>

<script language="JavaScript" type="text/javascript">
function viewset(FieldID)
{
     layer = document.getElementById(FieldID);
     layer.className=((layer.className=="shown")?"hidden":"shown");
}
</script>

<body>
<a href="javascript:viewset('help1')">Help1</a><br />
<div class="hidden" id="help1">This is Help1!<br /></div>
<a href="javascript:viewset('help2')">Help2</a><br />
<div class="hidden" id="help2">This is Help2!<br /></div>

<script language="JavaScript" type="text/JavaScript">
var strIDs=getCookie('savethis');
alert('restoring ' + strIDs); // this is to see what is about to be written and
                                      // can be removed when working o.k.
if (strIDs!='') {
   arrIds=strIDs.split(',');
   for (var i=0;i <= arrIds.length ; i++) {
      id=arrIds[i];
      el=document.getElementById(id);
      if (el) {
          // the following is better then
          // el.className='shown';
          // because it also handles elements that have multiple classes.
          el.className=el.className.replace('shown','');
          el.className=el.className + ' shown';
      }
   }
}

function setCookie(name, value, expires, path, domain, secure) {
  var curCookie = name + "=" + escape(value) +
      ((expires) ? "; expires=" + expires.toGMTString() : "") +
      ((path) ? "; path=" + path : "") +
      ((domain) ? "; domain=" + domain : "") +
      ((secure) ? "; secure" : "");
  document.cookie = curCookie;
}

function getCookie(name) {
  var dc = document.cookie;
  var prefix = name + "=";
  var begin = dc.indexOf("; " + prefix);
  if (begin == -1) {
    begin = dc.indexOf(prefix);
    if (begin != 0) return null;
  } else
    begin += 2;
  var end = document.cookie.indexOf(";", begin);
  if (end == -1)
    end = dc.length;
  return unescape(dc.substring(begin + prefix.length, end));
}

function getIdsOfElements(els){
    ids=[];
    for (var i=0; i<els.length; i++)
     {
          ids[i]=els[i].id;
     }
     return ids;
}

function getElementsByClassName(classname)
{
    var a = [];
    var re = new RegExp('\\b' + classname + '\\b');
    var els = document.all?document.all:document.getElementsById("*");

    for(var n=0; n <= els.length; n++)
    {
        if(re.test(els[n].className))a.push(els[n]);
    }
    return a;
}

window.onunload=function(){
      var els= getElementsByClassName('shown');
      var ids= getIdsOfElements(els);
      var strIDs =  ids.join(','); // to transform the array of ids to a comma separated string of ids
      alert('storing ' + strIDs); // this is to see what is about to be written and
                                            // can be removed when working o.k.
      setCookie('savethis',strIDs)
}
</script>
</body>


Did I miss anything?
SnowFlakeCommented:
no, you didn't - I did.

two changes are required:

1)

change
if (strIDs!='') {
to
if (strIDs && (strIDs!='')) {

and in getElementsByClassName
change the for loop from:
for(var n=0; n <= els.length; n++)
to
for(var n=0; n < els.length; n++)

and at least on my computer it then works.

SnowFlake
Scan_25Author Commented:
Now I have it restoring and storing the right stuff but it's not setting the class back to shown. Any idea's we are so that I can taste it. If change the
          el.className=el.className + ' shown';
to
          el.className= 'shown';

it works but I like the idea of multiple classes..

scan_25
Scan_25Author Commented:
It also doesn't seem to work with firefox is there any hope?
Scan_25Author Commented:
I changed this
         el.className=el.className.replace('shown','');
          el.className=el.className + ' shown';

to this
         el.className=el.className.replace('hidden','shown');
          el.className=el.className + ' shown';

and it works in IE but I would like it to work in Firefox also. I can't even find the cookie in firefox so it seems as if it doesn't make it.

Scan_25
SnowFlakeCommented:
lets start with the IE solution.
obviously I was wrong again and you are only partly right in your solution.

the reason I introduces the
    el.className=el.className.replace('shown','');
was to prevent the class name to be added when it already exists
but as you found out if you already have hidden and you add shown the result is not what we expected
so we want the two to be mutually exlusive.
that is why you are right in adding
   el.className=el.className.replace('hidden','shown');
but you should leave the other one as well.

so it should in all be

      if (el) {
          // hidden should be mutually exlusive with shown.
          el.className=el.className.replace('hidden','');
          // the following is better then
          // el.className='shown';
          // because it also handles elements that have multiple classes.
          el.className=el.className.replace('shown','');
          el.className=el.className + ((''+el.className).length)>0?' shown':'shown';
      }

note I also changed the last line so that if no className remains then it does not add an extra
space at the begining.

I will check up FF and post soon.
please confirm that it works for you with IE now.

SnowFlake

SnowFlakeCommented:
The problem with FF was that we wrongly tried to use getElementsById which does not exist
we should have used getElementsByTagName('*').

change it to getElementsByTagName('*') and firefox still does not work
but change it to:

function getElementsByClassName(classname)
{
    var a = [];
    var re = new RegExp('\\b' + classname + '\\b');

    var els;
    if (typeof(document.all)!='undefined') {
       els = document.all;
    }
    else {
       els =document.getElementsByTagName("*");
    }

    for(var n=0; n < els.length; n++)
    {
            if(re.test(els[n].className)) a.push(els[n]);
    }
   
    return a;
}

and it works O.K.

SnowFlake

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Scan_25Author Commented:
You know I had that by TagName and was as dumb to change it without you saying to. I'm sorry about that.

you so rock my little world.. LOL

We have just made a nice little simple menu system that is very easy to work with and runs on IE, Firefox and many others.

Well I guess I'll keep my own brain after all :)

Scan_25
SnowFlakeCommented:
your welcome, thanks for the grade and points.
SnowFlake
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
JavaScript

From novice to tech pro — start learning today.