SitePoint Sponsor

User Tag List

Results 1 to 9 of 9
  1. #1
    SitePoint Member
    Join Date
    Sep 2008
    Posts
    9
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Menu with tabs and using cookies

    Hi,
    My problem has to do with a menu with tabs. I can change a menu, using java, without changing the url, see this thread. Problem is the reload of the page. If menu 3 is shown and you refresh the page or click a link, the menu changes to default (=menu 1). I can't handle the cookies for containing menu 3 after reload.

    Can someone help me with this cookie problem?

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Sure thing. Use the cookie handling functions from this page
    http://www.quirksmode.org/js/cookies.html
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Member
    Join Date
    Sep 2008
    Posts
    9
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I tried to implement those cookie functions. My problem is, I haven't a clue about where and how to recall the functions.

    Do I have to make a function, which uses the cookie and changes the menu? I read the page on quirksmode.org/js/cookies.html twice and tried, but failed. Can someone help me?

    Machiel

    Here are my codes.

    Code for functions show and hide
    Code:
    function show(obj) {
    var el = document.getElementById(obj);
    el.style.display = 'block';
    }
    
    function hide(obj) {
    var el = document.getElementById(obj);
    el.style.display = 'none';
    }
    Code for show and hide menuobject
    Code:
    <div class="menutab01"><a class="menu" href="" onCLick="show('showmenu_01'); hide('showmenu_02'); hide('showmenu_03'); return false;">Menu 1</a></div>
    <div class="menutab02"><a class="menu" href="" onCLick="show('showmenu_02'); hide('showmenu_01'); hide('showmenu_03'); return false;">Menu 2</a></div>
    <div class="menutab03"><a class="menu" href="" onCLick="show('showmenu_03'); hide('showmenu_01'); hide('showmenu_02'); return false;">Menu 3</a></div>
    Code for the menuobject
    Code:
    <div id="showmenu_01" style="display:block"><div id="menu-01" class="sub-menu">
      <?php include('menu/menu-01.php') ?>
    </div></div>
    
    <div id="showmenu_02" style="display:none"><div id="menu-02" class="sub-menu">
      <?php include('menu/menu-02.php') ?>
    </div></div>
    
    <div id="showmenu_03" style="display:none"><div id="menu-03" class="sub-menu">
      <?php include('menu/menu-03.php') ?>
    </div></div>

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    You'll be needing to use the createCookie and readCookie functions.

    When you show a menu, you'll want to create a cookie that stores information about which menu is being shown.

    Code javascript:
    function show(obj) {
        var el = document.getElementById(obj);
        el.style.display = 'block';
        createCookie('menu', obj, 365);
    }

    When the page loads, you'll want to check what menu was last shown and show that one.

    There are better ways to organise the code, but with your current structure this should do fine.

    Code javascript:
    var menus = ['showmenu_01', 'showmenu_02', 'showmenu_03'],
        menu,
        i;
    if (readCookie('menu')) {
        for (i = 0; i < menus.length; i += 1) {
            menu = menus[i];
            if (menu === readCookie('menu')) {
                show(menu);
            } else {
                hide(menu);
            }
        }
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    SitePoint Member
    Join Date
    Sep 2008
    Posts
    9
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It's still not working, but I think I/we am/are allmost there.

    How must the link in the menu look like? The link I use is shown below.
    Code:
    <a href="" onCLick="show('showmenu-01');
    hide('showmenu-02');
    hide('showmenu-03');
    return false;">Menu 1</a>
    And is it neccesary to have the following code present?
    Code:
    <body onLoad="readCookie()">

  6. #6
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Imagine if you will a large blackboard. Someone is scraping their fingernails jaggedly across the board, from which is emanating teeth-shattering shrieks joy and destruction.

    That is what the sight of html encrusted with javascript does to me. I will come back to this if I recover by the end of the day.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  7. #7
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Let's move the javascript code out of the html element and into their own functions.

    Code javascript:
    function showMenu1() {
        show('showmenu_01');
        hide('showmenu_02');
        hide('showmenu_03');
        return false;
    }
    function showMenu2() {
        ...
    }
    function showMenu3() {
        ...
    }

    Code html4strict:
    <div class="menutab01">
        <a class="menu" href="" onclick="return showMenu1()">Menu 1</a>
    </div>
    <div class="menutab02">
        <a class="menu" href="" onclick="return showMenu2()">Menu 2</a>
    </div>
    <div class="menutab03">
        <a class="menu" href="" onclick="return showMenu3()">Menu 3</a>
    </div>

    It still means that there is a small amount of javascript in the code, but for now this will do fine.

    When the page loads and we read the cookie value, we're going to need to pass the menu number to some other function that will need to show and hide the menus again.

    What we can do is to replace the separate showmenu functions with one that's designed to take a menu number.

    Code javascript:
    function showMenu(num) {
    	var limit = 3,
    		i;
    	for (i = 1; i <= limit; i += 1) {
    		if (i === num) {
    			show('showmenu_0' + i);
    		} else {
    			hide('showmenu_0' + i);
    		}
    	}
    	return false;
    }

    That showMenu function can be use by both the menu tabs and when loading the page.

    Code html4strict:
    <div class="menutab01">
        <a class="menu" href="" onclick="return showMenu(1)">Menu 1</a>
    </div>
    <div class="menutab02">
        <a class="menu" href="" onclick="return showMenu(2)">Menu 2</a>
    </div>
    <div class="menutab03">
        <a class="menu" href="" onclick="return showMenu(3)">Menu 3</a>
    </div>

    When it comes to dealing with cookies, we want to set the cookie when we show a menu.

    Code javascript:
    createCookie('menu', i, 365);

    The trickier part is when we load the page. When we call the function with no menu number, what would work great is to instead get the cookie value.

    Code javascript:
    num = num || parseInt(readCookie('menu'));

    The parseInt is required because the menu number is stored in the cookie as a string value, but we want to use it as a numeric value.

    What though if this is the first time that someone is visiting the page. The page loads and runs the function with no number, there is no cookie value yet that has stored the last menu that was used, so num still remains undefined.

    We can specify another default value to use, should the others not be able to provide a useful result.

    Code javascript:
    num = num || parseInt(readCookie('menu')) || 1;

    If you want the above in long-hand, it is:

    Code javascript:
    num = num;
    if (!num) {
        num = parseInt(readCookie('menu'));
        if (!num) {
            num = 1;
        }
    }

    So now with the showMenu function and an onload event, the tab menus work as desired.

    Code html4strict:
    <div class="menutab01">
        <a class="menu" href="" onclick="return showMenu(1)">Menu 1</a>
    </div>
    <div class="menutab02">
        <a class="menu" href="" onclick="return showMenu(2)">Menu 2</a>
    </div>
    <div class="menutab03">
        <a class="menu" href="" onclick="return showMenu(3)">Menu 3</a>
    </div>

    Code javascript:
    function showMenu(num) {
    	var limit = 3,
    		i;
    	num = num || parseInt(readCookie('menu')) || 1;
    	for (i = 1; i <= limit; i += 1) {
    		if (i === num) {
    			show('showmenu_0' + i);
    			createCookie('menu', i, 365);
    		} else {
    			hide('showmenu_0' + i);
    		}
    	}
    	return false;
    }
    window.onload = function () {
    	showMenu();
    }

    I'll follow up with issues that the above mix of html and javascript can produce, and how unobtrusive javascript techniques can be used to improve on things.
    Last edited by paul_wilkins; Sep 24, 2008 at 20:25.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  8. #8
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    One of the first issues to be resolved is to separate the html, css and javascript.

    Code html4strict:
    <div class="menutab01">
        <a class="menu" href="" onclick="return showMenu(1)">Menu 1</a>
    </div>
    ...
    <div id="showmenu_01" style="display:block">
    	<div id="menu-01" class="sub-menu">
    		<h2>Menu 1</h2>
    		<ul>
    			<li>Item 1</li>
    			<li>Item 2</li>
    		</ul>
    	</div>
    </div>

    Currently there are style attributes in the code, which now serve a redundant purpose, as the script is performing the same job.
    The script code in the menu tabs should be removed completely and instead be attached when the page loads.
    The links should refer to a valid location for the benefit of web spiders and clients without javascript support.

    Code html4strict:
    <div id="menutabs">
        <div class="menutab01">
            <a class="menu" href="#showmenu_01">Menu 1</a>
        </div>
    </div>
    ...
    <div id="showmenu_01">
    	<div id="menu-01" class="sub-menu">
    		<h2>Menu 1</h2>
    		<ul>
    			<li>Item 1</li>
    			<li>Item 2</li>
    		</ul>
    	</div>
    </div>

    That leaves just the base html, so that someone without css or javscript capabilities can still use the page. It also means that any javascript changes occur in only the one place.

    With the style attributes removed you can then improve the script performance by moving it to the bottom of the body. This also frees you from using an onload technique, as the script, due to being at the bottom of the page, now has full access to the DOM without having to wait for the page to load.

    Instead of explicitly defining events for each menu tab, we can attach the events by starting from the first menu tab and walking through them to their end.

    Code javascript:
    var el = document.getElementById('menutabs'),
    	els = el.getElementsByTagName('a'),
    	i;
    for (i = 0; i < els.length; i += 1) {
    	els[i].onclick = showMenu;
    }

    When the showMenu function runs, we can get the menu number from the href of the link. All we're after is the last two digits of the fragment identifier, that being '#menutab_01'
    There can be a couple of different techniques to achieve this.
    One is to take the last two characters with '#menutab_01'.slice(-2)
    Another is to use a regular expression to get the first group of digits with '#menutab_01'.match(/\d+/)[0];

    Code javascript:
    function getNum(el) {
    	if (el.nodeType !== 1) {
    		return null;
    	}
    	fragId = el.getAttribute('href');
    	return parseInt(fragId.match(/\d+/), 10);
    }
    function showMenu() {
    	var	num = getNum(this) || parseInt(readCookie('menu'), 10) || 1,
    		el = document.getElementById('menutabs'),
    		els = el.getElementsByTagName('a'),
    		i;
    	for (i = 1; i <= els.length; i += 1) {
    		if (i === num) {
    			show('showmenu_0' + i);
    			createCookie('menu', i, 365);
    		} else {
    			hide('showmenu_0' + i);
    		}
    	}
    	return false;
    }
    var el = document.getElementById('menutabs'),
    	els = el.getElementsByTagName('a'),
    	i;
    for (i = 0; i < els.length; i += 1) {
    	els[i].onclick = showMenu;
    }

    One of the gotcha's with using parseInt is that it converts "08" to 0 and "09" to 1 because the leading zero makes parseInt think that it's dealing with octal numbers. This is why specifying a decimal number base with 10 is a good practice with parseInt.

    Now we can get rid of some duplication. There are two nearly identical loops, where the only difference is in the body of the loop. We can have a forEachMenuTab function and just provide it with a different function to run on each occasion.

    That leaves us with code that looks like:

    Code javascript:
    function show(obj) {
    	var el = document.getElementById(obj);
    	el.style.display = '';
    }
     
    function hide(obj) {
    	var el = document.getElementById(obj);
    	el.style.display = 'none';
    }
    function getNum(el) {
    	if (el.nodeType !== 1) {
    		return null;
    	}
    	fragId = el.getAttribute('href');
    	return parseInt(fragId.match(/\d+/), 10);
    }
    function forEachMenuTab(fn) {
    	var el = document.getElementById('menutabs'),
    		els = el.getElementsByTagName('a'),
    		i;
    	for (i = 0; i < els.length; i += 1) {
    		fn(els[i], i);
    	}
    }
    function showMenu() {
    	var	num = getNum(this) || parseInt(readCookie('menu')) || 1;
    	forEachMenuTab(function (el, i) {
    		if (i + 1 === num) {
    			show('showmenu_0' + (i + 1));
    			createCookie('menu', i, 365);
    		} else {
    			hide('showmenu_0' + (i + 1));
    		}
    	});
    	return false;
    }
    forEachMenuTab(function (el) {
    	el.onclick = showMenu;
    });
    showMenu();
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  9. #9
    SitePoint Member
    Join Date
    Sep 2008
    Posts
    9
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thank you very much for solving my problem. You're great!


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •