JQuery - animate moving DOM element to new parent? - javascript

I have an image tag inside of a table cell, that I'd love to move to another table cell, and have that movement animated.
The code looks something like this...
<td id="cell1"><img src="arrow.png" alt="Arrow"/></td>
<td id="cell2"></td>
I'd like to move "arrow.png" to "cell2", and have some kind of transition effect, preferably with JQuery.
Any ideas?
Thanks!

This is actually quite difficult because you have to remove and add it to the DOM but keep its position. I think you're looking for something like this. Basically we don't animate either the arrow in #cell1 or #cell2. We just create a new one in the body-tag and animate that. That way we don't have to worry about the table cell positions because we can position relative to the document.
var $old = $('#cell1 img');
//First we copy the arrow to the new table cell and get the offset to the document
var $new = $old.clone().appendTo('#cell2');
var newOffset = $new.offset();
//Get the old position relative to document
var oldOffset = $old.offset();
//we also clone old to the document for the animation
var $temp = $old.clone().appendTo('body');
//hide new and old and move $temp to position
//also big z-index, make sure to edit this to something that works with the page
$temp
.css('position', 'absolute')
.css('left', oldOffset.left)
.css('top', oldOffset.top)
.css('zIndex', 1000);
$new.hide();
$old.hide();
//animate the $temp to the position of the new img
$temp.animate( {'top': newOffset.top, 'left':newOffset.left}, 'slow', function(){
//callback function, we remove $old and $temp and show $new
$new.show();
$old.remove();
$temp.remove();
});
I think this should point you in the right direction.

#Pim Jager's answer is pretty good, however if you have object references to the original element they would break since the the original element was replaced with a clone
I came up with what I think is a slightly cleaner solution in that it only has a single clone that show up for animation then goes away, leaving the original in the new location.
function moveAnimate(element, newParent){
//Allow passing in either a jQuery object or selector
element = $(element);
newParent= $(newParent);
var oldOffset = element.offset();
element.appendTo(newParent);
var newOffset = element.offset();
var temp = element.clone().appendTo('body');
temp.css({
'position': 'absolute',
'left': oldOffset.left,
'top': oldOffset.top,
'z-index': 1000
});
element.hide();
temp.animate({'top': newOffset.top, 'left': newOffset.left}, 'slow', function(){
element.show();
temp.remove();
});
}
To use: moveAnimate('#ElementToMove', '#newContainer')

You'll need to do this in two steps: (1) animation (2) rehoming.
The animation you can take care of with .animate(), as #Ballsacian points out. The rehoming can be accomplished with .html() - for the example above,
var arrowMarkup = $('#cell1').html(); //grab the arrow
$('#cell1').html(""); //delete it from the first cell
$('#cell2').html(arrowMarkup); //add it to the second cell
Of course, you'll have to complicate that code to integrate the animation. And this way of doing it won't cause the selection (I'm assuming you're selecting a table row?) to activate rows between the old selection and the new one, as the arrow passes by them. That'd be even more complex to achieve.

I have extended one of the other answers a little further so that now you can pass an object as a third parameter which serves as a vehicle during the animation. For example, if you want to move some <li> from one <ul> to another, your <ul> likely has a certain class that gives the <li> its styling. So, it would really be handy to animate your <li> inside a temporary vehicle <ul> that provides for the same styling as either the source or the target <ul> of the animation:
//APPENDS AN ELEMENT IN AN ANIMATED FASHION
function animateAppendTo(el, where, float){
var pos0 = el.offset();
el.appendTo(where);
var pos1 = el.offset();
el.clone().appendTo(float ? float : 'body');
float.css({
'position': 'absolute',
'left': pos0.left,
'top': pos0.top,
'zIndex': 1000
});
el.hide();
float.animate(
{'top': pos1.top,'left': pos1.left},
'slow',
function(){
el.show();
float.remove();
});
}

