views:

574

answers:

6

I have a list of images like this:

<ul class='blah'>
  <li><img src='...' /></li>
  <li><img src='...' /></li>
  <li><img src='...' /></li>
  <li><img src='...' /></li>
</ul>

And I have it styled to display as a horizontal list without bullet points. Kinda of like what you see on GitHub for followers (see http://github.com/chrislloyd). I'm using jQuery and jQuery UI to make these images bigger when the user hovers their mouse over it. Here's the code I've got so far:

$(".blah img").hover(
  function() {
    $(this).effect("size",
      { to: { width: 64, height: 64 },
        origin: ['top','center'], scale: 'content' });
  },
  function() {
    $(this).effect("size",
      { to: { width: 32, height: 32 }, scale: 'content' });
  });

This works well while it is animating, but once an image reaches its maximum size the other images reflow (move out of the way). Any ideas how to do this without reflowing anything?

I tried variations of 'position: absolute;', 'position: relative', etc. on the images and the container (the <ul>) but it didn't really make any difference.

+3  A: 

You could try something like:

var pos = $(this).position();
$(this).css({ position: "absolute",
              top: pos.top, left: pos.left, zindex:10 });

This will keep the image from pushing the others but it sort of has the opposite problem of 'sucking' the others in since it is being positioned out of the way.


There is a pretty good example here of using jQuery to accomplish a similar effect.

This example uses the list items to sort of reserve the space for the images. We float the list items left and space them out by positioning them 'relative' and giving them an explicit size. Then the images are positioned 'absolute' within the list item. Now when we use jQuery to re-size our images, the other images don't re-flow since the list item is acting as a placeholder.

The example also uses animate rather than effect.size for a little cleaner effect.

Hope this helps. Good luck.

Greg Gianforcaro
That example is awesome, I wish I could find something like it when I googled for about an hour. I'll play with that when I get home. Thanks!
Mark A. Nicolosi
A: 

I got a slightly better result by setting the origin to top left instead of top center.

But otherwise it's about the same. Here's something for others to work on:

<html><head><title>HELL NO WE WON'T REFLOW</title>
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'&gt;&lt;/script&gt;
<script src='http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js'&gt;&lt;/script&gt;
<script>
jQuery(function($) {
  $('.blah img').each(function(){
    var p = $(this).position();
    $(this).css({top:p.top,left:p.left});
  });
  $('.blah img').each(function(){
    //$(this).css({position:'absolute'});
  });
  $(".blah img").hover(
  function() {
    $(this).effect("size",
      { to: { width: 375, height: 91 },
        origin: ['top','left'], scale: 'content' });
  },
  function() {
    $(this).effect("size",
      { to: { width: 250, height: 61 }, scale: 'content' });
  });
});
</script>
</head>
<body>
<ul class="blah">
<li><img src='http://sstatic.net/so/img/logo.png'/&gt;&lt;/li&gt;
<li><img src='http://sstatic.net/so/img/logo.png'/&gt;&lt;/li&gt;
<li><img src='http://sstatic.net/so/img/logo.png'/&gt;&lt;/li&gt;
<li><img src='http://sstatic.net/so/img/logo.png'/&gt;&lt;/li&gt;
</ul>
</body>
</html>
dlamblin
Yeah, not any noticable difference for me. Thanks.
Mark A. Nicolosi
+1  A: 

Hi there, If you use the 'animate' function from jquery instead of "effect," it works great (it doesn't reflow). I tried the following code in Firefox 3, Chrome and IE 8 and in all cases nothing reflowed. Here's the function:

 $(".blah img").hover(
   function() {
     $(this).animate( {'width': 64, 'height': 64 });
   },
   function() {
     $(this).animate( {'width': 32, 'height': 32 });
   });

And here's the full page I used to test the function:

 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
 <script src="../jquery.js" type="text/javascript"></script>
 <script type="text/javascript" >
   $(document).ready( function() {
   $(".blah img").hover(
     function() {
       $(this).animate( {'width': 64, 'height': 64 });
     },
     function() {
       $(this).animate( {'width': 32, 'height': 32 });
     });
   });
 </script>
 <style type="text/css" rel="stylesheet">
   ul li { 
     float: right;
     list-style: none;
   }
 </style>
 </head>
 <body>
 <ul class='blah'>
   <li><img src='../i32.jpg' /></li>
   <li><img src='../i32.jpg' /></li>
   <li><img src='../i32.jpg' /></li>
   <li><img src='../i32.jpg' /></li>
 </ul>
 </body>
 </html>
btelles
+1  A: 

The easy answer is usually to find an existing jQuery plugin that already does what you want. There are a lot of them out there, and even if one isn't perfect you can adapt or learn from it to get the desired result.

To do it yourself, I'd suggest having two copies of the image: one inline with the list, and another on top that zooms in. That way the layout of the list doesn't depend on the zoomed image at all.

Untested code, just thinking:

//  on page load, process selected images
$(function () {
  $('img.hoverme').each(function () {
    var img = $(this);
    var alt = $("<img src='" + img.attr('src') + "'/>");
    img.after(alt);
    alt.hide();

    img.hover(function () {
      var position = img.position();
      alt.css({ 
        position: "absolute",
        top: position.top,
        left: position.left,
        width: 32,
        height: 32
      }).animate({
        top: position.top - 16,
        left: postion.left - 16,
        width: 64,
        height: 64
      });
    }, function () { });

    alt.hover(function () { }, function () {
      var position = img.position();
      alt.animate({
        top: position.top,
        left: position.left,
        width: 32,
        height: 32
      }, function () {
        alt.hide();
      });  // alt.animate
    });  //  alt.hover

  });  //  each
});  // $(

When the mouse goes over the smaller image, the larger one is created hovering over it, and immediately expands in place. When the mouse leaves the larger image, it shrinks and then disappears.

Marcus Downing
+1  A: 

Here's my attempt. I set the list items to be positioned relative to hold the absolutely positioned images. This seems to work as you specified. The only issue is when you glide over all of them quickly, they all animate. I would assume you'd want to add something in there to make it that only one resizes at a time.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&gt;
<html>
<head>
    <title></title>
    <style type="text/css">
        li { position:relative; height:40px; width:40px; float:left; list-style:none; }
        li img { position:absolute; top:0; left:0; height:32px; width:32px; border:1px solid #666; background:#eee; }
    </style>
    <script src="http://code.jquery.com/jquery-latest.js"&gt;&lt;/script&gt;
    <script type="text/javascript">
        $(document).ready(function(){
            $(".blah img").hover(
              function() {
                $(this).css("z-index", "2");
                $(this).animate({
                  "height": "65px", "width": "65px", "top": "-16px", "left":"-16px"
                }, "slow");
              },
              function() {
                 var that = this;
                $(this).animate({
                  "height": "32px", "width": "32px", "top": "0", "left":"0"
                }, "slow", "swing", function() { $(that).css("z-index", "1"); });

              });
        });
    </script>
</head>

<body>
    <ul class='blah'>
      <li><img src='http://www.gravatar.com/avatar/116ed602629e00b79eae9af774398bb0?s=24&amp;d=http%3A%2F%2Fgithub.com%2Fimages%2Fgravatars%2Fgravatar-24.png' /></li>
      <li><img src='http://www.gravatar.com/avatar/116ed602629e00b79eae9af774398bb0?s=24&amp;d=http%3A%2F%2Fgithub.com%2Fimages%2Fgravatars%2Fgravatar-24.png' /></li>
      <li><img src='http://www.gravatar.com/avatar/116ed602629e00b79eae9af774398bb0?s=24&amp;d=http%3A%2F%2Fgithub.com%2Fimages%2Fgravatars%2Fgravatar-24.png' /></li>
      <li><img src='http://www.gravatar.com/avatar/116ed602629e00b79eae9af774398bb0?s=24&amp;d=http%3A%2F%2Fgithub.com%2Fimages%2Fgravatars%2Fgravatar-24.png' /></li>
    </ul>

</body>
</html>
Ken Earley
I actually like the effect where they all animate a few tenths of a second apart.
Mark A. Nicolosi
+1  A: 

You need a function that does the following:

  1. User hovers over image
  2. Function calls to CLONE ($(this).clone()...) image and absolutely position in exact same place as base image, but one layer on top (render the cloned element somewhere later in the DOM)
  3. Do your animation on the cloned image
  4. On hover off, reverse the animation, then remove the cloned element completely. (.remove())

Pretty simple. The other elements will stay untouched and not move.

Josh