简体   繁体   中英

Javascript left and top nav active current states

Need js help on this one. Two navs on one page, one side nav and one top nav working together. Side nav active state and top nav current state is highlighted when clicked and selected, however the side nav active/selected state is removed when the second and third top nav link is clicked.

Here is the HTML:

<div id="nav">
<ul>
  <li class="active"><a href="#SideNav1">Side Nav 1</a></li>
  <li><a href="#SideNav2">Side Nav 2</a></li>
</ul><br />

<div id="SideNav1">
  <ul>
      <li class="current">Side Nav 1 Top Nav 1</li>
      <p>Content here</p>  <br />                    
      <li><a href="#SideNav1TopNav2">Side Nav 1 Top Nav 2</a></li>  
      <p>Content here</p>  <br />              
      <li><a href="#SideNav1TopNav3">Side Nav 1 Top Nav 3</a></li>
      <p>Content here</p>    <br />             
    </ul>    
</div>

    <div id="SideNav2">
  <ul>
      <li class="current">Side Nav 2 Top Nav 2</li>
      <p>Content here</p>   <br />                  
      <li><a href="#SideNav2TopNav2">Side Nav 2 Top Nav 2</a></li>  
      <p>Content here</p>  <br />              
      <li><a href="#SideNav3TopNav3">Side Nav 2 Top Nav 3</a></li>
      <p>Content here</p>   <br />              
    </ul>    
</div>

    <div id="SideNav1TopNav2">
  <ul>
      <li><a href="#SideNav1">Side Nav 1 Top Nav 1</a></li>
      <p>Content here</p>  <br />                   
      <li class="current">Side Nav 1 Top Nav 2</li>  
      <p>Content here</p> <br />               
      <li><a href="#SideNav1TopNav3">Side Nav 1 Top Nav 3</a></li>
      <p>Content here</p>    <br />             
    </ul>    
</div>

    <div id="SideNav1TopNav3">
  <ul>
      <li><a href="#SideNav1">Side Nav 1 Top Nav 1</a></li>
      <p>Content here</p>    <br />                 
      <li><a href="#SideNav1TopNav3">Side Nav 1 Top Nav 2</a></li>  
      <p>Content here</p>  <br />              
      <li class="current">Side Nav 1 Top Nav 3</li>
      <p>Content here</p>   <br />              
    </ul>    
</div>

    <div id="SideNav2TopNav2">
  <ul>
      <li><a href="#SideNav2">Side Nav 2 Top Nav 1</a></li>
      <p>Content here</p>   <br />                  
      <li class="current">Side Nav 2 Top Nav 2</li>  
      <p>Content here</p>   <br />             
      <li><a href="#SideNav2TopNav3">Side Nav 2 Top Nav 3</a></li>
      <p>Content here</p>   <br />              
    </ul>    
</div>

    <div id="SideNav2TopNav3">
  <ul>
      <li><a href="#SideNav2">Side Nav 2 Top Nav 1</a></li>
      <p>Content here</p>    <br />                 
      <li><a href="#SideNav2TopNav3">Side Nav 2 Top Nav 2</a></li>  
      <p>Content here</p>    <br />            
      <li class="current">Side Nav 2 Top Nav 3</li>
      <p>Content here</p>     <br />            
    </ul>    
</div>

</div>

Here is the js:

$(function() {
$('#nav div').hide();
$('#nav div:first').show();
$('#nav ul li:first').addClass('active');
$('#nav ul li a').click(function(){
    var currentTab = $(this).attr('href');
    var vis = $(currentTab).is(':visible');
    $('#nav div').hide();
    $('#nav ul li').removeClass('active');
    $(this).parent().addClass('active');
    if(vis) {
        $(currentTab).hide();
    } else {
        $(currentTab).show();
    }
}); 
});

And the CSS:

.active {
    background:#008CDC;
}
.current {
        background:#ECEDED;
}

Dynamic code here:

http://jsfiddle.net/tqhHA/79/

This is an example in which describing the requirements would have bore plenty of fruits. For some reason, the Stack Overflow community (well, those that follow the JavaScript tag) love a good "hide me, show me" question. ;-) As far as I can tell, the requirements are these:

  1. A side navigation shall determine which content appears in a main area
  2. Each main area shall contain its own subnavigation
  3. Active side navigation shall be indicated by a different style and may be preselected by markup
  4. Active subnavigation shall be indicated by a different style and may be preselected by markup
  5. An additional requirement that I did NOT delve into with my answer could easily be: Navigation and Subnavigation states shall be stored (eg. with a cookie, localstorage API, or session variable) for retrieval on return to the page.

Personally, I feel that if "Tabs" are the way you want to go for what you are calling "Top Navigation" but which I will henceforth call "Subnav" you could easily go with jQuery UI. My example does not use jQuery UI.


