Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Trouble accessing the right selectors

So I'm still having some trouble with a project I'm working on using jQuery. Basically I need to show/hide certain images based on user input and if it matches the title (alt tags) of the image. The HTML structure has a gallery wrapper containing multiple "gallery-items". Here's the first one to give you an idea.

      <div class="gallery-item">
        <a href="assets/img/photos/01.jpg" data-lightbox="gallery" data-title="I love hay bales. Took this snap on a drive through the countryside past some straw fields.">
          <img src="assets/img/thumbnails/01.jpg" alt="Hay Bales">
        </a>
      </div>
$(function() {

  // Variables
  var $searchInput;
  var $title;

  // Get input based on keyup or change
  $('#searchInput').on('keyup change', function(){

    // Set User Input
    $searchInput = $('#searchInput').val();

    // Set Titles (alt tags)
    $title = $('.gallery-item img').attr('alt');

    // Show/Hide conditions
    if($searchInput.toLowerCase() === $title.toLowerCase()) {
      $('.gallery-item img').hide();
    } else {
      $('.gallery-item img').show();
    }
  });

}); // End

The two big problems I'm running into.

1) only the first alt tag is being saved into $title I think instead of the entire set of alt tags as only the first photos alt tag creates any action (Hay Bales).

2) The show/hide is showing and hiding the entire set of images. How do I have it select only the specific one (or ones) in question?

4 Answers

Ah! I think I understand, let me know if this is correct for what you need:

 var imageToShow = $('.gallery-item img[alt]').filter(function() { 
        return this.alt.toLowerCase().indexOf($searchInput.toLowerCase()) === 0; 
  });

If we compare to index 0, it'll only match images where the alt begins with what the user has typed. In this case "L" displays the first three, "Li" the first two, and "Lig" only the second.

The comparison !== -1 doesn't care about where the user string appears in the alt value, just that it's not -1 (doesn't exist). Using === 0 will only select images with an alt value that begins with the user string.

Yes! Ahh you're amazing thank you so much for walking me through this!

Casey Ydenberg
Casey Ydenberg
15,622 Points

Hi Chris,

The basic problem is that when you call $('.gallery-item img') jQuery has no way of knowing which one you are referring to. When you ask if for the alt attribute and save it to a single variable, it just picks the first one, and when you show or hide, it shows or hides all of them.

The cure for these cases is jQuery's .each.

$('gallery-item img').each(function() {
    var title = $(this).attr('alt');
    //rest of code goes here
});

Right! That make sense. I need to loop through all of them and add each as I go along. Thank you!

Casey is correct. You'll also have to use the correct image selector to hide or show the image you want, as currently your code will show all images or hide all images based on the conditional.

As an alternate, you can do something like the following to hide all images, then show only the images that match the alt attribute:

var imageToShow = $('.gallery-item img[alt]').filter(function() {
    return this.alt.toLowerCase() == $searchInput.toLowerCase();
});
$('.gallery-item img').hide();
imageToShow.show();

This will also support multiple images with the same alt attribute.

Right! I knew I some how needed to have a condition to the specific 'alt' tag in question but didn't know how to specify it. Thank you for this addition!

Actually, a slight change in behavior is needed. This code hides all the images and then shows the one that matches once it matches entirely. It should search dynamically so as the user is type "hay bales" all the images should be showing until the final 's' in 'hay bales' is typed or rather the function should be matching each letter press and as conditions are not met the images should be hidden. i.e. 3 images start with an 'L' so when that is first typed those images should be showing then as the second letter 'a' is pressed it narrows down even more.

I might be misunderstanding but it sounds like you're asking for two things: show all images until the user types in an exact match, and then show only those that match; and show all images that have a partial or full match to the user input.

If you're looking to show all images until the user's input exactly matches an alt value, then you'd change the hide() on all images to show(), then look to see if the count of matched selectors (imageToShow) is greater than 0 (meaning there was at least one match found):

  $('.gallery-item img').show(); // show all img by default
  if (imageToShow.length > 0) { // if the filter function has found image(s) with an 'alt' value equal to the user's input
        $('.gallery-item img').hide(); // hide all images
        imageToShow.show();  // show only the image(s) that had a matching alt value
  }

If you want to show all images with an alt value that has a partial or full match to the user input, you'd need to change the filter function to look for an occurrence of the search string within any of the alt values, rather than looking for an exact match:

   var imageToShow = $('.gallery-item img[alt]').filter(function() { 
        return this.alt.toLowerCase().indexOf($searchInput.toLowerCase()) !== -1; 
    });

To make it easier to read:

   var imageToShow = $('.gallery-item img[alt]').filter(function() { 
        var altValue = this.alt.toLowerCase();
        var userInput = $searchInput.toLowerCase();
        return altValue.indexOf(userInput) !== -1; 
    });

What this does is try to find the position of the first occurrence of userInput in altValue.

So say you have four images with the following alt values: Lion Liger Leopard Alligator

If you type in "L", you will see all four images, because they all contain the letter 'L'.

if you type in "Li", you will only see three images; "Leopard" will not show.

If you type in "Lig", only "Liger" and "Alligator" will show.

I'm following! Thank you so much for your help thus far. I'm sorry if I was hard to understand. The final filter function you wrote handles what I'm looking for with one small behavior I'm looking to not exist. In terms of your Lion, Liger, Leopard and Alligator example.. Typing L should only display the first three. Even though Alligator contains a match, I want the images to show/hide based on exact permutations of the words. So 'L' displays the first three, 'Li' the first two, and 'Lig' only the second image. Alligator won't show even though 'Lig' is in the word but it's not in the correct order..

I'm sorry if that's made it much more difficult.

I would think that we would just need to make a comparison after each keyup like we're doing but for each word individually not as a constant string of characters where any string might match up?