Building a Hover Detail Panel With CSS3 & jQuery

Many online shops and e-commerce websites use a small details panel to offer more information about a product. I remember this from the early days of browsing Template Monster, and it has grown into a common trend for modern web designers.

In this tutorial I’d like to explain how we can duplicate this effect using some very basic code. I’ve provided two different versions, one built on jQuery and another built using CSS3. There are benefits to each one and you can download a copy of my tutorial code to see which you would prefer.

jQuery DemoCSS3 DemoDownload Source Code

Structuring the Page

My jQuery version has a slightly different HTML setup than the CSS3 adaptation. Obviously we will need a copy of the jQuery library, but even more important is the handling of the details panel. With JS I am calling a single HTML element to replace the inner HTML content. CSS3 requires unique div elements next to each of the thumbnail links, and thus adds more bulk into your page.

  <div class="thumbs">
    <ul class="clearfix">
      <li><a href="http://pixelsdaily.com/resources/photoshop/psds/social-hx-icons/" target="_blank"><img src="img/social-hx-icons-thumb.jpg" data-fullsize="img/social-hx-icons.jpg" class="thumbimg" alt="Social HX Icons" data-date="Nov 12th, 2013"></a><meta class="desc" content="We love these little social media icons. With just a hint of depth and shadow, they make a change from the norm."></li>
      <li><a href="http://pixelsdaily.com/resources/photoshop/psds/message-app-interface/" target="_blank"><img src="img/message-app-interface-thumb.jpg" data-fullsize="img/message-app-interface.jpg" class="thumbimg" alt="Message App Interface" data-date="Nov 6th, 2013"></a><meta class="desc" content="Delight is in the details, and this is a delightful little interface. Subtle gradients, shadows and lines make for something that is remarkably tactile and click-able. A great start for a messaging/communication widget or app interface."></li>
      <li><a href="http://pixelsdaily.com/resources/photoshop/psds/flat-toggles-and-sliders/" target="_blank"><img src="img/flat-toggles-sliders-thumb.jpg" data-fullsize="img/flat-toggles-sliders.jpg" class="thumbimg" alt="Flat Toggles and Sliders" data-date="Oct 18th, 2013"></a><meta class="desc" content="This set includes common UI components such as sliders, toggles, and widgets. Plenty of variety here to ensure you have exactly the right type of form in your design."></li>
    </ul>
  </div>
  
  <!-- setup details pane template -->
  <div id="details-pane" style="display: none;">
    <h3 class="title"></h3>
    <p class="desc"></p>
    <div class="big-img"><a href="" target="_blank"><img src=""></a></div>
  </div><!-- @end #details-pane -->

This code is located in the body of my jQuery version. The thumbnails are split into list items where we extract information for the details panel from HTML attributes. These details can be found in the anchor link, the image tag, and a related meta tag which holds the content description. I’m also using an HTML5 data attribute for the publication date.

Towards the bottom you’ll notice a single container div with the ID #details-pane. Targeting this object in jQuery allows us to replace inner values for the hover details. Now let’s take a look at one list item element from the CSS3 version.

  <div class="thumbs">
    <ul class="clearfix">
      <li>
        <a href="http://pixelsdaily.com/resources/photoshop/psds/social-hx-icons/" class="hvrlink" target="_blank"><img src="img/social-hx-icons-thumb.jpg" data-fullsize="img/social-hx-icons.jpg" alt="Social HX Icons"></a>
        <div class="details-pane">
          <h3 class="title">Social HX Icons<span>(Nov 12th, 2013)</span></h3>
          <p class="desc">We love these little social media icons. With just a hint of depth and shadow, they make a change from the norm.</p>
          <div class="big-img"><a href="http://pixelsdaily.com/resources/photoshop/psds/social-hx-icons/" target="_blank"><img src="img/social-hx-icons.jpg"></a></div>
        </div><!-- @end .details-pane -->
      </li>

I’ve changed the ID to a class of .details-pane because we need multiple adjacent divs next to each thumbnail. CSS3 checks when the user is hovering on the link and displays the sibling div, which already contains all the information. So another benefit when using CSS3 is that we don’t need to pull out information from attributes each time the target display changes.

I want to start with the CSS3 example because my jQuery code is a bit more complicated. This could be another reason to stick with the frontend HTML/CSS version, although this effect will be lost in older legacy browsers.

Hover Details with CSS3

The way this all works is using a plus(+) sign in the selector. CSS will target the very last element of the selector, in this case our .details-pane which is found directly next to each anchor link. I’ve given the links a class of .hvrlink so they are easier to identify.

.thumbs {
  padding: 8px 0;
  text-align: center;
}

.thumbs ul li {
  display: block;
  position: relative;
  float: left;
  margin-left: 50px;
  margin-bottom: 50px;
}


/** detail panel **/
.details-pane {
  display: none;
  color: #414141;
  background: #f1f1f1;
  border: 1px solid #a9a9a9;
  position: absolute;
  top: 0;
  left: 290px;
  z-index: 99999;
  width: 530px;
  padding: 6px 8px;
  text-align: left;
  -webkit-box-shadow: 1px 3px 3px rgba(0,0,0,0.4);
  -moz-box-shadow: 1px 3px 3px rgba(0,0,0,0.4);
  box-shadow: 1px 3px 3px rgba(0,0,0,0.4);
}
.details-pane.rightside {
  left: -529px;
}

.details-pane h3 {
  font-size: 2.0em;
  line-height: 1.1em;
  margin-bottom: 4px;
}

.details-pane h3 span {
  font-size: 0.75em;
  font-style: italic;
  color: #555;
  padding-left: 15px;
}