I was trying #Davy8's function which is quite good, but I found it quite jarring when the moved element snapped off the page at the start then back in at the end. The other page elements suddenly shifting interrupted an otherwise smooth animation, but this likely would depend on your page layout.
So this is a modified version of #Davy8's function, which should also smoothly shrink and grow space between parents.
function moveAnimate(element, newParent,
slideAnimationSpeed/*=800*/, spacerAnimationSpeed/*=600*/)
{
//Allow passing in either a jQuery object or selector
element = $(element);
newParent= $(newParent);
slideAnimationSpeed=slideAnimationSpeed||800;
spacerAnimationSpeed=spacerAnimationSpeed||600;
var oldOffset = element.offset();
var tempOutgoing=element.clone().insertAfter(element);
tempOutgoing.hide(); //Don't take up space yet so 'newOffset' can be calculated correctly
element.appendTo(newParent);
var newOffset = element.offset();
var tempMover = element.clone().appendTo('body');
tempMover.css({
'position': 'absolute',
'left': oldOffset.left,
'top': oldOffset.top,
'z-index': 1000,
'margin':0 //Necessary for animation alignment if the source element had margin
});
element.hide();
element.show(spacerAnimationSpeed).css('visibility', 'hidden'); //Smoothly grow space at the target
tempMover.animate({'top': newOffset.top, 'left': newOffset.left}, slideAnimationSpeed, function(){
element.css('visibility', 'visible');
tempMover.remove();
});
tempOutgoing.show().css('visibility', 'hidden');
tempOutgoing.hide(spacerAnimationSpeed, function(){ tempOutgoing.remove() }); //smoothly shrink space at the source
}

If the animation doesn't have to be the thing moving, this question which uses fadeIn and fadeOut gives a simple, clean answer with no cloning and still conveys the motion quite well:
Re-ordering div positions with jQuery?

For anyone still viewing this, I found the provided examples didn't fit exactly what I wanted and they didn't account for margins, so here's my version:
jQuery.fn.extend({
moveElement : function (newParent, speed, after) {
var origEl = $(this);
var moveToEl = $(newParent);
var oldOffset = origEl.offset();
var temp = origEl.clone().appendTo('body');
temp.css({
'position' : 'absolute',
'left' : parseInt(oldOffset.left) - parseInt(origEl.css('margin-left')),
'margin' : origEl.css('margin'),
'top' : oldOffset.top,
'z-index' : 1000,
'height' : moveToEl.innerHeight(),
'width' : moveToEl.innerWidth()
});
var blankEl = $('<div></div>').css({
height : moveToEl.innerHeight(),
margin : moveToEl.css('margin'),
position : 'relative',
width : moveToEl.innerWidth()
});
if (after) {
origEl.insertAfter(moveToEl);
blankEl.insertAfter(newParent);
}
else {
origEl.insertBefore(moveToEl);
blankEl.insertBefore(newParent);
}
origEl.hide();
var newOffset = blankEl.offset();
temp.animate({
'top' : blankEl.offset().top - parseInt(moveToEl.css('margin-top')),
'left' : newOffset.left - parseInt(moveToEl.css('margin-left'))
}, speed, function () {
blankEl.remove();
origEl.show();
temp.remove();
});
}
});
Move an element before another: $('.elementToFind').moveElement('.targetElement', 1000);
Move an element after another: $('.elementToFind').moveElement('.targetElement', 1000, 'after');

JQuery http://docs.jquery.com/Downloading_jQuery
JQuery Effects http://docs.jquery.com/Effects/animate#paramsoptions
Example
$("#go1").click(function(){
$("#block1").animate( { width:"90%" }, { queue:false, duration:3000 } )
.animate( { fontSize:"24px" }, 1500 )
.animate( { borderRightWidth:"15px" }, 1500);
});

Related

Wow.js repeat animation every time you scroll up or down

