Solved

Help modifying expanding menu using javascript

Posted on 2012-03-12
11
234 Views
Last Modified: 2012-03-16
Hi, This web page has an expanding/collapsing menu on the left side and the menu items have links that use onClick to call a javascript function to switch out the image on the right side of the page in the mainContent div.  The sublinks and expanding/collapsing work fine, but I want the top menu items such as 'Introduction' and 'Assistive Technology' to also change the mainContent image when clicked on.    When I try to add a similar link on these top level menu items, then the sub-menu items won't expand.  

--------------------------- Javascript --------------------

function changeImg(img,w,h) 
{
var ipath = "url(" + img + ")" ;
var hstring = h + 'px';
var wstring = w + 'px';

document.getElementById("mainContent").style.backgroundImage = ipath;
document.getElementById("mainContent").style.width = wstring;
document.getElementById("mainContent").style.height = hstring;
}


// JavaScript Document
/* This script and many more are available free online at
The JavaScript Source :: http://javascript.internet.com
Created by: Travis Beckham :: http://www.squidfingers.com | http://www.podlob.com
version date: 06/02/03 :: If want to use this code, feel free to do so,
but please leave this message intact. (Travis Beckham) */

// Node Functions

if(!window.Node){
  var Node = {ELEMENT_NODE : 1, TEXT_NODE : 3};
}

function checkNode(node, filter){
  return (filter == null || node.nodeType == Node[filter] || node.nodeName.toUpperCase() == filter.toUpperCase());
}

function getChildren(node, filter){
  var result = new Array();
  var children = node.childNodes;
  for(var i = 0; i < children.length; i++){
    if(checkNode(children[i], filter)) result[result.length] = children[i];
  }
  return result;
}

function getChildrenByElement(node){
  return getChildren(node, "ELEMENT_NODE");
}

function getFirstChild(node, filter){
  var child;
  var children = node.childNodes;
  for(var i = 0; i < children.length; i++){
    child = children[i];
    if(checkNode(child, filter)) return child;
  }
  return null;
}

function getFirstChildByText(node){
  return getFirstChild(node, "TEXT_NODE");
}

function getNextSibling(node, filter){
  for(var sibling = node.nextSibling; sibling != null; sibling = sibling.nextSibling){
    if(checkNode(sibling, filter)) return sibling;
  }
  return null;
}
function getNextSiblingByElement(node){
        return getNextSibling(node, "ELEMENT_NODE");
}

// Menu Functions & Properties

var activeMenu = null;

function showMenu() {
  if(activeMenu){
    activeMenu.className = "";
    getNextSiblingByElement(activeMenu).style.display = "none";
  }
  if(this == activeMenu){
    activeMenu = null;
  } else {
    this.className = "active";
    getNextSiblingByElement(this).style.display = "block";
    activeMenu = this;
  }
  return false;
}

function initMenu(){
  var menus, menu, text, a, i;
  menus = getChildrenByElement(document.getElementById("menu"));
  for(i = 0; i < menus.length; i++){
    menu = menus[i];
    text = getFirstChildByText(menu);
    a = document.createElement("a");
    menu.replaceChild(a, text);
    a.appendChild(text);
    a.href = "#";
    a.onclick = showMenu;
    a.onfocus = function(){this.blur()};
  }
}

if(document.createElement) window.onload = initMenu;



-------- end of Javascript -----

---------Beginning of CSS ------

