Solved

UL and Nested UL Height

Posted on 2010-11-22
8
1,326 Views
Last Modified: 2012-05-10
All,

Kindly refer to the attached jpg with the listed problem included and review the script of HTML and jquery in order to investigate the root cause.

Basically, my design is to enable the expand/collapse of every UL via the DIV and IMG elements.
It's working properly if I do not have any nested UL elements within.

So now my question how to make it works with the current script attached when introduce the nested UL element in. I'm looking and trying not to make use of any CSS.
I'm thinking that need to address the height issue of nested lists in the jquery instead.

Please advice. Thanks.

Attached of the outcome of the script together with problem listed
<script type="text/javascript">
$(document).ready(function () { 
 // Find the heights of all the ul elements to be 
 // used  later during toggle

 ulHeights = [];
 var toggleElements = $('.toggle');
 //map's id attribute value as key to ul css height
 ulHeights = {}; 

 toggleElements.each(function () {
  var ul = $(this).parent().find('ul');
  var id = ul.attr('id');
  if (!ulHeights.hasOwnProperty(id)) {
   ulHeights[id] = ul.height();
   //ul.height(0);
  }
 });
	
 var animationDuration = 500;
 function toggleOnClick() {
  var ul = $(this).parent().find('ul')
  var img = $(this).parent().find('img')

  if (ul.height() == 0) { //expand
   ul.children('li').children('div').children('ul')
    .each(function(i) {
    $(this).attr('src', 'arrow_down.gif');
    $(this).animate(
     {height:ulHeights[$(this).attr('id')]}, 
     {duration: animationDuration});
   });
			
   img.attr('src', 'arrow_down.gif');
   ul.animate(
    {height: ulHeights[ul.attr('id')]}, 
    {duration: animationDuration});
  }
  else { //collapse
   ul.children('li').children('div').children('ul')
    .each(function(i) {
    $(this).attr('src', 'arrow_right.gif');
    $(this).animate({height: 0}, 
     {duration: animationDuration}); 
    });

   img.attr('src', 'arrow_right.gif');
   ul.animate({height: 0}, {duration: animationDuration});
  }
 }
 toggleElements.click(toggleOnClick);
});
</script>

Open in new window

<DIV style="PADDING-TOP: 10px">
 <IMG class=toggle src="/goldarrow_right.gif"> 
 <A style="COLOR: #a29061" class="generic-header2 
  toggle plain" href="" name=section1>Section 1</A> 
 <UL style="MARGIN: 0px; OVERFLOW: hidden" id=0>
  <LI>
   <DIV style="PADDING-TOP: 10px">
    <IMG class=toggle src="/goldarrow_right.gif"> 
    <A style="COLOR:#a29061" class="generic-header2 
     toggle plain" href="" name=section2>
     Sub Section  1.0</A> 
    <UL style="MARGIN: 0px; OVERFLOW: hidden" id=1>
      <LI><A class=link href="" target=_blank>
       Content 1</A> </LI> 
      <LI><A class=link href="" target=_blank>
       Content 2</A> </LI>
    </UL>
   </DIV>
  </LI>
  <LI><A class=link 
    href="" 
    target=_blank>ABC</A> 
  </LI>
    <LI><A class=link 
    href="" 
    target=_blank>DEF</A> 
    </LI>
 </UL>
</DIV>

Open in new window

0
Comment
Question by:wintersun
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 4
8 Comments
 
LVL 18

Expert Comment

by:Sudaraka Wijesinghe
ID: 34195061
From what I understand your problem is when you collapse and expand the list using the top most toggle IMG, the items ABC and DEF runs out of view port.

This happens because of one of the element selections in your code
var ul = $(this).parent().find('ul')

Open in new window

Here you are selecting all the UL elements inside the main DIV, your have 2, the main UL which contains all the list items, and the UL with in that main UL which contains ABC and DEF.
so the value get assigned to your variable ul is an array of 2 elements.

Therefor, in the code (line 35 in the code you posted)
ul.animate(
    {height: ulHeights[ul.attr('id')]}, 
    {duration: animationDuration});

Open in new window

it animates the two UL elements separately, and I believe you only need to animate the first of them.

So you can change it to only animate the first UL like this:
$(ul[0]).animate(
    {height: ulHeights[ul.attr('id')]}, 
    {duration: animationDuration});

Open in new window


Although this will work for your current HTML structure, if you nest more UL into this this might break again. So you will need to use this method and come up with an algorithm if you are planing to do that.

P.S.
Digging in to this code might also help you.
http://www.jstree.com/demo
0
 