I'm pretty new with Jquery. I would like that my animations with Wow.js could run more than once time. For instance: i scroll to the bottom of my page and see all the animations, and if i scroll back to the top i see again the animations like when you scroll down. I hope that I explained myself. I have already seen many websites that repeats the animations on theirs pages but unfortunately I don't remember them and I can't provide a link.
I have already tried this:
$(window).scroll(function(){
new WOW().init();
}
But it repeat the animations also if you scroll a little and it's pretty ugly to see. I try to explain me better: I have a with my animation and if it is focused the animation is triggered, then i scroll down to another div and the previous div is no more visible(not in the window viewport), then again i scroll back to my div with animation and the animation is triggered again.
I'm sorry for this messy question but I really don't know how to explain it.
Thanks in advance!
This example by BenoƮt Boucart shows how the animation can be "reset" when the user scrolls out of view and back in. The key here is the second function that removes the animation css class when the element scrolls out of view. I wish WOW.js would implement this, but they've indicated that they don't plan to.
http://codepen.io/benske/pen/yJoqz
Snippet:
// Showed...
$(".revealOnScroll:not(.animated)").each(function () {
var $this = $(this),
offsetTop = $this.offset().top;
if (scrolled + win_height_padded > offsetTop) {
if ($this.data('timeout')) {
window.setTimeout(function(){
$this.addClass('animated ' + $this.data('animation'));
}, parseInt($this.data('timeout'),10));
} else {
$this.addClass('animated ' + $this.data('animation'));
}
}
});
// Hidden...
$(".revealOnScroll.animated").each(function (index) {
var $this = $(this),
offsetTop = $this.offset().top;
if (scrolled + win_height_padded < offsetTop) {
$(this).removeClass('animated fadeInUp flipInX lightSpeedIn')
}
});
If a user wants to repeat the animation on both the events i.e.
onScrollUp
onScrollDown
then this will be a good solution for it:
First create an addBox function, it will help to push new elements into the WOW boxes array.
WOW.prototype.addBox = function(element){
this.boxes.push(element);
};
Then use jQuery and scrollspy plugin that helps to detect which element is out of the view and then push WOW as:
$('.wow').on('scrollSpy:exit',function(){
var element = $(this);
element.css({
'visibility' : 'hidden',
'animation-name' : 'none'
}).removeClass('animated');
wow.addBox(this);
});
Solution Courtesy: ugurerkan
Answer by #vivekk is correct I m just adding a working example so that people can easily get this
see the Demo fiddle
<script>
// Repeat demo content
var $body = $('body');
var $box = $('.box');
for (var i = 0; i < 20; i++) {
$box.clone().appendTo($body);
}
// Helper function for add element box list in WOW
WOW.prototype.addBox = function(element) {
this.boxes.push(element);
};
// Init WOW.js and get instance
var wow = new WOW();
wow.init();
// Attach scrollSpy to .wow elements for detect view exit events,
// then reset elements and add again for animation
$('.wow').on('scrollSpy:exit', function() {
$(this).css({
'visibility': 'hidden',
'animation-name': 'none'
}).removeClass('animated');
wow.addBox(this);
}).scrollSpy();
</script>

Using a jquery slider for text instead of images?

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);
});
});

js div overlay not working in IE