@charset "utf-8";
body  {
	font:Arial, Helvetica, sans-serif;
	font-size: 18px;
	background: #CCC;
	margin: 0; 
	padding: 0;
	text-align: center; /* this centers the container in IE 5* browsers. The text is then set to the left aligned default in 

the #container selector */
	color: #000000;
}
.twoColFixLt #container { 	
	width: 1100px;
	background: #FFFFFF;
	margin: 0 auto; /* the auto margins (in conjunction with a width) center the page */
	text-align: left; /* this overrides the text-align: center on the body element. */
}
.twoColFixLt #sidebar1 {
	float: left; /* since this element is floated, a width must be given */
	width: 300px;	
	padding: 15px 10px 15px 20px;
	background: #39C;
}
.twoColFixLt #mainContent { 	
	margin: 0 0 0 350px;
	padding: 0 20px 20px; 
	padding-bottom: 10px;
	position:relative;	
	background-repeat:no-repeat;
	background-position: left top;
	background-image:url(../DME/images/homepage.jpg);
	width: 684px;
	height: 684px;
} 
.fltrt { /* this class can be used to float an element right in your page. The floated element must precede the element it should 

be next to on the page. */
	float: right;
	margin-left: 8px;
}
.fltlft { /* this class can be used to float an element left in your page */
	float: left;
	margin-right: 8px;
}
.clearfloat { /* this class should be placed on a div or break element and should be the final element before the close of a 

container that should fully contain a float */
    clear:both;
    height:0;
    font-size: 1px;
    line-height: 0px;
}
ul#menu {
  width: 250px;
  list-style-type: none;
  border:none;
  margin: 0;
  padding: 0;
}

ul#menu ol {
  display: none;
  text-align: left;
  list-style-type: none;
  margin: 0;
  padding-left: 12px;
  padding-top: 3px;
  padding-bottom: 3px;
  padding-right: 0px;
}

ul#menu li, 
  ul#menu a {
  font-family: Arial, Helvetica, sans-serif;
  font-size: 20px;
  font-weight:bold;
  color:#000;
}

ul#menu li {
  line-height: 30px;
}

ul#menu ol li {
  border-bottom: none;
}

ul#menu ol li:before {
  content: ">";
}

ul#menu a {
  text-decoration: none;
  outline: none;
}

ul#menu a:hover {
  color: #FFF;
}

ul#menu a.active {
  color:#FFF;
}


------------ end of CSS ------------------------

------------ beginning of HTML -------------------

                         
<body class="twoColFixLt">

<div id="container">

  <div id="header">
	<h1 align="center">DME Guide</h1>
  </div>
  <div id="sidebar1">
    
<ul id="menu">
  <li> Introduction
    <ol>            
      <li> <a href="#" onclick="changeImg('images/introtext.jpg','684','684')">Overview</a>  </li>            	   
      <li> <a href="#" onclick="changeImg('images/committee.jpg','684','792')">Rehab Standards Committee</a></li>

    </ol>
  </li>
  <li>Assistive Technology
    <ol>
      <li> <a href="#">C1-3</a></li>
      <li> <a href="#">C4</a></li>
      <li> <a href="#">C5</a></li>
      <li> <a href="#">C6-T1</a></li>
    </ol>
  </li>
  <li>Bathing and Toileting
    <ol>
      <li> <a href="#">C1-4</a></li>
      <li> <a href="#">C5-6</a></li>
      <li> <a href="#">C7-T1</a></li>
      <li> <a href="#">T2-T12</a></li>
      <li> <a href="#">L1-S5</a></li>
    </ol>
  </li>

</ul>    
    
  <!-- end #sidebar1 --> </div>
  
  <div id="mainContent">
  </div>
	
  <br class="clearfloat" />

<!-- end #container --></div>

</body>
</html>

Open in new window

0
Comment
Question by:shepherdIT
  • 6
  • 5
11 Comments
 
LVL 38

Expert Comment

by:Tom Beck
ID: 37712823
What if you change line 98 from a.onclick = showmenu; to

a.onmouseover = showmenu;
a.onmouseout = showmenu;

Then, menus would expand on hover (the more common behavior) instead of on click thus freeing up the onclick event for the image change.
0
 

Author Comment

by:shepherdIT
ID: 37716455
I did what you said, and when I hover over 'Introduction', it expands the sub-items under 'Introduction' but when I try to move my pointer to the sub-items, they disappear so I can't even hover over them.  I'd prefer not to have the hover and just click links since it is a user guide.

