Yet Another Image Slider JQuery Question

I have trying for some time. And I gave a look at the image slider questions already. But I would really appreciate if I get some specific help here. Please tell me where the following code went wrong.

Here’s my jQuery code:

function slideMe {
$('.imgcontainer').animate({"margin-left":"-= 100%"}, 2000, function () {
  if($('.imgcontainer').css("margin-left") == (-300%)) {
    $('.imgcontainer').css("margin-left", "0px");
  }
 slideMe();
});

} window.onload = slideMe();

And here’s the script on HTML page:

<div class="fitimage">
<div class="imgcontainer">
<img src="img/copywrtng.jpg" class="headerimage" alt="copywriting" />
<img src="img/copywrtng1.jpg" class="headerimage" alt="copywriting" />
<img src="img/copywrtng2.jpg" class="headerimage" alt="copywriting" />
</div>
</div>

And here’s what I have on CSS:

div.fitimage {
width:100%;
height:93vh;
overflow: hidden;
}
img.headerimage {
padding: 0;
margin:0 auto;
width:100%;
/*max-height:100%;
max-width:100%;*/
height:93vh;
}

My logic is that as each image takes up 100% width, as the “.imgcontainer” div slides left with margin-left: -100%, each image should come up one by one from the right, until it reaches the last one when it reverts to the first image again.

It’s not working!

Please help.

Hi,

Img sliders need a lot of consideration so you may want to look at this article which ggoes into some detail.

You are missing the function call brackets here:

function slideMe

It should be:

function slideMe()

Your logic is flawed as your images are vertical to start with because they only have 100% space to play with and so drop to the next line so even if your js was working the whole block would slide away in one fell swoop. You need to ensure the images are horizontal to start with which can be accomplished by setting white-space to nowrap and forcing them into one line and overflow the parent.

You could them move the inner container by 100% with your jquery. However you won’t be able to check for 300% (3 images) as I believe jquery returns a pixel value. Instead just set a counter and when that reaches 3 reset it.

Of course when the third image has passed you will get a nasty jump back to the start and proper image sliders would either slide back to the beginning nicely or swap the images so that they keep looping smoothly.

Bearing in mind my JS is pretty basic your code would look something like this:

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Untitled Document</title>
<style>
html,body{margin:0;padding:0;}
div.fitimage {
	width:100%;
	height:93vh;
	overflow: hidden;
	font-size:0;
	white-space:nowrap;
}
img.headerimage {
	width:100%;
	height:93vh;
	display:inline-block;
}
.imgcontainer {
	width:100%;
}
</style>
</head>

<body>
<div class="fitimage">
  <div class="imgcontainer"> 
  <img src="http://www.placehold.it/350x150" class="headerimage" alt="copywriting1"> 
  <img src="http://www.placehold.it/350x150" class="headerimage" alt="copywriting2"> 
  <img src="http://www.placehold.it/350x150" class="headerimage" alt="copywriting3"> 
  </div>
</div>
<script
      src="https://code.jquery.com/jquery-2.2.4.min.js"
      integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
      crossorigin="anonymous"></script> 
<script>
$(document).ready(function() {
    (function() {
        var numimages = 3,
			counter = 0;

        function slideMe() {
            $('.imgcontainer').animate({
                "margin-left": "-=100%"
            }, 2000, function() {
                counter += 1;
                if (counter === numimages) {
                    $('.imgcontainer').css("margin-left", "0");
                    counter = 0;
                }
                slideMe();
            });
        }
        slideMe();
    }());
});
</script>
</body>
</html>

You don’t need the window onload if you simply put the script at the end of the html. (I wrapped it in a document ready anyway in case you moved it somewhere else).

Note that your css is stretching the image which is not a good idea and when you set width:100% you really should let the image be height:auto so that aspect ratio is maintained. If you want full screen images then use background images instead and use background-size:cover instead.

Lastly if you are just sliding 3 images with no logic then CSS can do that by itself without js at all.

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Untitled Document</title>
<style>
html,body{margin:0;padding:0;}
div.fitimage {
	width:100%;
	height:93vh;
	overflow: hidden;
	font-size:0;
	white-space:nowrap;
}
img.headerimage {
	width:100%;
	height:93vh;
	display:inline-block;
}
.imgcontainer {
	width:100%;
	animation:slide 6s ease-in-out infinite;
}
@keyframes slide{
	0%{transform:translateX(0)}
	25%{transform:translateX(-100%)}
	50%{transform:translateX(-200%)}
	75%{transform:translateX(-200%)}
	100%{transform:translateX(0)}
}

</style>
</head>

