0

I have a great piece of jQuery that swaps an image when the user hovers on an image. Check out the code below to see it.

It uses mouseenter and mouseleave.

I would like to modify it so that the image swap triggers when a specific input/label is :checked.

You can see my demo page here:

http://test.davewhitley.com/not-wp/static_folder/index.php

To give you some background, I have 4 inputs/labels. When one is checked, a group of images will be swapped from black and white to their color version. I have two files for every image (image_s.jpg and image_o.jpg).

I am using the :checked pseudo class to do some filtering. When an input is checked, some images remain full opacity and other reduce to opacity:0.1

I want only the images that remain full opacity after checking the input to be color.

So basically I want to say in javascript: Whenever an input is :checked within #container swap _s.jpg with _o.jpg.

Any help would be great.

UPDATE 1: To clarify: There is no image swap happening in the demo. The opacity is just changed when an input is checked. In addition to the opacity change, I would like an image swap. All of the images would be black and white by default, and when an input is selected, the selected images would change from black and white to color (by using an image swap).

UPDATE 2: To put it simply, I would like all of the images to be black and white until the user clicks on one of the filter labels (print, web, typefaces, etc.) When a label is clicked, the non-filtered images will lower in opacity and the filtered images that remain full opacity will swap to a color image.

UPDATE 3: I can't seem to get the below answers to work for me. I am willing to abandon the input/:checked/pseudo-class filtering technique if it gets the job done. Also, I working test would help me a lot (JSfiddle).

Here is the image swap javascript:

    $('.imgover').each(function() {

    var std = $(this).attr("src");
    var hover = std.replace("_s", "_o"); 
    $(this).clone().insertAfter(this).attr('src', hover).removeClass('imgover').siblings().css({
        position:'absolute'
    });
    $(this).mouseenter(function() {
        $(this).stop().fadeTo(200, 0);
    }).mouseleave(function() {
        $(this).stop().fadeTo(200, 1);
    });
});
davecave
  • 4,698
  • 6
  • 26
  • 32
  • What doesn't work? Your demo seems to work as you explained. – Jeff B Mar 06 '12 at 17:46
  • There is no image swap happening in the demo. The `opacity` is just changed when an input is checked. In addition to the opacity change, I would like an image swap. All of the images would be black and white by default, and when an input is selected, the selected images would change from black and white to color (by using an image swap). – davecave Mar 06 '12 at 18:18

2 Answers2

1

Currently it seems you're missing the imgover class on your images and their src is wrong (_e instead of _s), so the hover/swap doesn't work at all.

Just to get going, I fixed that by executing

$('#photos img').attr('src','images/iso_s.jpg').addClass('imgover');

in the console first.

Then re-apply the image-swapping code, as modified here:

$(function(){

    $('.imgover').each(function() {

        var std = $(this).attr("src");
        var hover = std.replace("_s", "_o"); 
        $(this).clone().insertAfter(this).attr('src', hover).removeClass('imgover').siblings().css({
            position:'absolute'
        });

        var $img = $(this);
        function isActive(){
            // Regex for extracting the ID number for the image group type
            var reg = /^(.*\s)?ff-item-type-(\d+)[^\d]*.*/;
            var classes = $img.closest('li').attr('class');
            var id = reg.exec(classes)[2];
            console.log('Checking image with group id',id);

            var found = $('#select-type-all, #select-type-'+id).filter(':checked').length;
            console.log('Matching and checked input count:', found);

            return found>0;
        }

        $(this).mouseenter(function() {
            if (isActive()){
                $(this).stop().fadeTo(200, 0);
            }
        }).mouseleave(function() {          
            $(this).stop().fadeTo(200, 1);
        });
    });
});

This appears to work. Note though that for some reason the b/w pictures are scale too big, probably a problem with your CSS. Applying the following fixes that well enough to see the swapping working as intended:

$('#photos img').css('width','auto !important')

The only change I made to the swapping code was adding the isActivefunction add checking that before performing the swap.

isActive extracts the i image group id from the class of its parent <li>, and then checks if the corresponding input #select-type-[id] (or the #select-type-all-input) is checked.


Ooops, I see now that you wanted the swapping to be triggered by the filtering directly. To do that, try this instead:

$(function(){

    $('#photos img').attr('src','images/iso_s.jpg').addClass('imgover');
    $('#photos img').css('width','auto !important')

    $('.imgover').each(function() {

        var std = $(this).attr("src");
        var hover = std.replace("_s", "_o"); 
        $(this).clone().insertAfter(this).attr('src', hover).removeClass('imgover').siblings().css({
            position:'absolute'
        });

        var $img = $(this);
        function isActive(){
            // Regex for extracting the ID number for the image group type
            var reg = /^(.*\s)?ff-item-type-(\d+)[^\d]*.*/;
            var classes = $img.closest('li').attr('class');
            var id = reg.exec(classes)[2];
            console.log('Checking image with group id',id);

            var found = $('#select-type-'+id).filter(':checked').length;
            console.log('Matching and checked input count:', found);

            return found>0;
        }

        $('#container input').change(function(){
            if (isActive()){
                $img.stop().fadeTo(200, 0);
            } else {         
                $img.stop().fadeTo(200, 1);
            }
        });
    });

});

Not the most effective or elegant solution (binding an onchange event for each image), but it gets the job done.