I just would really like to have the onClick on the top level <li> tags  like the following:

  <ul id="menu">
  <li> <a href="#" onclick="changeImg ('images/homepage.jpg','684','684')">Introduction</a>
    <ol>            
      <li> <a href="#" onclick="changeImg('images/introtext.jpg','684','684')">Overview</a>  </li>                     
      <li> <a href="#" onclick="changeImg('images/committee.jpg','684','792')">Rehab Standards Committee</a></li>

    </ol>
  </li>

....but when I add the onclick on 'Introduction', the onclick works but the menu sub-items for 'Introduction' do not show anymore.
0
 
LVL 38

Accepted Solution

by:
Tom Beck earned 500 total points
ID: 37717318
Ok, that was one idea.

How about this? Change your showMenu function to this, filling in the correct background images you want to display in the main content:
function showMenu() {
  if(activeMenu){
    activeMenu.className = "";
    getNextSiblingByElement(activeMenu).style.display = "none";
  }
  if(this == activeMenu){
    activeMenu = null;
  } else {
    this.className = "active";
    getNextSiblingByElement(this).style.display = "block";
    activeMenu = this;
  }
  var liId = activeMenu.id;
  var img, width, height;
  switch(liId){
    case "Intr":
       img = "images/homepage.jpg";
       width = 684;
       height = 684;
       break;
    case "Assi":
       img = "images/whatever.jpg";
       width = 684;
       height = 684;
       break;
    case "Bath":
       img = "images/whatever.jpg";
       width = 684;
       height = 684;
       break;
    default:
       img = "images/default.jpg"; 
       width = 684;
       height = 684;
  }
  changeImg(img, width, height);
  return false;
}

Open in new window


Then add this line, a.id = text.textContent.substring(0, 4);, to the menuInit function that grabs the first four characters of the menu item's text and uses it as an id for the anchor so you have a unique identifier for the showMenu function.
function initMenu(){
  var menus, menu, text, a, i;
  menus = getChildrenByElement(document.getElementById("menu"));
  for(i = 0; i < menus.length; i++){
    menu = menus[i];
    text = getFirstChildByText(menu);
    a = document.createElement("a");
    menu.replaceChild(a, text);
    a.appendChild(text);
    a.id = text.textContent.substring(0, 4);
    a.href = "#";
    a.onclick = showMenu;
    a.onfocus = function(){this.blur()};
  }
}

Open in new window

0
 

Author Comment

by:shepherdIT
ID: 37726133
When I tried the last solution, I lost the pointer altogether and couldn't click on anything in the menu.

 FYI, I've changed my original code to use an inline image instead of the background image because I realized the background image wouldn't print.  So the menu looks like this:

<ul id="menu">
  <li> Introduction
    <ol>  
      <li> <a href="#" Overview</a>  </li>
      <li> <a href="#" onclick="document.getElementById('mainimage').src='images/committee.jpg'">Rehab Standards Committee</a> </li>
    </ol>onclick="document.getElementById('mainimage').src='images/introtext.jpg'">
  </li>
....</ul>

and the mainContent div looks like this:

  <div id="mainContent">
     <img src="images/homepage.jpg" name="mainimage" id="mainimage" border="0" align="left" />
  </div>

Again, I need to somehow call
      onclick="document.getElementById('mainimage').src='images/homepage.jpg'" when 'Introduction' is clicked on, before the menu sub-items expand.

Thanks for any input.
0
 
LVL 38

Expert Comment