<body>
<div class="fitimage">
  <div class="imgcontainer"> 
  <img src="http://www.placehold.it/350x150" class="headerimage" alt="copywriting1"> 
  <img src="http://www.placehold.it/350x150" class="headerimage" alt="copywriting2"> 
  <img src="http://www.placehold.it/350x150" class="headerimage" alt="copywriting3"> 
  </div>
</div>
</body>
</html>

However, I would urge you to look at some professional sliders as there are many tried and tested ones about unless of course this is just practice for learning then by all means keep plugging away and testing.:slight_smile:

4 Likes

Thanks so much. Really, really appreciate your help @PaulOB.

Just one question, you said that the .imgcontainer is being stretched and that’s why it should have 100% width. I didn’t understand this. .imgcontainer should contain 3 100% wide images, right? So, that should be 300% in width.

And please look in this JSBin link. There is a little jerk when the whole thing restarts. Can I remove that jerk?

The parent should be 100% width and each image is set to 100% width of the parent. The images are then forced horizontally and overflow the parent. We don’t need to know what width they might be altogether as that will change depending on the number of images.

Jquery animations are often jumpy (especially in Chrome windows) but you may want to try running the code outside of a the fiddle as there is a lot of overhead in that code. You only need the link to jquery and not multiple versions or the jquery UI.

Also your images are very large at 700k each and you need to optimise them down to about 70k depending on the size you are viewing them at. I would rarely let an image go over 100k in a slider unless its a very slow or full screen slider.