I have this div that overlays images to color them blue when you mouseover. Works nicely! Except - it doesn't seem to work in IE at all.
Any ideas?
The js
http://www.rollinleonard.com/elements/overlaymouseover.js
The page
http://www.rollinleonard.com/elements
Thanks!
IE doesn't yet support rgba. IE9 beta does. In your case, since you don't have any text on the overlay, you don't need to set background opacity. Just set regular opacity on your #overlay.
#overlay{
...
background-color: rgb(0, 0, 255);
-moz-opacity:.60; filter:alpha(opacity=60); opacity:.60;
...
}
http://davidwalsh.name/css-opacity
http://css-tricks.com/rgba-browser-support/
Update: Like you mentioned, the clicks don't go through to the links. One approach is to add a handler to the overlay, copying the underlying link.
$(window).load(function(){
var $overlay = $('#overlay');
$('img').bind('mouseenter', function () {
var $this = $(this);
if ($this.not('.over')) {
$this.addClass('over');
$overlay.css({
width : $this.css('width'),
height : $this.css('height'),
top : $this.offset().top + 'px',
left : $this.offset().left + 'px',
}).show();
// This is hacked up,could be better, but works, it replaces the handler
// everytime you display it
$overlay.onclick = function() {
location.href = $this.getAttribute('href');
}
}
}).bind('mouseout', function () {
$(this).removeClass('over');
});
});
Use keyword var to declare your variables:
instead of:
$overlay = $('#overlay');
Use:
var $overlay = $('#overlay');
Same thing with $this = $(this);
Update --
Not sure what I was thinking.
As long as you are making an assignment your javascript is valid, however the error in IE is coming from line 15 of overlaymouseover.js:
left : $this.offset().left + 'px', // extra comma breaks IE
And that is your problem.

javascript - position not being set properly on page load

I am creating a coverflow plugin but I have a slight problem when it first loads.
The size/styles of the images is set based on their position in the coverflow. When the page first loads the images all resize properly but they do not reposition themselves. If I them use the left and right navigation they work correctly.
I am not sure what is causing this. I thought it might be something to do with the variable that sets the starting position of the coverflow...
Here's my code:
<script type="text/javascript" src="/scripts/jquery-ui.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var coverflowPos = Math.round($('#coverflow img').length / 2)
$('#coverflow img').each( function(i) {
$(this).css({'opacity' : 1-(Math.abs(coverflowPos-i)*0.4), 'z-index' : 100-(Math.abs(coverflowPos-i)) }).width(200-(Math.abs(coverflowPos-i)*50)).height(128-(Math.abs(coverflowPos-i)*50));
});
// If I run the testme() function here, it animates to the right place but I want it to start in this position rather than animate to it
$('#moveLeft').click( function() {
if(coverflowPos > 1) {
coverflowPos = coverflowPos-1
}
testme();
});
$('#moveRight').click( function() {
if(coverflowPos < $("#coverflow img").length -1) {
coverflowPos = coverflowPos+1
}
testme();
});
function testme() {
$('#coverflow img').each( function(i) {
$(this).animate({
opacity: 1-(Math.abs(coverflowPos-i)*0.4),
width: 200-(Math.abs(coverflowPos-i)*50),
height: 128-(Math.abs(coverflowPos-i)*50)
}, {
duration: 500,
easing: 'easeInOutSine'
}).css({ 'z-index' : 100-(Math.abs(coverflowPos-i)) });
});
};
});
</script>
And here's a link to a jsfiddle:
http://jsfiddle.net/r8NqP/4/
Calling testme() at the end of the ready() function moves them into place. It does ease them in though, which looks a bit odd, could get rid of the ease in testme() by adding a doease parameter.
Check you fist each :
'z-index' : 100-(Math.abs(coverflowPos-i)) }).width(200-(Math.abs(coverflowPos-i)*50)).height(128-(Math.abs(coverflowPos-i)*50));
I think U mean:
'z-index' : 100-(Math.abs(coverflowPos-i)),
'width' : 200-(Math.abs(coverflowPos-i)*50),
'height': 128-(Math.abs(coverflowPos-i)*50)
Linke In your testme() function ?!
After that, you can also add a "Hack", by executing testme(true); at the end of script.
And add, in your testme() function , a test parameter to set the duration at 0 or simply disable animate and replace by CSS().
But, it just a Hack.
200-(Math.abs(coverflowPos-i)*50) may be less than 0 -- e.g.,
200-(5-0)* 50= 200 - 250 = -50
And the negative width ends up not being applied, leaving the width at its original 200px value. The opacity gets set properly, so all you get is a huge blank space where the image is.
var width = 200-(Math.abs(coverflowPos-i)*50);
if ( width < 0 ) width = 0;
covers the init nicely.
I haven't bothered to check why it's okay once it's animated -- my guess is, that the images were already small, so it's not as noticeable.
The problem came from "Each index", that not correctly used to compute the Width and Height of the first image.
Try this :
$('#coverflow img').each( function(i) {
i++;
$(this).css({...
And remove the Blank.gif...
Here, you find my fork fiddle : http://jsfiddle.net/akarun/FQWQa/

How do I get an element to scroll into view, using jQuery?

I have an HTML document with images in a grid format using <ul><li><img.... The browser window has both vertical & horizontal scrolling.
Question:
When I click on an image <img>, how then do I get the whole document to scroll to a position where the image I just clicked on is top:20px; left:20px ?
I've had a browse on here for similar posts...although I'm quite new to JavaScript, and want to understand how this is achieved for myself.
There's a DOM method called scrollIntoView, which is supported by all major browsers, that will align an element with the top/left of the viewport (or as close as possible).
$("#myImage")[0].scrollIntoView();
On supported browsers, you can provide options:
$("#myImage")[0].scrollIntoView({
behavior: "smooth", // or "auto" or "instant"
block: "start" // or "end"
});
Alternatively, if all the elements have unique IDs, you can just change the hash property of the location object for back/forward button support:
$(document).delegate("img", function (e) {
if (e.target.id)
window.location.hash = e.target.id;
});
After that, just adjust the scrollTop/scrollLeft properties by -20:
document.body.scrollLeft -= 20;
document.body.scrollTop -= 20;
Since you want to know how it works, I'll explain it step-by-step.
First you want to bind a function as the image's click handler:
$('#someImage').click(function () {
// Code to do scrolling happens here
});
That will apply the click handler to an image with id="someImage". If you want to do this to all images, replace '#someImage' with 'img'.
Now for the actual scrolling code:
Get the image offsets (relative to the document):
var offset = $(this).offset(); // Contains .top and .left
Subtract 20 from top and left:
offset.left -= 20;
offset.top -= 20;
Now animate the scroll-top and scroll-left CSS properties of <body> and <html>:
$('html, body').animate({
scrollTop: offset.top,
scrollLeft: offset.left
});
Simplest solution I have seen
var offset = $("#target-element").offset();
$('html, body').animate({
scrollTop: offset.top,
scrollLeft: offset.left
}, 1000);
Tutorial Here
There are methods to scroll element directly into the view, but if you want to scroll to a point relative from an element, you have to do it manually:
Inside the click handler, get the position of the element relative to the document, subtract 20 and use window.scrollTo:
var pos = $(this).offset();
var top = pos.top - 20;
var left = pos.left - 20;
window.scrollTo((left < 0 ? 0 : left), (top < 0 ? 0 : top));
Have a look at the jQuery.scrollTo plugin. Here's a demo.
This plugin has a lot of options that go beyond what native scrollIntoView offers you. For instance, you can set the scrolling to be smooth, and then set a callback for when the scrolling finishes.
You can also have a look at all the JQuery plugins tagged with "scroll".
Here's a quick jQuery plugin to map the built in browser functionality nicely:
$.fn.ensureVisible = function () { $(this).each(function () { $(this)[0].scrollIntoView(); }); };
...
$('.my-elements').ensureVisible();
After trial and error I came up with this function, works with iframe too.
function bringElIntoView(el) {
var elOffset = el.offset();
var $window = $(window);
var windowScrollBottom = $window.scrollTop() + $window.height();
var scrollToPos = -1;
if (elOffset.top < $window.scrollTop()) // element is hidden in the top
scrollToPos = elOffset.top;
else if (elOffset.top + el.height() > windowScrollBottom) // element is hidden in the bottom
scrollToPos = $window.scrollTop() + (elOffset.top + el.height() - windowScrollBottom);
if (scrollToPos !== -1)
$('html, body').animate({ scrollTop: scrollToPos });
}
My UI has a vertical scrolling list of thumbs within a thumbbar
The goal was to make the current thumb right in the center of the thumbbar.
I started from the approved answer, but found that there were a few tweaks to truly center the current thumb. hope this helps someone else.
markup:
<ul id='thumbbar'>
<li id='thumbbar-123'></li>
<li id='thumbbar-124'></li>
<li id='thumbbar-125'></li>
</ul>
jquery:
// scroll the current thumb bar thumb into view
heightbar = $('#thumbbar').height();
heightthumb = $('#thumbbar-' + pageid).height();
offsetbar = $('#thumbbar').scrollTop();
$('#thumbbar').animate({
scrollTop: offsetthumb.top - heightbar / 2 - offsetbar - 20
});
Just a tip. Works on firefox only
Element.scrollIntoView();
Simple 2 steps for scrolling down to end or bottom.
Step1: get the full height of scrollable(conversation) div.
Step2: apply scrollTop on that scrollable(conversation) div using the value
obtained in step1.
var fullHeight = $('#conversation')[0].scrollHeight;
$('#conversation').scrollTop(fullHeight);
Above steps must be applied for every append on the conversation div.
After trying to find a solution that handled every circumstance (options for animating the scroll, padding around the object once it scrolls into view, works even in obscure circumstances such as in an iframe), I finally ended up writing my own solution to this. Since it seems to work when many other solutions failed, I thought I'd share it:
function scrollIntoViewIfNeeded($target, options) {
var options = options ? options : {},
$win = $($target[0].ownerDocument.defaultView), //get the window object of the $target, don't use "window" because the element could possibly be in a different iframe than the one calling the function
$container = options.$container ? options.$container : $win,
padding = options.padding ? options.padding : 20,
elemTop = $target.offset().top,
elemHeight = $target.outerHeight(),
containerTop = $container.scrollTop(),
//Everything past this point is used only to get the container's visible height, which is needed to do this accurately
containerHeight = $container.outerHeight(),
winTop = $win.scrollTop(),
winBot = winTop + $win.height(),
containerVisibleTop = containerTop < winTop ? winTop : containerTop,
containerVisibleBottom = containerTop + containerHeight > winBot ? winBot : containerTop + containerHeight,
containerVisibleHeight = containerVisibleBottom - containerVisibleTop;
if (elemTop < containerTop) {
//scroll up
if (options.instant) {
$container.scrollTop(elemTop - padding);
} else {
$container.animate({scrollTop: elemTop - padding}, options.animationOptions);
}
} else if (elemTop + elemHeight > containerTop + containerVisibleHeight) {
//scroll down
if (options.instant) {
$container.scrollTop(elemTop + elemHeight - containerVisibleHeight + padding);
} else {
$container.animate({scrollTop: elemTop + elemHeight - containerVisibleHeight + padding}, options.animationOptions);
}
}
}
$target is a jQuery object containing the object you wish to scroll into view if needed.
options (optional) can contain the following options passed in an object:
options.$container - a jQuery object pointing to the containing element of $target (in other words, the element in the dom with the scrollbars). Defaults to the window that contains the $target element and is smart enough to select an iframe window. Remember to include the $ in the property name.
options.padding - the padding in pixels to add above or below the object when it is scrolled into view. This way it is not right against the edge of the window. Defaults to 20.
options.instant - if set to true, jQuery animate will not be used and the scroll will instantly pop to the correct location. Defaults to false.
options.animationOptions - any jQuery options you wish to pass to the jQuery animate function (see http://api.jquery.com/animate/). With this, you can change the duration of the animation or have a callback function executed when the scrolling is complete. This only works if options.instant is set to false. If you need to have an instant animation but with a callback, set options.animationOptions.duration = 0 instead of using options.instant = true.

Categories

Resources