by:Tom Beck
ID: 37726725
I incorporated your new idea of using inline images instead of background images. It required changing a few lines in the showMenu script that I posted earlier.
function showMenu() {
  if(activeMenu){
    activeMenu.className = "";
    getNextSiblingByElement(activeMenu).style.display = "none";
  }
  if(this == activeMenu){
    activeMenu = null;
  } else {
    this.className = "active";
    getNextSiblingByElement(this).style.display = "block";
    activeMenu = this;
  }
  var liId = activeMenu.id;
  var img;
 switch(liId){
    case "Intr":
       img = "images/homepage.jpg";
       break;
    case "Assi":
       img = "images/whatever.jpg";
       break;
    case "Bath":
       img = "images/whatever.jpg";
       break;
    default:
       img = "images/default.jpg";
  }
  document.getElementById('mainimage').src= img;
  return false;
}

Open in new window

It works perfectly (as did the original script). If it is not working for you, let's investigate that. If you are losing the pointer and are unable to click, you likely have a syntax error in your javascript. It's the javascript that creates the links for the top level items. If they are not successfully converted to links, then there is an error.

I'm confused when you say you want to call onclick "when 'Introduction' is clicked on, before the menu sub-items expand." Don't you want the image to change AND the menu expand with the same click? That's what my suggested script does.
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 

Author Comment

by:shepherdIT
ID: 37729208
I was able to get the behavior working the way I want by hardcoding a change to 'src' in the showMenu function (in 2 places), but I'm not sure how to determine which <li> is active so I can change the image accordingly.  I tried taking the first 4 letters of the link's text value like in your previous example but I couldn't get it to work.  I thought maybe I can check it by looking at id, so I've added id's on the top level <li> items.  Embedded is the most recent version.


--------------------------- Javascript --------------------

function changeImg(img,w,h) 
{
var ipath = "url(" + img + ")" ;
var hstring = h + 'px';
var wstring = w + 'px';

document.getElementById("mainContent").style.backgroundImage = ipath;
document.getElementById("mainContent").style.width = wstring;
document.getElementById("mainContent").style.height = hstring;
}


// JavaScript Document
/* This script and many more are available free online at
The JavaScript Source :: http://javascript.internet.com
Created by: Travis Beckham :: http://www.squidfingers.com | http://www.podlob.com
version date: 06/02/03 :: If want to use this code, feel free to do so,
but please leave this message intact. (Travis Beckham) */

// Node Functions

if(!window.Node){
  var Node = {ELEMENT_NODE : 1, TEXT_NODE : 3};
}

function checkNode(node, filter){
  return (filter == null || node.nodeType == Node[filter] || node.nodeName.toUpperCase() == filter.toUpperCase());
}

function getChildren(node, filter){
  var result = new Array();
  var children = node.childNodes;
  for(var i = 0; i < children.length; i++){
    if(checkNode(children[i], filter)) result[result.length] = children[i];
  }
  return result;
}

function getChildrenByElement(node){
  return getChildren(node, "ELEMENT_NODE");
}

function getFirstChild(node, filter){
  var child;
  var children = node.childNodes;
  for(var i = 0; i < children.length; i++){
    child = children[i];
    if(checkNode(child, filter)) return child;
  }
  return null;
}

function getFirstChildByText(node){
  return getFirstChild(node, "TEXT_NODE");
}

function getNextSibling(node, filter){
  for(var sibling = node.nextSibling; sibling != null; sibling = sibling.nextSibling){
    if(checkNode(sibling, filter)) return sibling;
  }
  return null;
}
function getNextSiblingByElement(node){
        return getNextSibling(node, "ELEMENT_NODE");
}

// Menu Functions & Properties

var activeMenu = null;

function showMenu() {
  if(activeMenu){
    document.getElementById('mainimage').src='images/homepage.jpg' ;
    activeMenu.className = "";
    getNextSiblingByElement(activeMenu).style.display = "none";
  }
  if(this == activeMenu){
    activeMenu = null;
  } else {
    document.getElementById('mainimage').src='images/homepage.jpg';
    this.className = "active";
    getNextSiblingByElement(this).style.display = "block";
    activeMenu = this;
  }
  return false;
}