Author Comment

by:wintersun
ID: 34195315
Yes, you're correct here as the root cause is from the line below:-
var ul = $(this).parent().find('ul');

I've fixed the code by changing to use 'children' instead and it resolves my issue:-
var ul = $(this).parent().children('ul');

BTW, I've a another question about to apply css on LI which contains nested UL (in this example is LI > DIV > UL -> Sub Section 1.0 has children of Content 1 and Content 2)

// reset nested list settings
$('ul > li:has(div > ul)').each(function ()  {
  $(this).css('list-style-image', '/goldarrow_right.gif', false)})');
  $(this).addClass('toggle');
  $(this).click(toggleOnClick);
})

After the above code executed,

<LI> -> THIS <LI> CHANGED AS HAS CHILDREN OF DIV > UL
   <DIV style="PADDING-TOP: 10px">
    <IMG class=toggle src="/goldarrow_right.gif">
    <A style="COLOR:#a29061" class="generic-header2
     toggle plain" href="" name=section2>
     Sub Section  1.0</A>
    <UL style="MARGIN: 0px; OVERFLOW: hidden" id=1>
      <LI><A class=link href="" target=_blank> -> BUT THIS <LI>  IS CHANGED AS WELL AS NOT MY INTENTION, DO YOU KNOW WHY
       Content 1</A> </LI>
      <LI><A class=link href="" target=_blank> -> BUT THIS <LI>  IS CHANGED AS WELL AS NOT MY INTENTION, DO YOU KNOW WHY
       Content 2</A> </LI>
    </UL>
   </DIV>
  </LI>
0
 
LVL 18

Expert Comment

by:Sudaraka Wijesinghe
ID: 34195632
I don't see any problem with that piece of code except for the syntax error on css line:

// reset nested list settings
$('ul > li:has(div > ul)').each(function ()  {
  $(this).css('list-style-image', '/goldarrow_right.gif', false)})'); // <-- Syntax error here
  $(this).addClass('toggle');
  $(this).click(toggleOnClick);
})

// Should be

// reset nested list settings
$('ul > li:has(div > ul)').each(function ()  {
  $(this).css('list-style-image', '/goldarrow_right.gif');
  $(this).addClass('toggle');
  $(this).click(toggleOnClick);
})

Open in new window

0
Do you have a plan for Continuity?

It's inevitable. People leave organizations creating a gap in your service. That's where Percona comes in.

See how Pepper.com relies on Percona to:
-Manage their database
-Guarantee data safety and protection
-Provide database expertise that is available for any situation

 

Author Comment

by:wintersun
ID: 34196223
I can't make it works if follow the code. Anyway, I've edit the way how I want it to render by placing an <IMG> and set <LI> that has <DIV > UL> .css('list-style-type', 'none').

But now I encountered another issue of the extra area on Main <UL> when the nested lists is collapse.
See attached "UL-LI-UL_collapse_blank_area.jpg".

Thanks.

Attach of the collapse issue
<DIV style="PADDING-TOP: 10px">
 <IMG class=toggle src="/plus.gif"> 
 <A style="COLOR: #a29061" class="generic-header2 
  toggle plain" href="">Main A</A> 
 <UL style="MARGIN: 0px; OVERFLOW: hidden" id=0>
  <LI>
   <DIV style="PADDING-TOP: 10px">
    <IMG class=toggle src="/plus.gif"> 
    <A style="COLOR:#a29061" class="generic-header2 
     toggle plain" href="">
     Sub A1</A> 
    <UL style="MARGIN: 0px; OVERFLOW: hidden" id=1>
      <LI><A class=link href="" target=_blank>
       Sub A1-1</A> </LI> 
      <LI><A class=link href="" target=_blank>
       Sub A1-2</A> </LI>
    </UL>
   </DIV>
  </LI>
  <LI>
   <DIV style="PADDING-TOP: 10px">
    <IMG class=toggle src="/plus.gif"> 
    <A style="COLOR:#a29061" class="generic-header2 
     toggle plain" href="">
     Sub A2</A> 
    <UL style="MARGIN: 0px; OVERFLOW: hidden" id=2>
      <LI><A class=link href="" target=_blank>
       Sub A2-1</A> </LI> 
      <LI><A class=link href="" target=_blank>
       Sub A2-2</A> </LI>
    </UL>
   </DIV>
  </LI>
  <LI><A class=link href="" target=_blank>Content A1</A></LI>
  <LI><A class=link href="" target=_blank>Content A2</A></LI>
 </UL>
