views:

24376

answers:

6

This most be the second most simple rollover effect, still I don't find any simple solution.

Wanted: I have a list of items and a correspoding list of slides (DIVs). After loading, the first list item should be selected (bold) and the first slide should be visible. When the user hovers over another list item, that list item should be selected instead and the corresponding slide be shown.

The following code works, but is awful. How can I get this behavior in an elegant way? jquery has dozens of animated and complicated rollover effects, but I didn't come up with a clean way for this effect.

<script type="text/javascript">
function switchTo(id) {
    document.getElementById('slide1').style.display=(id==1)?'block':'none';
    document.getElementById('slide2').style.display=(id==2)?'block':'none';
    document.getElementById('slide3').style.display=(id==3)?'block':'none';
    document.getElementById('slide4').style.display=(id==4)?'block':'none';
    document.getElementById('switch1').style.fontWeight=(id==1)?'bold':'normal';
    document.getElementById('switch2').style.fontWeight=(id==2)?'bold':'normal';
    document.getElementById('switch3').style.fontWeight=(id==3)?'bold':'normal';
    document.getElementById('switch4').style.fontWeight=(id==4)?'bold':'normal';
}
</script>

<ul id="switches">
  <li id="switch1" onmouseover="switchTo(1);" style="font-weight:bold;">First slide</li>
  <li id="switch2" onmouseover="switchTo(2);">Second slide</li>
  <li id="switch3" onmouseover="switchTo(3);">Third slide</li>
  <li id="switch4" onmouseover="switchTo(4);">Fourth slide</li>
</ul>
<div id="slides">
  <div id="slide1">Well well.</div>
  <div id="slide2" style="display:none;">Oh no!</div>
  <div id="slide3" style="display:none;">You again?</div>
  <div id="slide4" style="display:none;">I'm gone!</div>
</div>
A: 

The only thing that's wrong with this code (at least to me) is that you're not using a loop to process all elements. Other than that, why not to it like that?

And with loop, I mean grabbing the container element via a JQuery and iterating over all child elements – basically a one-liner.

Konrad Rudolph
+4  A: 

Here's the jQuery version:

<script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.2.6.min.js"&gt;&lt;/script&gt;
<script type="text/javascript">
$(function () {
    $("#switches li").mouseover(function () {
     var $this = $(this);
     $("#slides div").hide();
     $("#slide" + $this.attr("id").replace(/switch/, "")).show();
     $("#switches li").css("font-weight", "normal");
     $this.css("font-weight", "bold");
    });
});
</script>

<ul id="switches">
  <li id="switch1" style="font-weight:bold;">First slide</li>
  <li id="switch2">Second slide</li>
  <li id="switch3">Third slide</li>
  <li id="switch4">Fourth slide</li>
</ul>
<div id="slides">
  <div id="slide1">Well well.</div>
  <div id="slide2" style="display:none;">Oh no!</div>
  <div id="slide3" style="display:none;">You again?</div>
  <div id="slide4" style="display:none;">I'm gone!</div>
</div>
travis
+1  A: 
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">

$(document).ready(
  function(){
    $( '#switches li' ).mouseover(
      function(){
        $( "#slides div" ).hide();
        $( '#switches li' ).css( 'font-weight', 'normal' );
        $( this ).css( 'font-weight', 'bold' );
        $( '#slide' + $( this ).attr( 'id' ).replace( 'switch', '' ) ).show();
      }
    );
  }
);

</script>
</head>
<body>
<ul id="switches">
  <li id="switch1" style="font-weight:bold;">First slide</li>
  <li id="switch2">Second slide</li>
  <li id="switch3">Third slide</li>
  <li id="switch4">Fourth slide</li>
</ul>
<div id="slides">
  <div id="slide1">Well well.</div>
  <div id="slide2" style="display:none;">Oh no!</div>
  <div id="slide3" style="display:none;">You again?</div>
  <div id="slide4" style="display:none;">I'm gone!</div>
</div>
</body>
</html>
mojo
+2  A: 

Nice. travis and I were both pushing in the same direction, but did things quite differently. I'd normally swallow a similar answer, but the code below highlights something that jquery really handles well, which is allowing you to separate out the handling to the pieces that are best suited for it. The HTML contains only the elements. The javascript adds/removes classes, and hooks up events. CSS is used for styling.

EDIT: Code removed. Carl Meyer's version did the same thing my original code did, but cleaner and simpler, especially using the .hover() jquery method instead of .mouseover(), and using real objects almost everywhere, instead of easily-broken string selectors. If you switched IDs in the future, you would only need to modify slide_for_switch() and the selector used in his $(document).ready() method.

Thomas G. Mayfield
+11  A: 
$(document).ready(function() {
    switches = $('#switches > li');
    slides = $('#slides > div');
    switches.each(function(idx) {
            $(this).data('slide', slides.eq(idx));
        }).hover(
        function() {
            switches.removeClass('active');
            slides.removeClass('active');             
            $(this).addClass('active');  
            $(this).data('slide').addClass('active');
        });
    });

Rather than displaying all slides when JS is off (which would likely break the page layout) I would place inside the switch LIs real A links to server-side code which returns the page with the "active" class pre-set on the proper switch/slide.

And here's the HTML:

<html>
<head>

<title>test</title>

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="switch.js"></script>

<style type="text/css">

#switches .active {
  font-weight: bold;
}

#slides div {
  display: none;
}

#slides div.active {
  display: block;
}

</style>
</head>
<body>

<ul id="switches">
  <li class="active">First slide</li>
  <li>Second slide</li>
  <li>Third slide</li>
  <li>Fourth slide</li>
</ul>
<div id="slides">
  <div class="active">Well well.</div>
  <div>Oh no!</div>
  <div>You again?</div>
  <div>I'm gone!</div>
</div>

</body>
</html>
Carl Meyer
instead of doing this.slide i recommend using the data function: $(this).data('slide', slides[idx]); That way you don't clutter the DOM with data.
Pim Jager
Good point, thanks. Edited to include this suggestion.
Carl Meyer
+4  A: 

Here's my light-markup jQuery version:

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
function switchTo(i) {
  $('#switches li').css('font-weight','normal').eq(i).css('font-weight','bold');
  $('#slides div').css('display','none').eq(i).css('display','block');
}
$(document).ready(function(){
  $('#switches li').mouseover(function(event){
    switchTo($('#switches li').index(event.target));
  });
  switchTo(0);
});
</script>
<ul id="switches">
  <li>First slide</li>
  <li>Second slide</li>
  <li>Third slide</li>
  <li>Fourth slide</li>
</ul>
<div id="slides">
  <div>Well well.</div>
  <div>Oh no!</div>
  <div>You again?</div>
  <div>I'm gone!</div>
</div>

This has the advantage of showing all the slides if the user has javascript turned off, uses very little HTML markup and the javascript is pretty readable. The "switchTo()" function takes an index number of which <li> / <div> pair to activate, resets all the relevant elements to their default styles (non-bold for list items, display:none for the divs) and the sets the desired list-item and div to bold and display. As long as the client has javascript enabled, the functionality will be exactly the same as your original example.

Neall