(Here's the fiddle for the following samples: http://jsfiddle.net/tqhHA/85/ )

The first thing to note about your original code is that it lacks a logical markup structure. The div with the id SideNav1 is at the same level as SideNav1TopNav2 . It just doesn't make sense. Then there are two items in nav that SEEM like they should be your side nav, but don't match up in terms of names. In the "content" area, the current tabs have the class "current" (great.) but don't have anchor links of their own for switching out the content (whoops!).

Not only that, but it is invalid markup (you cannot have your <p> elements interspersed with your <li> elements.

Let's clean this all up in the following ways:

  1. There will be one main navigation (resembling your original nav list)
  2. The "content area" will be populated by only one "content" div at any given time.
  3. The Subnav will follow the same pattern as the main nav.. links which describe the content that should be visible. It's just the same thing, nested one level deeper. Think of the "content" area as a smaller version of what the page itself does.
  4. Just in terms of providing sample code, the content itself will reveal what SHOULD be happening during interactions

The HTML:

<ul id="nav">
  <li><a href="#content1">Item 1</a></li>
  <li class="active"><a href="#content2">Item 2</a></li>
</ul>

<div id="content1" class="content">
  <ul>
    <li class="active"><a href="#tab1_1">Subnav 1</a></li>            
    <li><a href="#tab1_2">Subnav 2</a></li>               
    <li><a href="#tab1_3">Subnav 3</a></li>           
  </ul>   
  <div id="tab1_1">
    <p>This content should only be visible when Item 1 is selected from the
      main nav, and Subnav 1 is selected in the content area.</p>
  </div>
  <div id="tab1_2">
    <p>This content should only be visible when Item 1 is selected from the
      main nav, and Subnav 2 is selected in the content area. This <a href="#">fake anchor</a>
      shows that you can add anchors inside the tabs.</p>
  </div>
  <div id="tab1_3">
    <p>This content should only be visible when Item 1 is selected from the
      main nav, and Subnav 3 is selected in the content area.</p>
  </div>
</div>

<div id="content2" class="content">
  <ul>
    <li><a href="#tab2_1">Subnav 1</a></li>            
    <li><a href="#tab2_2">Subnav 2</a></li>               
    <li class="active"><a href="#tab2_3">Subnav 3</a></li>           
  </ul>   
  <div id="tab2_1">
    <p>This content should only be visible when Item 2 is selected from the
      main nav, and Subnav 1 is selected in the content area.</p>
  </div>
  <div id="tab2_2">
    <p>This content should only be visible when Item 2 is selected from the
      main nav, and Subnav 2 is selected in the content area.</p>
  </div>
  <div id="tab2_3">
    <p>This content should only be visible when Item 2 is selected from the
      main nav, and Subnav 3 is selected in the content area.</p>
  </div>
</div>

The CSS is revised as well:

  1. If we're going to talk about side and top nav, let's just make it side and top. We're not going to ask the helpful SO members to figure it out just by the text.
  2. You can use the same class ( active ) for both types of nav. All you need to do to style them differently is be specific
.active { background:navy; }
  .active a { color: white }

.content .active { background:yellow; }
  .content .active a { color: black; }

/* we're demoing sidebars... let's make it a SIDE bar! */
#nav, .content { float: left }
#nav { width: 100px; }
.content { width: 300px; margin-left: 24px;}

/* make the Subnav horizontal... more "tab"-like! */
.content li { display: inline-block }

OK, now we have a document in place. Here's the thing... all of the above may not even be the best approach. I have alarm bells going off about having so many IDs to facilitate the navigation. I left them in because it closely matches your approach, but it's not even as modular as it COULD be. Anyhow, just admitting for the record that it's not necessarily ideal, but that it will work.

The JS is completely new as well. The important thing is that it is easy to write the JS if we already have a proper HTML structure in place, The inline comments already provide enough documentation I think: so let's get right to it:

var updateMain = function() {

    // just hide ALL content areas first, rather than micro-managing
    $('.content, .content div').hide();

    // with all hidden, reveal only the content areas that have become active
    var activeLinks = $('.active a');
    activeLinks.each(function() {
        var activeArea = $(this).attr('href');
        $(activeArea).show();
    });

};

$(document).ready(function() {
    updateMain();

    // Bind a click event to the all navigation items (both main and sub)
    $('#nav a, .content > ul a').on('click', function(event) {

        event.preventDefault(); // prevent default anchor behaviour

        var $parent = $(this).parent(); // jQ object of the list item containing the anchor clicked

        // only do stuff if user has not just clicked a nav that's already active
        if (!($parent.hasClass('active'))) {
            $parent.siblings().removeClass('active');
            $parent.addClass('active');
        }

        updateMain(); // with new active classes in place, show the correct content
    });
});​

The bottom line is: all we're doing is on page load and then after each click, we're showing the content related to the "active" navigation. All that clicking does is change which is "active" and then runs the content updater function.


Disclaimers:

  • with the technique used here, you may get "Flash of Unstyled Content" (FOUC) because it's the JavaScript doing the hiding. Letting JavaScript handle it is better for accessibility and SEO. But you could research techniques for busting FOUC. I didn't go into those here.

  • hopefully you will see how content can be structured and events captured. Then you can spot opportunities for improvement, A popular way of showing/hiding related content is to structure it differently yet again, so that each link precedes the content and you can do a $(this).next().show() for example. There are a zillion ways to skin this cat.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM