3

I want to create simple product slider with thumbnails. Although there may be more than 3 thumbnails, I want to show only 3 of them. By clicking 'more' button i am aiming to hide first image which is already visible and show one from hidden images by changing their class.

Code works fine untill hiddenImages[0]. Firefox console gives the following error: "TypeError: hiddenImages[0] is undefined"

What i am doing wrong?

// All images
var images = $('[data-image]');

// Click for more images
var more = $('.more');


// Add show class to all images
images.each(function(index, element){$(this).parent().addClass('visible')})

// Hide images begining from 4th image
images.each(function(index, element){if($(this).data('image') >= 4)
{$(this).parent().removeClass('visible').addClass('hidden')} })

// Show big image when clicking thumbnail
images.each( function(index, element){
$(this).click(function(){ $('#pic img').attr('src', $(this).attr('src')) }) })


// Hide 1st from visible images and show first from hidden images
more.on('click', function(){
// Find all hidden images and remove visible class from first one
hiddenImages = images.hasClass('hidden');
hiddenImages[0].removeClass('visible').addClass('hidden');
})
#pic {
  width: 300px;
  height: 300px;
  border: 1px solid  #ccc;
  margin-right: 5px;
  float: left;
}

.thumbnails {
  height: 300px;
  width: 50px;
  padding:0; 
  margin:0;
  margin-right: 10px;
  float: left;

}

.thumbnails li {
  display: inline;
  list-style: none;
  float: left;
  width: 70px;
  height: 70px;
  margin-bottom: 5px;
  border:1px solid #ccc;
  text-align: center;
}

.thumbnails li img {
  width: 70px;
  height: 70px;
  cursor: pointer;
}

.more {
  display: inline;
  list-style: none;
  float: left;
  width: 70px;
  height: 70px;
  margin-bottom: 5px;
  border:1px solid #ccc;
  text-align: center;
}

.hidden {display: none!important;} 

.visible {display: block!important;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="holder">
	<div id="pic">
	<img src="https://placeimg.com/300/300/nature">
	</div>

	<ul class="thumbnails">
		<li><img data-image="1" src="https://placeimg.com/300/300/nature"></li>
		<li><img data-image="2" src="https://placeimg.com/300/300/any"></li>
		<li><img data-image="3" src="https://placeimg.com/300/300/animals"></li>
		<li><img data-image="4" src="https://placeimg.com/300/300/sepia"></li>
		<li><img data-image="5" src="https://placeimg.com/300/300/grayscale"></li>
		<li><img data-image="6" src="https://placeimg.com/300/300/tech"></li>
		<li class="more">MORE</li>
	</ul>

</div>

6
  • Try using .get(0) instead of [0] if you want the actual DOM element. If you still want the jQuery wrapped element, use .first() Commented Feb 28, 2018 at 18:20
  • get(0) gives another error " TypeError: hiddenImages.get is not a function" Commented Feb 28, 2018 at 18:21
  • Ohhh, yeah that's because .hasClass() returns a boolean. Commented Feb 28, 2018 at 18:22
  • Seems like you should simplify your overall strategy. Since the thumbnails seem to be a fixed size, why not just have a fixed size container that allows only 3 to be visible and hides the overflow. Then it's just a matter of adjusting the position of the container's content. Commented Feb 28, 2018 at 18:23
  • TypeError: hiddenImages.first is not a function Commented Feb 28, 2018 at 18:24

2 Answers 2

2

images.hasClass('hidden') doesn't do what you think it does. Check out the hasClass() docs. It returns a boolean.

Assuming that you're trying to get an array of objects with that class, you want to use find()

You're also going to want to confirm that the result of images.find('.hidden') returns an array of expected length (not demonstrated below).

more.on('click', function(){
    // Find all hidden images and remove visible class from first one
    hiddenImages = images.filter("[class~='hidden']");
    hiddenImages[0].removeClass('visible').addClass('hidden');
})

Here's a simple example with divs to illustrate:

let divsWithClass = $("div").filter("[class~='hidden']")
alert(`divs with 'hidden' class: ${divsWithClass.length}`);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="foo1"></div>
<div class="foo1"></div>
<div class="foo1"></div>
<div class="foo1"></div>
<div class="hidden"></div>
<div class="hidden"></div>
<div class="hidden"></div>
<div class="foo1"></div>
<div class="foo1"></div>
<div class="foo1"></div>

Sign up to request clarification or add additional context in comments.

7 Comments

images are the actual images, .find() will find children
Thanks, @mhodges, I missed that. Updated.
Looking more at this, it's actually the .parent() of the images that has the visible and hidden classes.
Hmm. If the OP can't get there using my example above, they can't get there :-)
@jeyhun_mikayil, Did I answer your question? If so, please click the check mark beside my answer. Also, As Alexander put in a fair amount of work below to get you pointed in the right direction, it would also be appropriate to vote up his answer as a thank you. See: stackoverflow.com/help/someone-answers
|
1

