Solved

UL and Nested UL Height

Posted on 2010-11-22
8
1,316 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
  • 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
 

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
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 

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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Using SQL Scripts we can save all the SQL queries as files that we use very frequently on our database later point of time. This is one of the feature present under SQL Workshop in Oracle Application Express.
Envision that you are chipping away at another e-business site with a team of pundit developers and designers. Everything seems, by all accounts, to be going easily.
This tutorial walks through the best practices in adding a local business to Google Maps including how to properly search for duplicates, marker placement, and inputing business details. Login to your Google Account, then search for "Google Mapmakerā€¦
The viewer will learn how to dynamically set the form action using jQuery.

758 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

20 Experts available now in Live!

Get 1:1 Help Now