Question

I recently asked a question to get the alt tag of an image displayed in the center of the image when you hover over it, and was thankfully provided with this following code to do so.

<script type="text/javascript">
    $(document).ready(function () { 
        $('#illustration-content ul li img').hover(
            function(){
                $(this).css('opacity','.2');
                var a = $(this).attr('alt');
                $(this).parent().append('<div class="title">' + a + '</div>');
            },
            function(){
                $(this).css('opacity','1');
                $(this).next().remove('.title');
            }
        );
    });
</script>

This works great.

The issue I now have is that if the cursor moves over the alt tag when its in the center of the image, it makes the whole effect disappear. I only want the effect to disappear when the cursor leaves the image.

Here is the css I am using and the html is a straight forward UL list of images.

#illustration-content ul li img{
    position: relative;
}

.title{
    position: absolute;
    z-index: 1000;
    width: 100px;
    margin: 80px 0px 0px -150px;
    color: #fff;
    font-size: 18px;
    display: inline-block;
}

I've tried with and without the inline block, same result, but the tag needs to be positions differently.

Any ideas why this may be happening?

Here is an example of the problem: http://jsfiddle.net/ysSJu/

Thanks for any support.

Was it helpful?

Solution

Explanation

The reason why the effect disappears when you hover over the title, is because you have the event for the img element only.
Instead, you should "link" the event to the entire li, so that when a user hovers over the list item, the entire element, including the div.title, will be affected.

demonstration

In the above image, you can see how it works.

Currently, you have your events linked to what corresponds to green, img. This means that the div.title, yellow, will be treated as an "outside" element (because the div.title isn't inside the img element), and therefore the mouseleave event will trigger when you hover over the div.title.
If you link the events to the li, red, the event will encompass everything within the red border, which is green and yellow.

You also have to treat your li blocks as display: inline-block so that you can position the title in relation to the image.

So, basically, what I'm trying to say is, instead of

$('#illustration-content ul li img'); 

you should do

$('#illustration-content ul li'); 

Then adjust the code accordingly.


Solution

Here is a working solution.

var $lis = $('#illustration-content ul li');

$lis.each(function(index, el){
    var $this = $(el);
    var eImg = $("img", $this);

    $this.mouseenter(function(){
        var eTitle = $('<div class="title">' + eImg .attr('alt') + '</div>');
        eImg.css('opacity','.1');
        $this.prepend(eTitle);
    }).mouseleave(function(){
        var eTitle = $("div.title", $this);
        eImg.css('opacity','1');
        eTitle.remove('.title');
    });
});

Alternative solution

Instead of creating and destroying a div at every mouseenter/mouseleave, it might be better to produce the div's just once at document.ready(), and show/hide them when a user hovers over them.
It should actually increase overall performance.

Here's an example.

By default, the .title has the style property display: none.

var $lis = $('#illustration-content ul li');

$lis.each(function(index, el){
    var $this = $(el);
    var eImg = $("img", $this);
    var eTitle = $('<div class="title">' + eImg.attr('alt') + '</div>');

    $this.prepend(eTitle)
        .mouseenter(function(){
            eImg.css('opacity','.1');
            eTitle.show();
        })
        .mouseleave(function(){
            eImg.css('opacity','1');
            eTitle.hide();
        });
});

Additional information, good to know

Note that I used the mouseenter and mouseleave instead of hover. Hover is actually a shorthand function of those two events combined. I tend to use the former, because it is generally easier to read, and more reliable.

Also note that in both solutions, I used the function .each(). By assinging the elements to variables, JQuery won't have to waste more processing to find the nodes every time I reference them; making it more efficient.

By passing an element as a second parameter in the selector, like this

$(".title", parent_element).css(...);

I don't have to use functions like .children() or .next(). It is also very efficient.

You mentioned you had hard time with the positioning, have a look at this, it might clarify things.


Phew, these long posts are hard to read, eh? Well, I think I've covered everything needed.

Good luck & happy coding! :)

OTHER TIPS

You can check the event.relatedTarget property to see if it is a .title element in your mouseout function:

            function(event){
                if (!$(event.relatedTarget).hasClass('title')) {
                    $(this).css('opacity','1').siblings().remove();
                }
            }

Demo: http://jsfiddle.net/ysSJu/1/

Also notice how I chained the function calls so you don't have to make two jQuery objects from this.

UPDATE

I noticed that if you move your cursor from one image to another that the .title element will not be removed as it should be from the previous image. You can fix that by resetting the img element's opacity and .remove()ing the extra .title elements:

var $images = $('#illustration-content ul li img');
$images.hover(
    function(){
        $images.css('opacity', 1).siblings().remove('.title');
        var $this = $(this),
            a     = $this.attr('alt');
        $this.css('opacity','.2').parent().append('<span class="title">' + a + '</span>');
    },
    function(event){
        if (!$(event.relatedTarget).hasClass('title')) {
            $(this).css('opacity','1').siblings().remove('.title');
        }
    }
);

Here's a demo: http://jsfiddle.net/ysSJu/3/

Change .hover. to .mouseenter and .mouseleave.

So basically...

var images = $('#illustration-content ul li img');
images.mouseenter(function(){
    $(this).css('opacity','.2');
    var a = $(this).attr('alt');
    $(this).parent().append('<div class="title">' + a + '</div>');
});
images.mouseleave(function(){
    $(this).css('opacity','1');
    $(this).next().remove('.title');
 });

The reason why is, as you pointed out, hovering over the alt <div> causes the hover event to no longer fire on the outer img. However with mouseenter and mouseleave hovering over the alt <div> doens't fire the mouseleave for the <img>.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top