function initMenu(){
  var menus, menu, text, a, i;
  menus = getChildrenByElement(document.getElementById("menu"));
  for(i = 0; i < menus.length; i++){
    menu = menus[i];
    text = getFirstChildByText(menu);
    a = document.createElement("a");
    menu.replaceChild(a, text);
    a.appendChild(text);
    a.href = "#";
    a.onclick = showMenu;
    a.onfocus = function(){this.blur()};
  }
}

if(document.createElement) window.onload = initMenu;



-------- end of Javascript -----

---------Beginning of CSS ------

@charset "utf-8";
body  {
	font:Arial, Helvetica, sans-serif;
	font-size: 18px;
	background: #CCC;
	margin: 0; 
	padding: 0;
	text-align: center; /* this centers the container in IE 5* browsers. The text is then set to the left aligned default in 

the #container selector */
	color: #000000;
}
.twoColFixLt #container { 	
	width: 1100px;
	background: #FFFFFF;
	margin: 0 auto; /* the auto margins (in conjunction with a width) center the page */
	text-align: left; /* this overrides the text-align: center on the body element. */
}
.twoColFixLt #sidebar1 {
	float: left; /* since this element is floated, a width must be given */
	width: 300px;	
	padding: 15px 10px 15px 20px;
	background: #39C;
}
.twoColFixLt #mainContent { 	
	margin: 0 0 0 350px;
	padding: 0 20px 20px; 
	padding-bottom: 10px;
	position:relative;	

} 
.fltrt { /* this class can be used to float an element right in your page. The floated element must precede the element it should 

be next to on the page. */
	float: right;
	margin-left: 8px;
}
.fltlft { /* this class can be used to float an element left in your page */
	float: left;
	margin-right: 8px;
}
.clearfloat { /* this class should be placed on a div or break element and should be the final element before the close of a 

container that should fully contain a float */
    clear:both;
    height:0;
    font-size: 1px;
    line-height: 0px;
}
ul#menu {
  width: 300px;
  list-style-type: none;
  border:none;
  margin: 0;
  padding: 0;
}

ul#menu ol {
  display: none;
  text-align: left;
  list-style-type: none;
  margin: 0;
  padding-left: 12px;
  padding-top: 3px;
  padding-bottom: 3px;
  padding-right: 0px;
}

ul#menu li, 
  ul#menu a {
  font-family: Arial, Helvetica, sans-serif;
  font-size: 20px;
  font-weight:bold;
  color:#000;
}

ul#menu li {
  line-height: 30px;
}

ul#menu ol li {
  border-bottom: none;
}

ul#menu ol li:before {
  content: ">";
}

ul#menu a {
  text-decoration: none;
  outline: none;
}

ul#menu a:hover {
  color: #FFF;
}

ul#menu a.active {
  color:#FFF;
}

div#preload { display: none; }


------------ end of CSS ------------------------

------------ beginning of HTML -------------------

                         
<body class="twoColFixLt">

<div id="container">

  <div id="header">
	<h1 align="center">DME Guide</h1>
  </div>
  <div id="sidebar1">
    
<ul id="menu">
  <li id="li_intro"> Introduction
    <ol>            
      <li> <a href="#" onclick="document.getElementById('mainimage').src='images/introtext.jpg'">Overview</a>  </li>
      <li> <a href="#" onclick="document.getElementById('mainimage').src='images/committee.jpg'">Rehab Standards Committee</a></li>
    </ol>
  </li>
  <li id="li_assist">Assistive Technology
    <ol>
      <li> <a href="#">C1-3</a></li>
      <li> <a href="#">C4</a></li>
      <li> <a href="#">C5</a></li>
      <li> <a href="#">C6-T1</a></li>
    </ol>
  </li>
  <li id="li_bath">Bathing and Toileting
    <ol>
      <li> <a href="#">C1-4</a></li>
      <li> <a href="#">C5-6</a></li>
      <li> <a href="#">C7-T1</a></li>
      <li> <a href="#">T2-T12</a></li>
      <li> <a href="#">L1-S5</a></li>
    </ol>
  </li>