The css version would be smoother of course.

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Untitled Document</title>
<style>
html,body {
  margin:0;
  padding:0;
}
.container {
  margin:0 auto;
  height: 250px;
  width: 55%;
  overflow:hidden;
}
.slider {
   white-space:nowrap;
   width:100%;
   height:250px;
  animation:slide 4s ease infinite;
}
.slide1{background:url(http://www.iamwire.com/wp-content/uploads/2017/02/free-business-plan-templates.jpg) no-repeat 50% 50%;}
.slide2{background:url(http://www.megri.com/wp-content/uploads/global-business.jpg) no-repeat 50% 50%;}
.slide3{background:url(http://study.com/cimages/course-image/praxis-ii-business-education-test_118084_large.jpg) no-repeat 50% 50%;}
.slide{
	width:100%;
	height:100%;
	background-size:cover;
	display:inline-block;
}

@keyframes slide{
	0%{transform:translate3d(0,0,0)}
	33%{transform:translate3d(-100%,0,0)}
	66%{transform:translate3d(-200%,0,0)}
	100%{transform:translate3d(-300%,0,0)}
}

</style>
</head>

<body>
<div class="container">
 <div class="slider">
 	<div class="slide slide1"></div>
 	<div class="slide slide2"></div>
 	<div class="slide slide3"></div>
 	<div class="slide slide1"></div>
 </div>
</div>

</body>
</html>

Yes, I tried my own CSS version, it didn’t work to my satisfaction. I am trying this code now. And just to clarify a bit of theory, here’s what I am saying:

When I am setting the width of “.imgcontainer” div to 100%, am I not restricting it to one single box. I mean, it is supposed to hold all 3 boxes and the whole red box is supposed to move when margin-left:-100% is done, isn’t it?

No you don’t need to know the total width and indeed setting a 300 percent would make it awkward as the image’s width would then be based on 300 percent.

The outer container must be 100% and overflow hidden. The inner container is then automatically stretched to 300 percent because we forced the inner content not to wrap but to overflow the parent.

The inner container is them moved with a 100% margin based on the width of the containing block (the parent).

All that is needed is to allow the inner container to overflow horizontally. With this approach you can have 3 or 300 images without changing the CSS. :slight_smile:

Ah, I think I got the idea here.

One thing is baffling me - how to put a mouseover/mouseout or mouseenter/mouseleave into the equation. I just can’t seem to make it work in the jquery animation. Can you help?

Here’s the JSBin. I have already used a few JS and jQuery code there. But the above functions are not working.

Also here’s another jQuery code.

$(function() {
	var animVar = $('.slider').css("animation-play-state");
	$('.slider').css("animation-play-state", "running");
	$('.slider').click(function() {
			$(this).css("animation-play-state", function() {
				if (animVar === "running") {
					return "paused";
				} else {
					return "running";
				}
			});//css function
			/*return (animVar === "running" ? "paused" : "running"); */
	});	//click function
});

If I apply this code, once I click on it, it stops and doesn’t resume on clicking again. Is it because ‘style’ attribute gets activated?

You’re not changing the animVar variable to ‘paused’ so it always tests as ‘running’.

Do this:

$(function() {
	var animVar = $('.slider').css("animation-play-state");
	$('.slider').css("animation-play-state", "running");
	$('.slider').click(function() {
			$(this).css("animation-play-state", function() {
				if (animVar === "running") {
					animVar = 'paused';
					return "paused";
				} else {
					animVar = 'running';
					return "running";
				}
			});
			
	});
});

However you could just toggle a class and let css work it all out.

e.g.

$(function() {
	$('.slider').click(function() {	$( this ).toggleClass('paused');});
});

CSS:

.paused {
  animation-play-state: paused;
 }

If you just want it paused on hover then no js is needed at all.
e.g.

.slider:hover{
  animation-play-state: paused;
}
1 Like

Thanks for the CSS code. Actually, as you might have probably guessed by now, I am practicing jQuery and JS. :smile:

But I didn’t get why we would have to change the value of animVar. I mean, in the function below…[quote=“PaulOB, post:8, topic:262117”]
$(‘.slider’).click(function() {
$(this).css(“animation-play-state”, function() {
if (animVar === “running”) {
return “paused”;
} else {
return “running”;
}
});

});

[/quote]

Aren’t we already changing the CSS property of ‘.slider’? If we are doing it, isn’t animVar already getting updated?

Also, if it would not too bothersome to you (I know I am asking a lot of questions here), can you please tell me why mouseenter/mouseexit or the hover is not working? Tried them but in vain.

animVar is set on page load so is always set to the value of ‘running’.

In your condition you test for running:


	if (animVar === "running") {
					//animVar = 'paused';
					return "paused";

The routine is true and so the animation-play-state is set to paused.

The next time you click the slider the routine does exactly the same because animVar is still set as ‘running’ and therefore the play-state remains paused.

The ‘else’ part of your logic never gets seen because animVar is always set to ‘running’.

You need to change its value as shown in my additions or do another check of the play-state and assign it to the variable but that would be a waste of time and resources because we already know what it will be so we set it directly.

No because you never check it again after the very first page load.

If you put the check for play-state inside your conditional logic you could check it again but as I pointed above there would be no point and you’d be slowing the script down for no reason.

Where’s your JS code for this? (The css version I gave you works fine :))

To make it pause on hover you would need to revise the script like this.

<script>
$(function() {
    var el = $('.slider');
    el.css("animation-play-state", "running");

    el.hover(
        function() {
            el.css("animation-play-state", "paused");
        },
        function() {
            el.css("animation-play-state", "running");
        }
    );
});
</script>

No need to test for anything just turn it off on mousenter and on on mousleave (jquery .hover() is a shorthand for both those handlers).

The .hover() method binds handlers for both mouseenter and mouseleave events. You can use it to simply apply behavior to an element during the time the mouse is within the element.

Of course you don’t need jquery for basic hover as CSS does that much better.:slight_smile:

Also remember that touch devices don’t have hover so you may want to stick with click or cater for touch events also.

1 Like

I am bugging you with questions I know. I am sorry for that. Need to clarify the theory here. I might going horridly wrong somewhere.

Here’s a little image of what I am thinking.

What I don’t understand here is, let’s say, I click on the slide. It reads from the $(function () {…

The animVar property is set to “running” and the condition is met and then…

$(‘.slider’).css(“animation-play-state”, “paused”); happens.

My question is, it has now updated the $('.slider').css("animation-play-state", "running"); at the top and it in turn, changes the animVar to “paused”.

So, on the next click, shouldn’t the else condition run?

I must note here that I have already did what you suggested by alerting the play state and found out that you are absolutely right. When the animVar is not updated inside, it is just not reaching the else condition on further clicks.

But here’s where I am getting confused.

I mean, inside the click(function() {…I have already updated the play state.

Moreover, considering the other hypothesis if I understand it correctly that on each successive click, the function is being read from the top again, and the animVar is set to “running” again, then…

Why would it work if I update animVar inside the if statement? I mean, it is getting overridden as the $('.slider').css("animation-play-state", "running"); is being called again in each successive click.

Even updating inside the if/else statements shouldn’t work, right?

I am so sorry to ask you so many questions. I have got the solution alright (thanks to you). Just couldn’t understand what’s going on behind the whole thing.

Your logic is not correct as only the code inside the click function is used each time someone clicks.

All the stuff outside the click function only runs when the page first loads. It is not called again.

I think that’s where you are getting confused:).

Aaaah! Light bulb on!

The browser doesn’t go up, it reads top to bottom.

So, when I click again on the slide, it doesn’t go up to change the variable. But it changes the CSS play state property within the click() block.

On the other hand, the animVar is always set to “running” and doesn’t get updated, so that we can move into the else statement.

Please correct me if I am wrong anywhere.

You got it :slight_smile:

1 Like

Thank you so much for the help, @PaulOB :wink:

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.