How can I position some divs inside an unordered list so they line up with the root element of the list?


Tags: jquery,css,html-lists

Problem :

I want to position all the divs to line up to the left on the same x coordinate so it looks nice.

Notice the picture below, how based on the number of nested categories the div (and its contents) show up at slightly different x coordinates. I need to have the div's line up at exactly the same x coordinate no matter how deeply nested. Note, the bottom most category always has a div for the content, but that div has to be situated inside the last < li >.

I am using an unordered list to display the menu and thought the best solution would be to grab the root category (Cat 2, and mCat1) and obtain their left offset using jquery, then simply use that value to update the positioning of the div...but I couldn't seem to get it to work just right. I would appreciate any advice or help that you are willing to give.

Heres the HTML

<ul id="nav>
  <li>Cat 2
     <ul>
        <li>sub cat2</li>
     </ul>
  </li>
  <li>mCat1
      <ul>
         <li>Subcat A
            <ul>
               <li>Subcat A.1
                  <ul>
                     <li>Annie</li>
                  </ul>
               </li>
            </ul>
          </li>
        </ul>
   </li>
 </ul>

Heres some jquery I tried (I have do insert the div inside this .each() loop in order to retrieve some values, but basically, this selector is grabbing the last < li > in the menu tree and placing a div after it and that is the div that I want to position. the 245 value was something I was playing around with to see how I could get things to line up, and I know its out of wack, but the problem is still the same no matter what I do:

$("#nav li:not(:has(li))").each(function () {
                var self = $(this);
                self.after( '<div id="' + self.attr('p_node') + '_p_cont_div" class="property_position" style="display:none;" /> ' );
        });

        //ROOT - Retrieve the position of the root element in order to place the div in the right spot
        var p = $("#nav li:first");
        var position = p.offset();
        var xbaseLeft = Math.round(position.left);
        $("#nav li:not(:has(li))>div").css('left', xbaseLeft);

Heres the css:

.property_position{
float:left;
position: relative;
top: 0px;
padding-top:5px;
padding-bottom:10px;
  }

/*  MENU NAVIGATION (<UL><LI> LISTS
****************************************/
ul#nav{
/* This handles the main root <ul> */
margin-left:0;
padding-left:0px;
text-indent:15px;   
}
ul#nav div{
  overflow: hidden;
}

#nav li>a:hover { 
    cursor: pointer;
}
#nav ul { 
/* This handles any nested <ul>'s inside an id of "#nav" */
display:none; 
margin-left:0px; 
padding-left:15px;
text-indent:15px;

margin:0px;
}
#nav li {
list-style-type:none;
vertical-align: top;
list-style-image: none;
left:0px;
text-align:left;
clear: both;

margin:0px;
}

alt text



Solution :

Well, for anyone who comes along and can't seem to find an answer through plain css, I seemed to have found and answer. I was able to accomplish this with some good 'ol math and javascript, with jqueries help of course.

Here's what I did:

  1. assign a "level" attribute to your unordered list: < ul > < li level=0 >, etc.
  2. assign a click event to the bottom most node in the list. In my case I chose to tie this to the , which is a child to the last < li >
  3. Retrieve the left coordinate (.offset().left) of the topmost (root)
  4. Assign a base left margin for the root elements (those that don't have children), which in my presentation I wanted 10px from the left, which would line the boxes up starting with the left edge exactly under the folder icon.
  5. Loop through the levels for the nested < li >'s and do some math to figure out how far from the BASE of the root element you were. In my case, the css for the li's was using an indentation of 15px, so I needed to subtract by a factor of 15 for each level deep in order to get it to line up with the menu items that had no children. For example, if you have a level 3 element I would need to subtract 10-45 = -35, thus my left edge needs to be "-35px" in order for it to match the root element, if its a level 1 nesting, it would be 10-15 = -5px for the left edge.
  6. Add the new pixel position to the css for the chosen element; in my case I am placing this into a div that I previously assigned an id I could easily select.
  7. Sit back and smile, because the dang thing finally worked.
  8. Try to share your excitement with the wife and kids.....then the smile wears off when you remember they have no clue what your doing anyway....and they say..."uh, I guess that's cool dad...you got some boxes to line up...uh, am I supposed to congratulate you? When are you going to make something cool like an xBox or a Wii game!". Dang 7 year old!

Here's the code that did the trick:

$('#nav li:not(:has(li))>a').toggle(function() { //1st click
     //show div and content

var parent_offset = $(this).parents("li:last");
var level = $(this).parent().attr('level'); //Retrieve the level for the li of the clicked anchor
var position = parent_offset.offset(); //Obtain the left coordinate of the root li in the <ul> for the clicked anchor
var xBase=10, xbaseLeft;
if(level == 0) // Root node and is the base left position we want to establish, which is 10px
{
    xbaseLeft = xBase;
}
else // Subtract 15px based on how deep the nested anchor is. keep adding 15 for multiple levels deep in order to line up the box with the root boxes
{
    var xPx=0, aa=1, xPs;
    for(aa; aa<=level; aa++)
    {
        xPx += 15; //Add 15 to represent the pixel indentation fromt the css for the ul and li's
        console.log("xPx", xPx);
    }
    xbaseLeft = (xBase-xPx) + "px";
}

var p_id = $(this).parent().attr('p_node');
var p_name =  p_id + '_p_cont_div';

$("#"+p_name+"").css("left", xbaseLeft);

}, function() { //2nd click
    //Hide div and content

}); //END TOGGLE

    CSS Howto..

    How to stick a div when scrolled past 50% of the item?

    How can you remove the table-tags in CreateUserWizard control

    How do I make a class/div behave like it were floating?

    How do I make this CSS work to have semi-transparent border?

    How to include CSS in laravel 5 running with artisan?

    How to point to CSS default class using the class attribute

    How to center my main navigation bar on my site with css?

    How do I align form inputs nicely on top of a picture?

    How to make padding look the same in moz and webkit browsers?

    How to align a

    into

    or when content it is dynamic?

    Updating CSS via plain JavaScript… how do I update if the property uses vendor prefixes?

    Jquery/CSS - how to clear a CSS-background set in another file I can't edit

    Border display issue, not sure how to resolve

    How to style the div to triangle using CSS? [duplicate]

    How do people make sections in HTML pages? [closed]

    how to remove elements in document fragment after append

    How to wrap text of html button with fixed width

    How to replace @media (max-width) using Stylish or Greasemonkey?

    How to turn off spell checking in CSS?

    How to Protect an HTML element from it's respective assigned CSS

    How to use absolute element when its closet ancestor is a float element?

    Angularjs how to add/remove dynamically input form with ng-Repeat on Google Maps?

    Dropdown Menu doesn't show all links and can't change css social media icons color

    How to add multiple style rules to css pseudo elements using js?

    How to make float navbars with JS and CSS?

    Issue with CSS background image (Image not Showing)

    How to compile or convert sass / scss to css with node-sass (no Ruby)?

    How to detect mouse is over a child element in jQuery?

    How to remove the gap between each list-item in an unordered list?

    CSS how to make a fixed height?