</DIV>
<DIV style="PADDING-TOP: 10px">
 <IMG class=toggle src="/plus.gif"> 
 <A style="COLOR: #a29061" class="generic-header2 
  toggle plain" href="">Main B</A>
 <UL style="MARGIN: 0px; OVERFLOW: hidden" id=3>
 <LI><A class=link href="" target=_blank>Content B</A></LI></UL></DIV>

Open in new window

0
 

Author Comment

by:wintersun
ID: 34196275

<script type="text/javascript">
$(document).ready(function () {
		
 //Find the heights of all the ul elements to be 
 // used later during toggle
 ulHeights = [];
 var toggleElements = $('.toggle');
 //map's id attribute value as key to ul css height
 ulHeights = {}; 

 toggleElements.each(function () {
  var ul = $(this).parent().find('ul');
  var id = ul.attr('id');
  if (!ulHeights.hasOwnProperty(id)) {
   ulHeights[id] = ul.height();
  }
 });	

 var animationDuration = 500;
 function toggleOnClick() {
  var ul = $(this).parent().children('ul');
  var img = $(this).parent().children('img');

  ul.children('li').children('div').children('ul')
    .each(function() {
   $(this).parent().children('img').attr('src','/plus.gif');
   $(this).animate({height: 0}, {duration: 0});
  });  

  if (ul.height() == 0) { 
    img.attr('src', 'minus.gif');	
    ul.animate({height: ulHeights[ul.attr('id')]}, 
      {duration: animationDuration});
  } else {
    img.attr('src', 'plus.gif');	
    ul.animate({height: 0}, {duration: animationDuration});
  }
 }
 toggleElements.click(toggleOnClick);

 // reset nested list settings
 $('ul > li:has(div > ul)').css('list-style-type', 'none');

 $('ul > li > div').animate({marginLeft: '-15px'});
 
 $('ul').height(0);	
				
});
</script>

Open in new window

0
 
LVL 18

Accepted Solution

by:
Sudaraka Wijesinghe earned 500 total points
ID: 34196456
Gap is caused by the fixed height of the UL. Try setting it to auto (or to the desired height) after the animation is completed.

<script type="text/javascript">
$(document).ready(function () {
                
 //Find the heights of all the ul elements to be 
 // used later during toggle
 ulHeights = [];
 var toggleElements = $('.toggle');
 //map's id attribute value as key to ul css height
 ulHeights = {}; 

 toggleElements.each(function () {
  var ul = $(this).parent().find('ul');
  var id = ul.attr('id');
  if (!ulHeights.hasOwnProperty(id)) {
   ulHeights[id] = ul.height();
  }
 });    

 var animationDuration = 500;
 function toggleOnClick() {
  var ul = $(this).parent().children('ul');
  var img = $(this).parent().children('img');

  ul.children('li').children('div').children('ul')
    .each(function() {
   $(this).parent().children('img').attr('src','/plus.gif');
   $(this).animate({height: 0}, {duration: 0});
  });  

  if (ul.height() == 0) { 
    img.attr('src', 'minus.gif');       
    ul.animate({height: ulHeights[ul.attr('id')]}, 
      {
		  duration: animationDuration,
		  complete: function(){$(this).height('auto');}
		}
	);
  } else {
    img.attr('src', 'plus.gif');        
    ul.animate({height: 0}, {duration: animationDuration});
  }
 }
 toggleElements.click(toggleOnClick);

 // reset nested list settings
 $('ul > li:has(div > ul)').css('list-style-type', 'none');

 $('ul > li > div').animate({marginLeft: '-15px'});
 
 $('ul').height(0);     
                                
});
</script>

Open in new window

0
 

Author Closing Comment

by:wintersun
ID: 34198044
Sudaraka, thanks for your quick guidance.
0
 
LVL 18

Expert Comment

by:Sudaraka Wijesinghe
ID: 34198361
Glad to help. Thanks for the points.
0

Featured Post

Secure Your WordPress Site: 5 Essential Approaches

WordPress is the web's most popular CMS, but its dominance also makes it a target for attackers. Our eBook will show you how to:

Prevent costly exploits of core and plugin vulnerabilities
Repel automated attacks
Lock down your dashboard, secure your code, and protect your users

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

When it comes to write a Context Sensitive Help (an online help that is obtained from a specific point in state of software to provide help with that state) ,  first we need to make the file that contains all topics, which are given exclusive IDs. …
This article will inform Clients about common and important expectations from the freelancers (Experts) who are looking at your Gig.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)

631 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