As pointed out already in an answer above the problem is hasClass() returns a Boolean and not a jQuery set of elements, subsequently even if hasClass() did return say a set of jQuery elements your next line of code would fail as it does because hiddenImages[0] is not a jQuery object. below is an example of how you could have 3 active images and interchange the classes:

$(function() {
  
    // All images
    var images = $('[data-image]'),
      // Click for more images
      more = $('.more');
  
    // Add show class to all images
    images.each(function(index, element) {
      $(this).parent().addClass('visible')
    })
  
    // Hide images begining from 4th image
    images.each(function(index, element) {
      if ($(this).data('image') >= 4) {
        $(this).parent().removeClass('visible').addClass('hidden')
      }
    })
  
    // Show big image when clicking thumbnail
    images.each(function(index, element) {
      $(this).click(function() {
        $('#pic img').attr('src', $(this).attr('src'))
      })
    })
  
  
    // Hide 1st from visible images and show first from hidden images
    var hiddenElemInfoObj = {
        idx: $('.thumbnails > li.hidden').first().index(),
        initialIdx: $('.thumbnails > li.hidden').first().index(),
        lengthOfElems: $('.thumbnails > li.hidden').length - 1 // To make length zero based 
      },
      visibleElemInfoObj = {
        idx: $('.thumbnails > li.visible').first().index(),
        initialIdx: $('.thumbnails > li.visible').first().index(),
        lengthOfElems: $('.thumbnails > li.visible').length - 1 // To make length zero based
      }
  
  
    more.on('click', function() {
  
      $thumbnails = $('.thumbnails');
  
      if (parseInt(hiddenElemInfoObj['idx']) > (parseInt(hiddenElemInfoObj['initialIdx']) + parseInt(hiddenElemInfoObj['lengthOfElems']))) {
        hiddenElemInfoObj['idx'] = $('.thumbnails > li.hidden').first().index();
        hiddenElemInfoObj['initialIdx'] = $('.thumbnails > li.hidden').first().index();
      }
  
      if (parseInt(visibleElemInfoObj['idx']) > (parseInt(visibleElemInfoObj['initialIdx']) + parseInt(visibleElemInfoObj['lengthOfElems']))) {
        visibleElemInfoObj['idx'] = $('.thumbnails > li.visible').first().index();
        visibleElemInfoObj['initialIdx'] = $('.thumbnails > li.visible').first().index();
      }
  
      $thumbnails
        .children('li')
        .eq(hiddenElemInfoObj.idx)
        .removeClass('hidden')
        .addClass('visible');
  
      $thumbnails
        .children('li')
        .eq(visibleElemInfoObj.idx)
        .removeClass('visible')
        .addClass('hidden');
  
      hiddenElemInfoObj['idx'] = parseInt(hiddenElemInfoObj['idx']) + 1;
      visibleElemInfoObj['idx'] = parseInt(visibleElemInfoObj['idx']) + 1;
  
    });
  
  
  });
#pic {
  width: 300px;
  height: 300px;
  border: 1px solid #ccc;
  margin-right: 5px;
  float: left;
}

.thumbnails {
  height: 300px;
  width: 50px;
  padding: 0;
  margin: 0;
  margin-right: 10px;
  float: left;
}

.thumbnails li {
  display: inline;
  list-style: none;
  float: left;
  width: 70px;
  height: 70px;
  margin-bottom: 5px;
  border: 1px solid #ccc;
  text-align: center;
}

.thumbnails li img {
  width: 70px;
  height: 70px;
  cursor: pointer;
}

.more {
  display: inline;
  list-style: none;
  float: left;
  width: 70px;
  height: 70px;
  margin-bottom: 5px;
  border: 1px solid #ccc;
  text-align: center;
}

.hidden {
  display: none !important;
}

.visible {
  display: block !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="holder">
		<div id="pic"><img src="https://placeimg.com/300/300/nature"></div>
		<ul class="thumbnails">
			<li><img data-image="1" src="https://placeimg.com/300/300/nature"></li>
			<li><img data-image="2" src="https://placeimg.com/300/300/any"></li>
			<li><img data-image="3" src="https://placeimg.com/300/300/animals"></li>
			<li><img data-image="4" src="https://placeimg.com/300/300/sepia"></li>
			<li><img data-image="5" src="https://placeimg.com/300/300/grayscale"></li>
			<li><img data-image="6" src="https://placeimg.com/300/300/tech"></li>
			<li class="more">MORE</li>
		</ul>
  </div>

3 Comments

@aleksander-solonik Your approach is much better. Thank you. But there is a minor issue. After 3rd click of "MORE" button regular changing method of thumbnails doesnt work.
@jeyhun_mikayil what do you mean by regular changing meathod ? can you be a bit more specific ?
in default images changes one by one, but suddenly function changes 2 or more images simultaneosly

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.