.details-pane .desc {
  font-size: 1.2em;
  margin-bottom: 6px;
}

I hope there’s nothing too complicated in this initial setup block. Each list item is floated left with a wide margin to position only two thumbnails per row. These list items contain the thumbnail link and the details window. We give the li a relative position so that now we can use absolute positioning on the hover panel.

What you might find interesting is the position change between the first two links. For every-other link I’ve added a class of .rightside onto the details panel container. Originally we push the hover panel over 290px to the left because this is the length of the thumbnail. But on the right column we get this panel hovering way off-screen, so instead we’d like it to appear over to the left. This uses a negative left position based on the full 530px width of the details pane itself.

/** hover styles **/
a.hvrlink:hover + .details-pane {
  display: block;
}
.details-pane:hover {
  display: block;
}

These last two selectors are all we need to get the hover setup properly. When the user mouses over a thumbnail link we immediately display the related .details-pane. The second block of code keeps it displaying even when the user mouses off the thumbnail onto the details, so that it always displays until the users completely moves out of that particular list item.

Moving to jQuery

Let’s tackle the JS code found at the bottom of my original index.html file. Much of the CSS code is similar, but it’s even shorter and easier to read because we don’t need absolute values. This is handled instead by jQuery calculations which may appear complicated – but it all does make sense once you think about it.

$(function(){
  $('.thumbs ul li img').on('mouseover', function(e){
    var dpane      = $('#details-pane');
    var dpanetitle = $('#details-pane .title');
    var dpanedesc  = $('#details-pane .desc');
    var newtitle   = $(this).attr('alt');
    var newdate    = $(this).attr('data-date');
    var newdesc    = $(this).parent().next('meta.desc').attr('content');
    var newimg     = $(this).attr('data-fullsize');
    var linkurl    = $(this).parent().attr('href');
    
    var position = $(this).offset();
    var imgwidth = $(this).width();
    var ycoord   = position.top;
    if(position.left / $(window).width() >= 0.5) {
      var xcoord = position.left - 530;
      // details pane is 530px fixed width
      // if the img position is beyond 50% of the page, we move the pane to the left side
    } else {
      var xcoord = position.left + imgwidth;
    }

I am targeting the thumbnail image for the hover event listener and, once this is triggered, we set up a whole bunch of variables. The first big block of code obtains all the important information we need to append into the hover panel container.

Next we need to gather coordinates to understand where this details panel should appear. The y-coordinate won’t change based on the position, but the x-coordinate will if we are hovering on the right column. I have a logic statement checking against the current image position divided by the whole window using $(window).width(). If the image is beyond 50% of the total page width, then we need it displaying over to the left side instead of the right.

   $('.big-img a').attr('href',linkurl);
    $('.big-img a img').attr('src',newimg);
    
    var titlehtml = newtitle + '('+newdate+')';
    
    dpanetitle.html(titlehtml);
    dpanedesc.html(newdesc);
    dpane.css({ 'left': xcoord, 'top': ycoord, 'display': 'block'});
    
  }).on('mouseout', function(e){
    $('#details-pane').css('display','none');
  });

These next set of code is replacing internal HTML values within the details pane using variables defined earlier. At the end we update inline CSS properties to change display: block and make it appear on the page. When hovering off the thumbnail, it will auto-hide the display again, unless we hover onto the details pane itself. This hover will be captured using a whole separate event listener which requires a bit of tricky logic.

 // when hovering the details pane keep displayed, otherwise hide
  $('#details-pane').on('mouseover', function(e){
    $(this).css('display','block');
  });
  $('#details-pane').on('mouseout', function(e){
    //this is the original element the event handler was assigned to
    var e = e.toElement || e.relatedTarget;
    if (e.parentNode == this || e.parentNode.parentNode == this || e.parentNode.parentNode.parentNode == this || e == this || e.nodeName == 'IMG') {
      return;
    }
    $(this).css('display','none');
    //console.log(e.nodeName)
  });
});

Notice I am using two separate .on() methods attached to the same jQuery selector. You might consider pairing these into a jQuery chain along with other similar event listeners. When hovering we just need to keep the panel displayed as a block element. But when leaving the details panel we only wish to hide it when the user isn’t hovering onto the same thumbnail image used for displaying in the first place.

The variable e is returned from each mouseout event, telling us which new element is being hovered. Applying the property parentNode we can check out where this new element is contained. I have four separate logic checks inside this statement, and three of them are checking against objects within the details pane. The variable name this refers to the #details-pane.

We need to check in case the user hovers onto the h3 element, the paragraph, or even the image(which is embedded into an anchor link and a div). If so then we do not hide anything.

I’m also checking with the nodeName property in case the user hovers off the details pane into the body. If the node returns IMG then it shouldn’t try to hide anything right away. This is more of a hacked solution because the only images in my demo relate to the thumbnails themselves. So when hovering onto the image we still want to keep the panel displayed, otherwise it gets hidden using jQuery’s .css() method.

I’ve been unable to solve with a better solution but I’d love to hear feedback from other developers. It may be possible to check against the image class, but unfortunately you can’t do this with a nodeName property. You’ll find my commented code console.log(e.nodeName) which you can run to check what these properties are returning to the browser.

jQuery DemoCSS3 DemoDownload Source Code

Closing

These techniques are generally simple to understand if you are comfortable in frontend web development. CSS3 provides an easy solution but it only works properly in standards-compliant browsers. You’ll notice both versions are basically the same to the visitor’s perspective. Try this out in your next web project and see what else you can build on top of my template.