I've created an animation with jQuery which sequentially (in reverse) slides in lines of texts. It's pretty simple and works great when hovering once. When rapidly moving the mouse around however, the lines will animate in a seemingly random order.
Please see the GIF below for a demonstration. The first hover is how the animation should look. After that hover, I moved my mouse in and out to demonstrate the issue with the text lines animating at random.
Please see an example of my code below:
HTML
<div class="image-block">
<p>Text Line 1</p>
<p>Text Line 2</p>
<p>Text Line 3</p>
<p>Text Line 4</p>
<p>Text Line 5</p>
</div>
CSS
/* Hide text lines to begin with */
.image-block {
position: relative;
overflow: hidden;
background-image: url('background-image.png');
}
.image-block p {
position: absolute;
left: -120%
}
jQuery
jQuery(document).ready(function() {
// Animate text lines on hover with CSS 'left'.
jQuery('.image-block').hover(function() {
jQuery.fn.reverse = [].reverse;
time = 0;
speed = 300;
jQuery(this).find('p').reverse().each(function() {
jQuery(this).stop(true).delay(time).animate({
left: '0'
}, speed,
function() {
jQuery(this).stop(true).delay(time).animate({
left: '0'
}, speed);
})
time = time + 125;
speed = speed - 25;
});
}, function() {
// Animate text lines on hover release with CSS 'left'.
jQuery(this).find('p').reverse().each(function() {
jQuery(this).stop(true).animate({
left: '-120%'
}, 150)
});
});
});
How is it possible for the top two lines to animate first when rapidly moving the mouse? Do I need to reset my animation somehow after hover has been released? I added stop(true) to the animation but this hasn't fixed the problem.
Thank you in advance.
It got me scratching my head as well. As I've worked out this before, but for the sake of explanation had to go in detail.
The problem is with .delay() method which you're using with animation. When you add delay to animation, it can't be cleared. So when you hoverIn/hoverOut very fast, the handlers from previous hover events are not cleared and some p tags are animated out of order. For detailed explanation about .delay() check the selected answer here StackOverflow or better jQuery delay
About the solution to your problem. You can use setTimeout as a replacement for delay. I've made this jsfiddle as a workaround to solve your problem using setTimeout.
You can do it this way with setTimeout:
jQuery(document).ready(function() {
// Animate text lines on hover with CSS 'left'.
// To store setTimeout response
var animations = new Array(5);
jQuery('.image-block').hover(function() {
jQuery.fn.reverse = [].reverse;
time = 0;
speed = 300;
jQuery(this).find('p').reverse().each(function(index, item) {
// Clear previous handlers
clearTimeout(animations[index]);
// Set new handlers and add to `animations`
animations[index] = setTimeout(function(){
jQuery(item).stop(true).animate({
left: '0'
}, speed);
}, time);
time = time + 125;
speed = speed - 25;
});
}, function() {
// Animate text lines on hover release with CSS 'left'.
jQuery(this).find('p').each(function(index, item) {
// Clear previous handlers
clearTimeout(animations[index]);
jQuery(item).stop(true).animate({
left: '-120%'
}, 150);
});
});
});
You need to save setTimeout return values to a variable when adding animation for hover(MouseIn), so that you can clear those at the time of MouseOut. If You need more explanation about the fiddle, please comment back.
UPDATE: .clearQueue() was not actually needed, so removed it and updated the fiddle code as well.
Related
I've created some divs fading in with jQuery, I have a problem if the user scrolls a bit down though. If you are not at the top of the page, it will always jump to the bottom when a new div fades in.
Here's my code:
<style>
#overflowwrap {
overflow:hidden !important;
}
</style>
<div id="overflowwrap">
<div id="five" style="display: none;">a lot of content</div>
<div id="four" style="display: none;">a lot of content</div>
<div id="three" style="display: none;">a lot of content</div>
<div id="two" style="display: none;">a lot of content</div>
<div id="one" style="display: none;">a lot of content</div>
</div>
<script>
$('#overflowwrap').css('max-height',$(window).height());
$("#one").fadeIn(500);
setTimeout( function show() {
$("#two").fadeIn(500);
}, 3000);
setTimeout( function show() {
$("#three").fadeIn(500);
}, 6000);
setTimeout( function show() {
$("#four").fadeIn(500);
}, 9000);
setTimeout( function show() {
$("#five").fadeIn(500);
}, 12000);
</script>
Update: Example fiddle: https://jsfiddle.net/6qj1hbp0/1/
This wouldn't be a problem, if this was the only element on a page and the user couldn't scroll. However, it's integrated on another site (survey software), so the user is able to scroll.
Is there anything I can do to prevent the site from jumping to the bottom?
Try a different approach.
Instead, of display: none on every element, try opacity: 0;
Then instead of:
setTimeout( function show() {
$("#two").fadeIn(500);
}, 5000);
use:
setTimeout( function show() {
$("#two").addClass('is-visible);
}, 5000);
and add:
.is-visible { opacity: 1 !important; }
within your <style> tags.
you cannot “freeze” scroll, but you can read and change the scroll position, especially because you are using jQuery.
My solution consists in saving the current position of the scroll immediately before the fadeIn instruction then reassign the same value immediately after, with this function:
function fadeInElement(id, time) {
var currentScroll = $(window).scrollTop();
$('#' + id).fadeIn(time);
$(window).scrollTop(currentScroll);
}
Then you may call the same function several times with different ids and duration time, something like this:
fadeInElement('one', 500);
or this:
setTimeout(function() {
fadeInElement('two', 500);
}, 5000);
You may look a working example on CodePen or on JSFiddle
In short, the easiest thing you can do is hide the previous div every time you show a new one.
https://jsfiddle.net/6qj1hbp0/2/
$("#one").fadeIn(500);
setTimeout( function() {
$("#one").hide();
$("#two").fadeIn(500);
}, 3000);
setTimeout( function() {
$("#two").hide();
$("#three").fadeIn(500);
}, 6000);
setTimeout( function() {
$("#three").hide();
$("#four").fadeIn(500);
}, 9000);
setTimeout( function() {
$("#four").hide();
$("#five").fadeIn(500);
}, 12000);
If you want to fade from one box to the other (which creates a much smoother looking effect), you will need to do some other stuff - most notably:
put the boxes in order, top to bottom, #one to #five (you should do this anyways - it just makes sense congnatively)
set the position to absolute for each of the boxes
set some other styles (see the fiddle below)
use a special class while a box is fading in
https://jsfiddle.net/6qj1hbp0/3/
It's simple. Just reorder your div's to the order you want to show them instead of "five, four, three, two, one".
Your browser doesn't have any intention to take you to the bottom, it's just trying to keep your view point fixed on the current hash navigation. As your fading div is always above, your scrollTop will just jump to the bottom.
Another solution - if you don't want to reorder - is to remove all div id's and creating other way to recognize them, something like "data-id".
PS: look for some id's after too!
Do you need to restrict the overflow with hidden?
You can just set overflow: auto and browser will automatically take care of ensuring scrollTop remains the same after the fade in. The element user is looking at after scroll will remain at the same offset. If user hasn't scrolled then it will show the latest faded element at the top
Here's a jsfiddle: https://jsfiddle.net/sm2qaa3c/2/
After re-reading your comment, it seems you always want to display the latest faded div at the top. In that case you want a function to reset scrollTop to 0. You want to do it on overflowwrap not window since that's where the overflow scrolling will happen.
['#one', '#two', '#three', '#four', '#five'].forEach((id, idx) => {
setTimeout(() => {
$(id).fadeIn(500);
$('#overflowwrap').scrollTop(0);
}, idx * 5000);
});
See jsfiddle: https://jsfiddle.net/sm2qaa3c/3/
Thanks for the answers, they didn't work for my purpose though.
However, I've got a solution from another forum which doesn't require changing the functionality. This seems to work:
$('#overflowwrap').css('max-height', $(window).height());
fadeIn("#one", 0)
fadeIn("#two", 5000)
fadeIn("#three", 10000)
fadeIn("#four", 15000)
fadeIn("#five", 20000)
function cs() {
console.log(document.scrollingElement.scrollTop)
}
document.scrollingElement.scrollTop = 16
function fadeIn(el, when) {
setTimeout(function show() {
var t=document.scrollingElement.scrollTop
$(el).fadeIn(500);
document.scrollingElement.scrollTop = t
}, when);
}
Here is a working example on JSFiddle: https://jsfiddle.net/6qj1hbp0/4/
This may be a little too specific, but I have a jquery slider that I am using <p> classes instead of images to cycle through customer quotes. Basically the problem I am running into right now is when it is static and non moving (JS code is commeneted out) they are aligned how I want them to be. As soon as the JS is un commented, they stretch out of view and you just see a white box?
Any ideas?
How I want each panel to look like:
jsfiddle
So I sort of made this my Friday project. I've changed a whole lot of your code, and added a vertical-align to the quotes and authors.
Here's the fiddle http://jsfiddle.net/qLca2fz4/49/
I added a whole lot of variables to the top of the script so you could less typing throughout.
$(document).ready(function () {
//rotation speed and timer
var speed = 5000;
var run = setInterval(rotate, speed);
var slides = $('.slide');
var container = $('#slides ul');
var elm = container.find(':first-child').prop("tagName");
var item_width = container.width();
var previous = 'prev'; //id of previous button
var next = 'next'; //id of next button
Since you used a % based width I'm setting the pixel widths of the elements in case the screen is reszed
slides.width(item_width); //set the slides to the correct pixel width
container.parent().width(item_width);
container.width(slides.length * item_width); //set the slides container to the correct total width
As you had, I'm rearranging the slides in the event the back button is pressed
container.find(elm + ':first').before(container.find(elm + ':last'));
resetSlides();
I combined the prev and next click events into a single function. It checks for the ID of the element targeted in the click event, then runs the proper previous or next functions. If you reset the setInterval after the click event your browser has trouble stopping it on hover.
//if user clicked on prev button
$('#buttons a').click(function (e) {
//slide the item
if (container.is(':animated')) {
return false;
}
if (e.target.id == previous) {
container.stop().animate({
'left': 0
}, 1500, function () {
container.find(elm + ':first').before(container.find(elm + ':last'));
resetSlides();
});
}
if (e.target.id == next) {
container.stop().animate({
'left': item_width * -2
}, 1500, function () {
container.find(elm + ':last').after(container.find(elm + ':first'));
resetSlides();
});
}
//cancel the link behavior
return false;
});
I've found mouseenter and mouseleave to be a little more reliable than hover.
//if mouse hover, pause the auto rotation, otherwise rotate it
container.parent().mouseenter(function () {
clearInterval(run);
}).mouseleave(function () {
run = setInterval(rotate, speed);
});
I broke this in to its own function because it gets called in a number of different places.
function resetSlides() {
//and adjust the container so current is in the frame
container.css({
'left': -1 * item_width
});
}
});
//a simple function to click next link
//a timer will call this function, and the rotation will begin :)
And here's your rotation timer.
function rotate() {
$('#next').click();
}
It took me a little bit, but I think I figured out a few things.
http://jsfiddle.net/qLca2fz4/28/
First off, your console was throwing a few errors: first, that rotate wasn't defined and that an arrow gif didn't exist. Arrow gif was probably something you have stored locally, but I changed the 'rotate' error by changing the strings in the code here to your actual variables.
So, from:
run = setInterval('rotate()', speed);
We get:
run = setInterval(rotate, speed);
(No () based on the examples here: http://www.w3schools.com/jsref/met_win_setinterval.asp)
But I think a more important question is why your text wasn't showing up at all. It's because of the logic found here:
$('#slides ul').css({'left' : left_value});
You even say that this is setting the default placement for the code. But it isn't..."left_vaule" is the amount that you've calculated to push left during a slide. So if you inspect the element, you can see how the whole UL is basically shifted one slide's worth too far left, unable to be seen. So we get rid of 'left_value', and replace it with 0.
$('#slides ul').css({'left' : 0});
Now, there's nothing really handling how the pictures slide in, so that part's still rough, but this should be enough to start on.
Let me know if I misunderstood anything, or if you have any questions.
So, a few things:
1) I believe you are trying to get all of the lis to be side-by-side, not arranged up and down. There are a few ways to do this. I'd just make the ul have a width of 300%, and then make the lis each take up a third of that:
#slides ul {
....
width: 300%;
}
#slides li {
width: calc(100% / 3);
height:250px;
float:left;
}
2) You got this right, but JSFiddle automatically wraps all your JS inside a $(document).ready() handler, and your function, rotate needs to be outside, in the normal DOM. Just change that JSFiddle setting from 'onload' to 'no wrap - in head'
3) Grabbing the CSS value of an element doesn't always work, especially when you're dealing with animating elements. You already know the width of the li elements with your item_width variable. I'd just use that and change your code:
var left_indent = parseInt($('#slides ul').css('left')) - item_width;
$('#slides ul').animate({'left' : left_indent}, 1500, function () {
to:
$('#slides ul').stop().animate({'left' : -item_width * 2}, 1500, function () {
4) Throw in the .stop() as seen in the above line. This prevents your animations from overlapping. An alternative, and perhaps cleaner way to do this, would be to simply return false at the beginning of your 'next' and 'prev' functions if #slides ul is being animated, like so:
if ($('#slides ul').is(':animated')) return false;
And I think that's everything. Here's the JSFiddle. Cheers!
EDIT:
Oh, and you may also want to clearInterval at the beginning of the next and prev functions and then reset it in the animation callback functions:
$('#prev').click(function() {
if ($('#slides ul').is(':animated')) return false;
clearInterval(run);
$('#slides ul').stop().animate({'left' : 0}, 1500,function(){
....
run = setInterval('rotate()', speed);
});
});
I have a testimonials area on my website that fades from one testimonial to another. I'm having an issue where it will fade out too slowly before the next item fades in causing both to come up making a large div making it look ugly.
I want it to fade from one testimonial to another without jumping and flashing with both.
You can see an example here: http://ledragonvert.com/index_test.php
Here is my Javascript code:
function rotate_p() {
if (p_current == p_count) {
p_current = 1;
} else {
p_current++;
}
var $container = $('#container');
$container.find('p').fadeOut();
$container.find('p:nth-child(' + p_current + ')').fadeIn();
}
var p_count;
var p_current = 0;
var p_interval;
$(document).ready(function () {
rotate_p();
p_count = $('#container').find('p').length;
p_interval = setInterval(function () {rotate_p();}, 7000);
});
Thanks you very much for taking your time out to help me.
the solution is CSS based. since the position of the "p" element is static and you call both fadeOut and fadeIn, there is an overlap, as two p elements are inevitably shown together. To get them one on top of the other you need to use absolute positioning on the p element, like so:
#container {
position:relative;
}
#container>p {
position:absolute;
//use any values you wish, to set the testimonial relative to #container:
top:10px;
left:50px;
}
I am trying get this background to fade in on click. I found one tutorial that was helpful, and I ended up created the code so it has two images, and they fade in and out on click to bring up the picture.
Here's the work: http://www.mccraymusic.com/bgchangetest.html
Only a couple of issues though:
How do I make this work without the images getting selected at random? I'd like it to just switch from the plain black image to the image with the drum set. (And cross-fade to if possible, but not necessary)
How do I center the image on the page, so the image of the drums are centered?
I'm guessing this is what you're after:
$(function() {
var images = ["black.jpg","bg.jpg"];
$('<img>').attr({'src':'http://www.mccraymusic.com/assets/images/'+images[0],'id':'bg','alt':''}).appendTo('#bg-wrapper').parent().fadeIn(0);
$('.entersite').click(function(e) {
e.preventDefault();
var image = images[1];
$('#bg').parent().fadeOut(200, function() {
$('#bg').attr('src', 'http://www.mccraymusic.com/assets/images/'+image);
$(this).fadeIn(1000);
});
$(this).fadeOut(1000, function() {
$(this).remove();
});
});
});
DEMONSTRATION
Also added :
display: block;
margin-left: auto;
margin-right: auto;
to your #bg element to center the image.
Alright, assuming you use JQuery
You have #backgroundid and #imageid
Begin by setting
$('#backgroundid').css('opacity',1);
$('#imageid').css('opacity',0); // setting opacity (transparency) to 0, invisible
Now you have #buttonid.
Set up a jquery event so that when it's clicked, you fade out the background, and fade in the image using JQuery's animate.
$('#buttonid').click(function() {
$('#backgroundid').animate(function() {
opacity : 0 // fade it to 0 opacity, invisible
}, 1000); // animation will take 1000ms, 1second
$('#imageid').animate(function() {
opacity : 1 // fade it to full opacity, solid
}, 1000);
});
Now about that image centering.
You can either let css manage it with
body { /* Body or #imageid parent */
text-align : center;
}
#imageid {
margin: 0px auto;
}
Or you can stick to a JQuery solution, using absolute/fixed positioning.
First, use some css to fix the position of your image
#imageid {
position: absolute; // or fixed, if you want
}
Now use JQuery to reposition it
function positionImage() {
var imagewidth = $('#imageid').width();
var imageheight = $('#imageid').height();
$('#imageid').css('left', ($(window).width() - imagewidth) / 2);
$('#imageid').css('top', ($(window).height() - imageheight) / 2);
}
$(document).ready(positionImage); // bind the ready event to reposition
$(window).resize(positionImage); // on window resize, reposition image too
if you keep a div element with height and width as 100% and bgcolor as black. And then change the opacity of the div as desired to get the fade in/out effect, that should generate the same effect. I guess..
You are better off using any available jQuery plugin as they would have optimized and fixed bugs for multiple browsers.
Try lightBoxMe plugin
http://buckwilson.me/lightboxme/
This is the simplest plugin available!
I am looking for a jquery plugin that bounces a div element periodically infinite times. I have no such example website to show, but what I want is a div element that bounces to right(say) and bounces back to left. And at the time of bounce I want to change the content. Also bounce should go on. It should not stop or slow down.
I searched in
http://jquery.com/
http://mootools.net/
but found nothing that I wanted. There is bounce that stops after sometime.
Can you please help me?
If I have interpret your description correctly, you want a div that moves left and right continuously and changes content on "arrival". I'm still not sure if you want to toggle content or loop through more content.
A little illustration to clarify:
__________ __________
| | >>>>>>>>>>>>>>>>>> | |
| content1 | | content2 |
|__________| <<<<<<<<<<<<<<<<<< |__________|
|---------------------------------|
content change content change
Now, because the VERY specific request, I highly doubt there's such a plugin available. You just have to be creative yourself! Luckily, I'm a nice guy and save you some work.
See the online demo.
My javascript function:
function startBouncing(selector, content, duration, easing) {
// get the required movement (parent width - element width)
var movement = $(selector).parent().width() - $(selector).width();
var contentIndex = 0; // we want to start with content index 0
// define function that makes element go forth
var goForth = function() {
// start animation and change text
$(selector).animate({
'margin-left': movement
}, duration, easing, goBack).children('p:first').html(content[contentIndex % content.length]);
contentIndex++; // increment index for next time
};
// define function that makes element go back
var goBack = function() {
// start animation and change text
$(selector).animate({
'margin-left': 0
}, duration, easing, goForth).children('p:first').html(content[contentIndex % content.length]);
contentIndex++; // increment index for next time
};
// start the sequence
goForth();
}
Which I call using:
var content = [
'content #1',
'content #2',
'content #3'
]; // if you wish to toggle, just use 2 elements
// calling the function
startBouncing('#bouncer', content, 2000, 'linear');
And finally, the HTML:
<div style="background-color: gray; height: 50px; width: 500px;">
<div id="bouncer" style="background-color: #ff0000; height: 50px; width: 50px;">
<p>content</p>
</div>
</div>
It might not look good, but it works. I haven't spend a second in optimizing the code.
edit
I've edited the function so you specify a different duration and easing per side.
function startBouncing(selector, content, duration1, duration2, easing1, easing2) {
// get the required movement (parent width - element width)
var movement = $(selector).parent().width() - $(selector).width();
var contentIndex = 0; // we want to start with content index 0
// define function that makes element go forth
var goForth = function() {
// start animation and change text
$(selector).animate({
'margin-left': movement
}, duration1, easing1, goBack).children('p:first').html(content[contentIndex % content.length]);
contentIndex++; // increment index for next time
};
// define function that makes element go back
var goBack = function() {
// start animation and change text
$(selector).animate({
'margin-left': 0
}, duration2, easing2, goForth).children('p:first').html(content[contentIndex % content.length]);
contentIndex++; // increment index for next time
};
// start the sequence
goForth();
}
For more advanced easing strings, you should use an plugin that adds more easing strings like this one.
use should combine the animate function using an cool bouncing easing effect (requires jquery ui). i order to make it bounce repeatedly, use the setTimeout function..