I have a list of Elements that are floating left:
<style>
ul {
overflow: hidden;
width: 100%;
}
ul li {
float: left;
width: 100px;
}
</style>
<ul>
<li id="#one">One</li>
<li id="#two">Two</li>
<li id="#three">Three</li>
...
</ul>
...having a window width of 200px the first Element that is floating on the second line would be #three... having a window width of just 100px that would be #two.
Now on every <li> there is a click-event bound, that should tell me which Element is the first Element on the next Line, seen by itself.
Clicking on #one with a window width of 200px I want to get #three, and with a window width of 100px I want to get #two.
I tried to solve this by getting the position of the Element that is clicked on, query the Position inside the window using .getBoundingClientRect(), get the height of the Element (taking 20px here), and then get the Element by using document.elementFromPoint(y,x)...something like this:
y = clicked_element.getBoundingClientRect().left
x = clicked_element.getBoundingClientRect().top + 21
first_element_on_next_line = document.elementFromPoint(y,x)
so far, so good... but of course this is just working if the first element on the next line is really inside the window! :-\
and this is where I'm stuck...if I have a window width of 200px and a window height of just 20px (just for the example), I just can't select #three with document.elementFromPoint() because its position is outside the window.
I could also use jQuery, it's included anyways, but everything came across was using document.elementFromPoint()
Any Ideas??
You could use following logic using jQuery .position() method: {as suggested by maja}
$(function () {
var $lis = $('ul li').on('click', function(){
var $nextFirstElement = $(this).nextAll('.first').first();
console.log($nextFirstElement);
});
$(window).on('resize', function () {
var initialLeft;
$lis.each(function(i){
if (i === 0) { // this could be put out of the loop, but...
initialLeft = $(this).position().left;
}
$(this).toggleClass('first', i === 0 || $(this).position().left === initialLeft);
});
}).resize();
});
-jsFiddle-
The Solution I'm using now (which in fact is the Solution charlietfl brought up in the Comments) is this:
$(function(){
var list = $("ul"),
lis = list.children(),
first_li = lis.first(),
list_width, first_li_width, items_per_line
var set_vars = function(){
list_width = list.width()
first_li_width = first_li.width()
items_per_row = Math.floor(list_width / first_li_width)
}
set_vars()
lis.click(function(){
index = lis.index(this)
next_first_index = index + (items_per_row - (index % items_per_row))
console.log(next_first_index)
})
$(window).resize(function(){
set_vars()
})
})
Related
I am creating a chat using Ajax requests and I'm trying to get messages div to scroll to the bottom without much luck.
I am wrapping everything in this div:
#scroll {
height:400px;
overflow:scroll;
}
Is there a way to keep it scrolled to the bottom by default using JS?
Is there a way to keep it scrolled to the bottom after an ajax request?
Here's what I use on my site:
var objDiv = document.getElementById("your_div");
objDiv.scrollTop = objDiv.scrollHeight;
This is much easier if you're using jQuery scrollTop:
$("#mydiv").scrollTop($("#mydiv")[0].scrollHeight);
Try the code below:
const scrollToBottom = (id) => {
const element = document.getElementById(id);
element.scrollTop = element.scrollHeight;
}
You can also use Jquery to make the scroll smooth:
const scrollSmoothlyToBottom = (id) => {
const element = $(`#${id}`);
element.animate({
scrollTop: element.prop("scrollHeight")
}, 500);
}
Here is the demo
Here's how it works:
Ref: scrollTop, scrollHeight, clientHeight
using jQuery animate:
$('#DebugContainer').stop().animate({
scrollTop: $('#DebugContainer')[0].scrollHeight
}, 800);
Newer method that works on all current browsers:
this.scrollIntoView(false);
var mydiv = $("#scroll");
mydiv.scrollTop(mydiv.prop("scrollHeight"));
Works from jQuery 1.6
https://api.jquery.com/scrollTop/
http://api.jquery.com/prop/
alternative solution
function scrollToBottom(element) {
element.scroll({ top: element.scrollHeight, behavior: 'smooth' });
}
smooth scroll with Javascript:
document.getElementById('messages').scrollIntoView({ behavior: 'smooth', block: 'end' });
If you don't want to rely on scrollHeight, the following code helps:
$('#scroll').scrollTop(1000000);
Java Script:
document.getElementById('messages').scrollIntoView(false);
Scrolls to the last line of the content present.
My Scenario: I had an list of string, in which I had to append a string given by a user and scroll to the end of the list automatically. I had fixed height of the display of the list, after which it should overflow.
I tried #Jeremy Ruten's answer, it worked, but it was scrolling to the (n-1)th element. If anybody is facing this type of issue, you can use setTimeOut() method workaround. You need to modify the code to below:
setTimeout(() => {
var objDiv = document.getElementById('div_id');
objDiv.scrollTop = objDiv.scrollHeight
}, 0)
Here is the StcakBlitz link I have created which shows the problem and its solution : https://stackblitz.com/edit/angular-ivy-x9esw8
If your project targets modern browsers, you can now use CSS Scroll Snap to control the scrolling behavior, such as keeping any dynamically generated element at the bottom.
.wrapper > div {
background-color: white;
border-radius: 5px;
padding: 5px 10px;
text-align: center;
font-family: system-ui, sans-serif;
}
.wrapper {
display: flex;
padding: 5px;
background-color: #ccc;
border-radius: 5px;
flex-direction: column;
gap: 5px;
margin: 10px;
max-height: 150px;
/* Control snap from here */
overflow-y: auto;
overscroll-behavior-y: contain;
scroll-snap-type: y mandatory;
}
.wrapper > div:last-child {
scroll-snap-align: start;
}
<div class="wrapper">
<div>01</div>
<div>02</div>
<div>03</div>
<div>04</div>
<div>05</div>
<div>06</div>
<div>07</div>
<div>08</div>
<div>09</div>
<div>10</div>
</div>
You can use the HTML DOM scrollIntoView Method like this:
var element = document.getElementById("scroll");
element.scrollIntoView();
Javascript or jquery:
var scroll = document.getElementById('messages');
scroll.scrollTop = scroll.scrollHeight;
scroll.animate({scrollTop: scroll.scrollHeight});
Css:
.messages
{
height: 100%;
overflow: auto;
}
Using jQuery, scrollTop is used to set the vertical position of scollbar for any given element. there is also a nice jquery scrollTo plugin used to scroll with animation and different options (demos)
var myDiv = $("#div_id").get(0);
myDiv.scrollTop = myDiv.scrollHeight;
if you want to use jQuery's animate method to add animation while scrolling down, check the following snippet:
var myDiv = $("#div_id").get(0);
myDiv.animate({
scrollTop: myDiv.scrollHeight
}, 500);
I have encountered the same problem, but with an additional constraint: I had no control over the code that appended new elements to the scroll container. None of the examples I found here allowed me to do just that. Here is the solution I ended up with .
It uses Mutation Observers (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) which makes it usable only on modern browsers (though polyfills exist)
So basically the code does just that :
var scrollContainer = document.getElementById("myId");
// Define the Mutation Observer
var observer = new MutationObserver(function(mutations) {
// Compute sum of the heights of added Nodes
var newNodesHeight = mutations.reduce(function(sum, mutation) {
return sum + [].slice.call(mutation.addedNodes)
.map(function (node) { return node.scrollHeight || 0; })
.reduce(function(sum, height) {return sum + height});
}, 0);
// Scroll to bottom if it was already scrolled to bottom
if (scrollContainer.clientHeight + scrollContainer.scrollTop + newNodesHeight + 10 >= scrollContainer.scrollHeight) {
scrollContainer.scrollTop = scrollContainer.scrollHeight;
}
});
// Observe the DOM Element
observer.observe(scrollContainer, {childList: true});
I made a fiddle to demonstrate the concept :
https://jsfiddle.net/j17r4bnk/
Found this really helpful, thank you.
For the Angular 1.X folks out there:
angular.module('myApp').controller('myController', ['$scope', '$document',
function($scope, $document) {
var overflowScrollElement = $document[0].getElementById('your_overflow_scroll_div');
overflowScrollElement[0].scrollTop = overflowScrollElement[0].scrollHeight;
}
]);
Just because the wrapping in jQuery elements versus HTML DOM elements gets a little confusing with angular.
Also for a chat application, I found making this assignment after your chats were loaded to be useful, you also might need to slap on short timeout as well.
Like you, I'm building a chat app and want the most recent message to scroll into view. This ultimately worked well for me:
//get the div that contains all the messages
let div = document.getElementById('message-container');
//make the last element (a message) to scroll into view, smoothly!
div.lastElementChild.scrollIntoView({ behavior: 'smooth' });
small addendum: scrolls only, if last line is already visible. if scrolled a tiny bit, leaves the content where it is (attention: not tested with different font sizes. this may need some adjustments inside ">= comparison"):
var objDiv = document.getElementById(id);
var doScroll=objDiv.scrollTop>=(objDiv.scrollHeight-objDiv.clientHeight);
// add new content to div
$('#' + id ).append("new line at end<br>"); // this is jquery!
// doScroll is true, if we the bottom line is already visible
if( doScroll) objDiv.scrollTop = objDiv.scrollHeight;
Just as a bonus snippet. I'm using angular and was trying to scroll a message thread to the bottom when a user selected different conversations with users. In order to make sure that the scroll works after the new data had been loaded into the div with the ng-repeat for messages, just wrap the scroll snippet in a timeout.
$timeout(function(){
var messageThread = document.getElementById('message-thread-div-id');
messageThread.scrollTop = messageThread.scrollHeight;
},0)
That will make sure that the scroll event is fired after the data has been inserted into the DOM.
This will let you scroll all the way down regards the document height
$('html, body').animate({scrollTop:$(document).height()}, 1000);
You can also, using jQuery, attach an animation to html,body of the document via:
$("html,body").animate({scrollTop:$("#div-id")[0].offsetTop}, 1000);
which will result in a smooth scroll to the top of the div with id "div-id".
Scroll to the last element inside the div:
myDiv.scrollTop = myDiv.lastChild.offsetTop
You can use the Element.scrollTo() method.
It can be animated using the built-in browser/OS animation, so it's super smooth.
function scrollToBottom() {
const scrollContainer = document.getElementById('container');
scrollContainer.scrollTo({
top: scrollContainer.scrollHeight,
left: 0,
behavior: 'smooth'
});
}
// initialize dummy content
const scrollContainer = document.getElementById('container');
const numCards = 100;
let contentInnerHtml = '';
for (let i=0; i<numCards; i++) {
contentInnerHtml += `<div class="card mb-2"><div class="card-body">Card ${i + 1}</div></div>`;
}
scrollContainer.innerHTML = contentInnerHtml;
.overflow-y-scroll {
overflow-y: scroll;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css" rel="stylesheet"/>
<div class="d-flex flex-column vh-100">
<div id="container" class="overflow-y-scroll flex-grow-1"></div>
<div>
<button class="btn btn-primary" onclick="scrollToBottom()">Scroll to bottom</button>
</div>
</div>
Css only:
.scroll-container {
overflow-anchor: none;
}
Makes it so the scroll bar doesn't stay anchored to the top when a child element is added. For example, when new message is added at the bottom of chat, scroll chat to new message.
Why not use simple CSS to do this?
The trick is to use display: flex; and flex-direction: column-reverse;
Here is a working example. https://codepen.io/jimbol/pen/YVJzBg
A very simple method to this is to set the scroll to to the height of the div.
var myDiv = document.getElementById("myDiv");
window.scrollTo(0, myDiv.innerHeight);
On my Angular 6 application I just did this:
postMessage() {
// post functions here
let history = document.getElementById('history')
let interval
interval = setInterval(function() {
history.scrollTop = history.scrollHeight
clearInterval(interval)
}, 1)
}
The clearInterval(interval) function will stop the timer to allow manual scroll top / bottom.
I know this is an old question, but none of these solutions worked out for me. I ended up using offset().top to get the desired results. Here's what I used to gently scroll the screen down to the last message in my chat application:
$("#html, body").stop().animate({
scrollTop: $("#last-message").offset().top
}, 2000);
I hope this helps someone else.
I use the difference between the Y coordinate of the first item div and the Y coordinate of the selected item div. Here is the JavaScript/JQuery code and the html:
function scrollTo(event){
// In my proof of concept, I had a few <button>s with value
// attributes containing strings with id selector expressions
// like "#item1".
let selectItem = $($(event.target).attr('value'));
let selectedDivTop = selectItem.offset().top;
let scrollingDiv = selectItem.parent();
let firstItem = scrollingDiv.children('div').first();
let firstItemTop = firstItem.offset().top;
let newScrollValue = selectedDivTop - firstItemTop;
scrollingDiv.scrollTop(newScrollValue);
}
<div id="scrolling" style="height: 2rem; overflow-y: scroll">
<div id="item1">One</div>
<div id="item2">Two</div>
<div id="item3">Three</div>
<div id="item4">Four</div>
<div id="item5">Five</div>
</div>
I have these "pages" aka div's inside a scrollable container. On command, I am trying to find out what part of the div in question, is touching the top of .pageContent.
So for example, right when the page loads, no part of #page_1 is touching the top of pageContent, but as I scroll down. #page_1 hits the top of .pageContent and I now want to figure out where that is.
I know I can get the position of .pageContent using $("#pageContent").scrollTop() but these page's could be different sizes and I am not sure how to go about figuring it out.
Could anyone put me in the right direction?
jsfiddle
HTML
<div id="pageContent">
<div id="page_1" class="content"></div>
<div id="page_2" class="content"></div>
<div id="page_3" class="content"></div>
</div>
CSS
#pageContent {
overflow: auto;
width:500px;
height:300px;
padding:10px;
border:1px solid black;
background-color:grey;
}
.content {
height:400px;
width:300px;
margin:0 auto;
background-color:red;
margin-bottom:10px;
}
You can use the jQuery .position() function to compute where each page is in relation to the top of the container. See this Fiddle.
For example, for #page_1,
var page1 = $('#page_1');
$('#pageContent').scroll(function() {
// page1.position().top gives the position of page_1 relative to the
// top of #pageContent
});
ScrollTop can be used, be I wouldn't recommend it.
Attach a scroll event to your main div and listener for all the objects inside:
$('#pageContent').scroll(function(){
var pages = $("#pageContent > .content");
for (var i = 0; i < pages.length; i++)
{
if ($(pages[i]).position().top < 0 && ( $(pages[i]).position().top + $(pages[i]).outerHeight() ) > 0)
{
var outerHeight = $(pages[i]).outerHeight();
var pixels = (outerHeight - (outerHeight + $(pages[i]).position().top));
console.log("These pixels are in view between: " + pixels + " and " + outerHeight );
}
}
})
Every time the div scroll a loop is performed checking the position of all elements. If the elements scroll out of view a the top the if is triggered, calculating the remaining visible pixels of the page currently visible.
This uses jQuery's: position() and outerHeight() and JavaScript's native offsetTop.
http://jsfiddle.net/q5aaLo9L/4/
I tried something like this
$(document).ready(function () {
var divs = $('.content').map(function (i, el) {
return $(el).offset().top - $(el).parent().offset().top;
});
$('#pageContent').scroll(function () {
var index = findIndex($(this).scrollTop(), divs) - 1;
if (index > -1) {
console.log($(this).children().eq(index).attr('id'));
} else {
console.log('outside');
}
});
});
function findIndex(pos, divs) {
return (divs.filter(function (el, et) {
return et <= pos
}).length);
}
It's not super clean code because I had to do it quickly.
DEMO
I hope this helps
I mocked this up, it uses JQuery's each() function to iterate through the pages and return the information of the page that has breached the top of the box.
I wasn't sure from your question exactly what you wanted returned, so I got it to return either the percentage of the page that has cleared the top border, the position (as negative value of pixels) of the top of the "page " in relation to the content container, and also just the ID of that div.
var getCurrentPage = function(){
var page;
var position;
var percentageRead;
$('.content').each(function(){
if($(this).position().top <= 0){
page = $(this);
position = $(this).position().top;
}
});
percentageRead = ((position *-1)/ $(page).height()* 100);
console.log(page.attr('id'));
console.log(position);
console.log(percentageRead + '%');
}
$('#pageContent').on('scroll', getCurrentPage);
You could fire this on any event but I used scroll to build it.
I am trying to add a scroll event which will change the background of a div which also acts as the window background (it has 100% width and height). This is as far as I get. I am not so good at jquery. I have seen tutorials with click event listeners. but applying the same concept , like, returning scroll event as false, gets me nowhere. also I saw a tutorial on SO where the person suggest use of array. but I get pretty confused using arrays (mostly due to syntax).
I know about plugins like waypoints.js and skrollr.js which can be used but I need to change around 50-60 (for the illusion of a video being played when scrolled) ... but it wont be feasible.
here is the code im using:-
*
{
border: 2px solid black;
}
#frame
{
background: url('1.jpg') no-repeat;
height: 1000px;
width: 100%;
}
</style>
<script>
$(function(){
for ( i=0; i = $.scrolltop; i++)
{
$("#frame").attr('src', ''+i+'.jpg');
}
});
</script>
<body>
<div id="frame"></div>
</body>
Inside your for loop, you are setting the src attribute of #frame but it is a div not an img.
So, instead of this:
$("#frame").attr('src', ''+i+'.jpg');
Try this:
$("#frame").css('background-image', 'url(' + i + '.jpg)');
To bind a scroll event to a target element with jQuery:
$('#target').scroll(function() {
//do stuff here
});
To bind a scroll event to the window with jQuery:
$(window).scroll(function () {
//do stuff here
});
Here is the documentation for jQuery .scroll().
UPDATE:
If I understand right, here is a working demo on jsFiddle of what you want to achieve.
CSS:
html, body {
min-height: 1200px; /* for testing the scroll bar */
}
div#frame {
display: block;
position: fixed; /* Set this to fixed to lock that element on the position */
width: 300px;
height: 300px;
z-index: -1; /* Keep the bg frame at the bottom of other elements. */
}
Javascript:
$(document).ready(function() {
switchImage();
});
$(window).scroll(function () {
switchImage();
});
//using images from dummyimages.com for demonstration (300px by 300px)
var images = ["http://dummyimage.com/300x300/000000/fff",
"http://dummyimage.com/300x300/ffcc00/000",
"http://dummyimage.com/300x300/ff0000/000",
"http://dummyimage.com/300x300/ff00cc/000",
"http://dummyimage.com/300x300/ccff00/000"
];
//Gets a valid index from the image array using the scroll-y value as a factor.
function switchImage()
{
var sTop = $(window).scrollTop();
var index = sTop > 0 ? $(document).height() / sTop : 0;
index = Math.round(index) % images.length;
//console.log(index);
$("#frame").css('background-image', 'url(' + images[index] + ')');
}
HTML:
<div id="frame"></div>
Further Suggestions:
I suggest you change the background-image of the body, instead of the div. But, if you have to use a div for this; then you better add a resize event-istener to the window and set/update the height of that div with every resize. The reason is; height:100% does not work as expected in any browser.
I've done this before myself and if I were you I wouldn't use the image as a background, instead use a normal "img" tag prepend it to the top of your page use some css to ensure it stays in the back under all of the other elements. This way you could manipulate the size of the image to fit screen width better. I ran into a lot of issues trying to get the background to size correctly.
Html markup:
<body>
<img src="1.jpg" id="img" />
</body>
Script code:
$(function(){
var topPage = 0, count = 0;
$(window).scroll( function() {
topPage = $(document).scrollTop();
if(topPage > 200) {
// function goes here
$('img').attr('src', ++count +'.jpg');
}
});
});
I'm not totally sure if this is what you're trying to do but basically, when the window is scrolled, you assign the value of the distance to the top of the page, then you can run an if statement to see if you are a certain point. After that just simply change run the function you would like to run.
If you want to supply a range you want the image to change from do something like this, so what will happen is this will allow you to run a function only between the specificied range between 200 and 400 which is the distance from the top of the page.
$(function(){
var topPage = 0, count = 0;
$(window).scroll( function() {
topPage = $(document).scrollTop();
if(topPage > 200 && topPage < 400) {
// function goes here
$('#img').attr('src', ++count +'.jpg');
}
});
});
I want to put a bar on the bottom of my page containing a varying number of pictures, which (if wider than the page) can be scrolled left and right.
The page width is varying, and I want the pane to be 100% in width.
I was trying to do a trick by letting the middle div overflow and animate it's position with jquery.animate().
Like this:
Here is a fiddle without the js: http://jsfiddle.net/SoonDead/DdPtv/7/
The problems are:
without declaring a large width to the items holder it will not overflow horizontally but vertically. Is this a good hack? (see the width: 9000px in the fiddle)
I only want to scroll the middle pane if it makes sense. For this I need to calculate the width of the overflowing items box (which should be the sum of the items' width inside), and the container of it with the overflow: hidden attribute. (this should be the width of the browser window minus the left and right buttons).
Is there a way to calculate the length of something in js without counting all of it's childrens length manually and sum it up?
Is there a way to get the width of the browser window? Is there a way to get a callback when the window is resized? I need to correct the panes position if the window suddenly widens (and the items are in a position that should not be allowed)
Since the window's width can vary I need to calculate on the fly if I can scroll left or right.
Can you help me with the javascript?
UPDATE: I have a followup question for this one: Scroll a div vertically to a desired position using jQuery Please help me solve that one too.
Use white-space:nowrap on the item container and display:inline or display:inline-block to prevent the items from wrapping and to not need to calculate or set an explicit width.
Edit:: Here's a live working demo: http://jsfiddle.net/vhvzq/2/
HTML
<div class="hscroll">
<ol>
<li>...</li>
<li>...</li>
</ol>
<button class="left"><</button>
<button class="right">></button>
</div>
CSS
.hscroll { white-space:nowrap; position:relative }
.hscroll ol { overflow:hidden; margin:0; padding:0 }
.hscroll li { list-style-type:none; display:inline-block; vertical-align:middle }
.hscroll button { position:absolute; height:100%; top:0; width:2em }
.hscroll .left { left:0 }
.hscroll .right { right:0 }
JavaScript (using jQuery)
$('.hscroll').each(function(){
var $this = $(this);
var scroller = $this.find('ol')[0];
var timer,offset=15;
function scrollLeft(){ scroller.scrollLeft -= offset; }
function scrollRight(){ scroller.scrollLeft += offset; }
function clearTimer(){ clearInterval(timer); }
$this.find('.left').click(scrollLeft).mousedown(function(){
timer = setInterval(scrollLeft,20);
}).mouseup(clearTimer);
$this.find('.right').click(scrollRight).mousedown(function(){
timer = setInterval(scrollRight,20);
}).mouseup(clearTimer);
});
Thanks Phrogz for this part -- give the image container the white-space: nowrap; and display: inline-block;.
You can calculate the width without having to calculate the width of the children every time but you will need to calculate the width of the children once.
//global variables
var currentWidth = 0;
var slideDistance = 0;
var totalSize = 0;
var dispWidth = (winWidth / 2); //this should get you the middle of the page -- see below
var spacing = 6; //padding or margins around the image element
$(Document).Ready(function() {
$("#Gallery li").each(function () {
totalSize = totalSize + parseFloat($(this).children().attr("width"));// my images are wrapped in a list so I parse each li and get it's child
});
totalSpacing = (($("#Gallery li").siblings().length - 1) * spacing); //handles the margins between pictures
currentWidth = (parseFloat($("#Gallery li.pictureSelected").children().attr("width")) + spacing);
maxLeftScroll = (dispWidth - (totalSize + totalSpacing)); //determines how far left you can scroll
});
function NextImage() {
currentWidth = currentWidth + (parseFloat($("#Gallery li.pictureSelected").next().children().attr("width")) + spacing); //gets the current width plus the width of the next image plus spacing.
slideDistance = (dispWidth - currentWidth)
$("#Gallery").animate({ left: slideDistance }, 700);
}
There is a way to get the browser window with in javascript (jQuery example).
and there is a way to catch the resize event.
var winWidth = $(window).width()
if (winWidth == null) {
winWidth = 50;
}
$(window).resize(function () {
var winNewWidth = $(window).width();
if (winWidth != winNewWidth) {
window.clearTimeout(timerID);
timerID = window.setInterval(function () { resizeWindow(false); }, 100);
}
winWidth = winNewWidth;
});
On my gallery there's actually quite a bit more but this should get you pointed in the right direction.
You need to change your #items from
#items
{
float: left;
background: yellow;
width: 9000px;
}
to
#items {
background: yellow;
}
Then calculate the width very easily with jQuery
// #items width is calculated as the number of child .item elements multiplied by their outerWidth (width+padding+border)
$("#items").width(
$(".item").length * $(".item").outerWidth()
);
and simply declare click events for the #left and #right elements
$("#left").click(function() {
$("#middle").animate({
scrollLeft: "-=50px"
}, 'fast');
});
$("#right").click(function() {
$("#middle").animate({
scrollLeft: "+=50px"
}, 'fast');
});
jsFiddle link here
EDIT
I overlooked that detail about the varying image widths. Here is the correct way to calculate the total width
var totalWidth = 0;
$(".item").each(function(index, value) {
totalWidth += $(value).outerWidth();
});
$("#items").width(totalWidth);
This question already has answers here:
Animate element to auto height with jQuery
(21 answers)
Closed 7 years ago.
I'm trying to get an element to animate to its "natural" height - i.e. the height it would be if it had height: auto;.
I've come up with this:
var currentHeight = $this.height();
$this.css('height', 'auto');
var height = $this.height();
$this.css('height', currentHeight + 'px');
$this.animate({'height': height});
Is there a better way to do this? It feels like a bit of a hack.
Edit:
Here's a complete script to play with for anyone that wants to test.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<title>jQuery</title>
<style type="text/css">
p { overflow: hidden; background-color: red; border: 1px solid black; }
.closed { height: 1px; }
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
<script type="text/javascript">
$().ready(function()
{
$('div').click(function()
{
$('p').each(function()
{
var $this = $(this);
if ($this.hasClass('closed'))
{
var currentHeight = $this.height();
$this.css('height', 'auto');
var height = $this.height();
$this.css('height', currentHeight + 'px');
$this.animate({'height': height});
}
else
{
$this.animate({'height': 1});
}
$this.toggleClass('closed');
});
});
});
</script>
</head>
<body>
<div>Click Me</div>
<p>Hello - I started open</p>
<p class="closed">Hello - I started closed</p>
</body>
</html>
I permit myself to answer this thread, even if it's been answered a long time ago, cuz it just helped me.
In fact, i don't understand the first answer : why opening a half-closed element to get its height, and then closing it again ?
At the beginning, you hide the element so that just a part of it appears, right ? The best way (i believe) to do this is onready, with javascript. So, when you hide the element onready, just save the orig height in a var, so you don't have to hide(onready)-show-save height-hide to be able to toggle the elements visibility.
Look at what i did, it works perfectly :
$(document).ready(function(){
var origHeight = $("#foo").css('height');
$("#foo").css({"height" : "80px"});
$("#foo .toggle").bind("click", function(event){ toggleFoo(event, lastSearchesMidHeight); });
});
Here, when you call your toggle function, you know what is your original element height without wanking around.
I wrote it fast, hoping it could help someone in the future.
the easiest solution I found was to simply wrap the content element with a div that is limited in height and set to overflow:hidden. This truncates the inner content element to the height of the wrapping div. when the user clicks, hovers, etc. to show the full height of the content element - simply animate the wrapping div to the height of the inner content div.
I could suggest an equally-hackish solution...Clone the element, position it out of view, and get its height...then delete it and animate your original.
That aside, you could also use $.slideUp() and $.slideDown():
$this.hasClass('closed') ? $(this).slideDown() : $(this).slideUp() ;
If you need to keep a 1px line, you can apply that with a parent element:
<div style='border-top:1px solid #333333'>
<div class='toggleMe'>Hello World</div>
</div>
And apply the slidingUp/Down on the .toggleMe div.
I'd also like to chime in on this old thread, if I may, in case my solution helps anyone. My specific situation is this: I have some div's that are set with a max-height value that limits them to three lines tall, and when the user mouseovers them I want them to expand to their natural height; and when the mouse cursor leaves the div, I want them to shrink back down to the clipped, max-three-lines-tall height. I need to use the CSS max-height property, rather than height, because I have some div's that contain only one or two lines of text and I don't want them unnecessarily tall.
I tried many of the solutions in this thread, and the one that worked for me was the 'hackish suggestion' involving cloned elements suggested by Jonathan Sampson. I translated his idea into the following code. Please feel free to suggest improvements.
The functions are delegated to a parent element to handle div's created via an Ajax call. The div.overflow_expandable class has the following declaration: { max-height: 5em; overflow: hidden; }
$('#results').delegate('div.overflow_expandable', 'mouseenter', function() {
var $this = $(this);
// Close any other open divs
$('#results div.overflow_expandable').not($(this)).trigger('mouseleave');
// We need to convert the div's current natural height (which is less than
// or equal to its CSS max-height) to be a defined CSS 'height' property,
// which can then animate; and we unset max-height so that it doesn't
// prevent the div from growing taller.
if (!$this.data('originalHeight')) {
$this.data('originalHeight', $this.height());
$this.data('originalMaxHeight', parseInt($this.css('max-height')));
$this.css({ 'max-height':'none',
height: $this.data('originalHeight') });
}
// Now slide out if the div is at its original height
// (i.e. in 'closed' state) and if its original height was equal to
// its original 'max-height' (so when closed, it had overflow clipped)
if ($this.height() == $this.data('originalHeight') &&
$this.data('originalMaxHeight') == $this.data('originalHeight')) {
// To figure out the new height, clone the original element and set
// its height to auto, then measure the cloned element's new height;
// then animate our div to that height
var $clone = $this.clone().css({ height: 'auto', position: 'absolute',
zIndex: '-9999', left: '-9999px', width: $this.width() })
.appendTo($this);
$this.animate({ height: $clone.height() }, 'slow');
$clone.detach();
}
}).delegate('div.overflow_expandable', 'mouseleave', function() {
var $this = $(this);
// If the div has 'originalHeight' defined (it's been opened before) and
// if it's current height is greater than 'originalHeight' (it's open
// now), slide it back to its original height
if ($this.data('originalHeight') &&
$this.height() > $this.data('originalHeight'))
$this.animate({ height: $this.data('originalHeight') }, 'slow');
});
Found this post and end up using Greg's original 1px suggestion - works great!
Just added a callback to the animate function, to set the height of the element to 'auto' when the animation ends (in my case, the content of that specific element could change and be bigger).
$('div').click(function() {
if($('p').is(':hidden')) {
$('p').slideUp();
} else {
$('p').slideDown(function() { $('p').css('height','1px'); });
}
}
That should set the height of the p tags to be 1px once they've finished sliding.
This worked for me.
<div class="product-category">
<div class="category-name">
Cars
</div>
<div class="category-products" style="display: none; overflow: hidden;">
<div class="product">Red Car</div>
<div class="product">Green Car</div>
<div class="product">Yellow Car</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('.product-category .category-name').click(function() {
if ($(this).parent().hasClass('active')) {
$(this).parent().removeClass('active');
var height = $(this).parent().find('.category-products').height();
$(this).parent().find('.category-products').animate({ height : '0px' }, 600, function() {
$(this).parent().find('.category-products').height(height).hide();
});
} else {
$(this).parent().addClass('active');
var height = $(this).parent().find('.category-products').height();
$(this).parent().find('.category-products').height(0).show().animate({ height : height + 'px' }, 600);
}
});
});
</script>
My solution is to store in the data attribute of the close button the original size of container (could have been stored also in the container itself, if you don't use the same button to also show again the container):
$(document).ready(function(){
$('.infoBox .closeBtn').toggle(hideBox, showBox);
});
function hideBox()
{
var parent = $(this).parent();
$(this).text('Show').data('originalHeight', parent.css('height'));
parent.animate({'height': 20});
return false;
}
function showBox()
{
var parent = $(this).parent();
$(this).text('Hide');
parent.animate({
'height': $(this).data('originalHeight')
});
return false;
}
I wanted to point to this answer, which suggest setting the height to "show" with the animate() function. I had to edit my "slideUp" style animate to use height:"hide" to work with it.