</ul>    
    
  <!-- end #sidebar1 --> </div>
  
  <div id="mainContent">

     <img src="images/homepage.jpg" name="mainimage" id="mainimage" border="0" align="left" />

  </div>
	
  <br class="clearfloat" />

<!-- end #container --></div>

<div id="preload">
   <img src="images/homepage.jpg" />
   <img src="images/introtext.jpg" />
   <img src="images/committee.jpg" />	
</div>

</body>
</html>

Open in new window

0
 

Author Comment

by:shepherdIT
ID: 37729224
Sorry, I didn't see your last post before I posted.  I will take a look at it.
0
 
LVL 38

Expert Comment

by:Tom Beck
ID: 37729302
Putting ids in the top level <li>s is not going to help. You need ids in the anchor tags that are being created inside the top level <li>s by the init function. I have done this for you in ID: 37717318 on line 10 of the init function. The id of the anchor tag is set to the first four characters of the text in the <li>. Now you only need the showMenu function from ID: 37726725 to get the id of the active menu item's anchor tag to change the image according to the id.
0
 

Author Comment

by:shepherdIT
ID: 37731321
Okay I didn't see before that I was getting a javascript error '..textContent is null or not an object' and I am using IE 8 and I see from looking around that some versions of IE have a problem with textContent.  And when I tested it in Firefox, it worked!  So I need to find out what is an alternative to textContent I could use that would work in all browsers.  
  Also the image change was not occurring when I clicked the top-level menu item when the sub-menu items were already expanded.  The sub-menu items would collapse like normal but the image that was already there would stay.  To fix this, I just copied that same case statement code before the lines that collapse the menu.

function showMenu() {
  var liId;
  var img;
  
  if(activeMenu){
      liId = activeMenu.id;
      switch(liId) {
     	 case "Intr":
           img = "images/homepage.jpg";
           break;
         case "Assi":
           img = "images/homepage.jpg";
           break;
         case "Bath":
           img = "images/whatever.jpg";
           break;
         default:
           img = "images/homepage.jpg";
       }
     document.getElementById('mainimage').src= img;
	
     activeMenu.className = "";
     getNextSiblingByElement(activeMenu).style.display = "none";
  }
  if(this == activeMenu){
    activeMenu = null;
  } else {
         this.className = "active";
         getNextSiblingByElement(this).style.display = "block";
         activeMenu = this;
         }
  
  liId = activeMenu.id;
  switch(liId) {
   	 case "Intr":
       img = "images/homepage.jpg";
       break;
     case "Assi":
       img = "images/homepage.jpg";
       break;
     case "Bath":
       img = "images/whatever.jpg";
       break;
     default:
       img = "images/homepage.jpg";
     }
     document.getElementById('mainimage').src= img;
	
  return false;
}

Open in new window

0
 
LVL 38

Assisted Solution

by:Tom Beck
Tom Beck earned 500 total points
ID: 37731353
Try nodeValue.

a.id = text.nodeValue.substring(0, 4);
0
 

Author Comment

by:shepherdIT
ID: 37731380
Thank you SO much!  That did the trick!
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

International Data Corporation (IDC) prognosticates that before the current the year gets over disbursing on IT framework products to be sent in cloud environs will be $37.1B.
Nothing in an HTTP request can be trusted, including HTTP headers and form data.  A form token is a tool that can be used to guard against request forgeries (CSRF).  This article shows an improved approach to form tokens, making it more difficult to…
In this tutorial viewers will learn how to define a gradient in CSS. Create a new HTML document with an internal stylesheet.: Create a div in CSS and name it Gradient. Define the background as "linear-gradient(to right, #ee3668, black)". Ensure you …
In this tutorial viewers will learn how to embed custom externally-hosted Google Fonts using the Google Font API in CSS Go to the Google Fonts website at google.com/fonts: Browse or search based on font properties or name to find a suitable font for…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now