Supr
  • 18,572
  • 3
  • 31
  • 36
  • Hmm the code doesn't seem to be working. I updated my demo site. The JavaScript wasn't injecting the CSS for `#photos img` so I put it in there manually. Also I changed the image file names to the correct name. Any ideas? – davecave Mar 09 '12 at 01:02
  • It doesn't have any effect now because it is running before the rest of the document is loaded, so `$('.imgover')` doesn't match anything. Wrap it in `$(function(){...});` to delay it until after the DOM has finished loading. I'll update the post to illustrate. – Supr Mar 09 '12 at 12:46
  • Ah thank you! Now I'm trying to figure out the weird scaling of the images. The colored ones are scaled slightly vertical. Also, there is an `img` being injected into the anchor tags so there are now two images in every anchor tag. Not sure if that was intended. If it was, is it possible to only have one `img` per anchor? And just swap the `_s` for `_o`? For some reason on page load, some of the images start out colored. Also, I think some of the CSS rules are causing the fluid width of the grid to go out of wack: http://cl.ly/2e222D3v3F151R2Y1o1h – davecave Mar 09 '12 at 16:15
  • Regarding the two images for each anchor, that's simply how your swap code works: one image for the grayscale version, and one for the colored version. Then the bw version is faded in/out as needed. You could change it to use a single image and just swap the src, but then you would lose the fade effect. As for the other stuff, if you're still having trouble (it seems to work fine here in Opera), I would start by removing any and all sizing-related CSS and start fresh to get it right. – Supr Mar 12 '12 at 01:35
1

I think there's a simpler way to achieve your desired effect without pseudo classes.

You could give each link wrapping an image a class and id, and using CSS declare the bg image for each link. Then tie each input to a link, and using .hover() and/or .click(), alter the CSS and thusly the bg image.

Something like:

<!-- HTML -->
<div id="inputsContainer">
    <input id="trigger1 />
</div>
<div id="linkElementsContainer">
    <a id="triggered1" class="state1"></a>
</div>
<!-- End HTML -->

/* CSS */
#triggered1.state1 {
    background: url('yoursite/images/triggered1_s.jpg') 50% 50% transparent no-repeat;
}

#triggered1.state2 {
    background: url('yoursite/images/triggered1_o.jpg') 50% 50% transparent no-repeat;
}

// jQuery
// shorten the names of all of these, please, they're long for 
// purposes of illustrating concept

function changeClassOfAssociatedLink(inputElement) {
    // get id of input element
    var inputIdString = inputElement.attr("id");
    // regex to get numbers in id attr of input element
    var inputIdStringWithNoLetters = inputIdString.replace(/\D/g,'');

    // define id string of triggered link element you're looking for
    var linkIdString = '#triggered' + inputIdStringWithNoLetters;
    // find the link element with the id string you defined above
    var linkElement = jQuery(linkIdString);

    if(linkElement.hasClass('state1')) {
        linkElement.attr('class', 'state2');
    }

}

jQuery('#inputsContainer input').hover(changeClassOfAssociatedLink($(this)));

jQuery('#inputsContainer input').click(changeClassOfAssociatedLink($(this)));

A couple ways to get fancy/efficient with it:

1) Use image sprites to make sure all images load at the same time, providing a consistent UX to users with varying connections speeds. E.g.:

/* CSS */

#triggered1.state1 {
    background: url('yoursite/images/sprites.jpg') 0 0 transparent no-repeat;
}

#triggered1.state1 {
    background: url('yoursite/images/sprites.jpg') 0 -250px transparent no-repeat;
}

/* Moves the background image "down" 250px */

2) If using sprites with a finite amount of input/image pairs or identically sized images, add some easing animation to your transitions by defining the positions of the black and white and color images within your sprites image. NOTE: this would require some basic CSS positioning, wrapping absolutely positioned links with relatively positioned elements. E.g.:

function changeImages(linkElement, topP) {
            var linkElementClass = linkElement.attr("class");
    linkElement.animate({
        top: topP
    }, {
        duration: 500,
        easing: 'easeInOutExpo',
        complete: linkClassController(linkElement)
    });
}

function linkClassController(l) {
    var linkClass = l.attr("class");

    if(linkClass == 'state1') {
         l.attr('class', 'state2');
    } else {
         l.attr('class', 'state1');
    }
}

jQuery('#inputsContainer input').hover(function() {
    // get id of input element
    var inputIdString = inputElement.attr("id");
    // regex to get numbers in id attr of input element
    var inputIdStringWithNoLetters = idString.replace(/\D/g,'');

    // define id string of triggered link element you're looking for
    var linkIdString = '#triggered' + idString1WithNoLetters;
    // find the link element with the id string you defined above
    var l = jQuery(linkIdString);

    var top = // define the top position

    changeImages(l, top);
});

// note, .toggleClass() could make provided code more efficient
// this was a quick example

3) The provided code gives you an opportunity to get away from using inputs. Unless you're gathering data using the inputs, it might benefit you in both JavaScript versatility and SEO to use a <ul>, or if you're using HTML5, a <nav>.

dyelawn
  • 750
  • 5
  • 17
  • I will try this out asap and see what happens. Thanks! – davecave Mar 11 '12 at 05:55
  • I got a test page running here : http://test.davewhitley.com/not-wp/color_test/index.php | I can't get it to work, but I don't understand a lot of javascript so I'm sure there's something wrong I am doing. – davecave Mar 11 '12 at 17:44
  • Please see this jsfiddle: http://jsfiddle.net/5qHCN/4/ Note: I changed some HTML and some of the code from my original response, so you'll probably have to do some editing, but it works. – dyelawn Mar 13 '12 at 01:21