I'm creating vertically auto scrolling divs using jquery with animation... I have finished creating it but the only problem is when I scroll down the window to the middle of list div where divs are scrolling, it pushes the window up to the scrolling list div. I don't know what the problem is. However when I try to give the list div width in pixels, it is not pushing up...
Try to scroll down to the middle of the scrolling list div. You will understand what the problem is. Thanks...
setInterval(function(){
$('#list').stop().animate({scrollTop:40}, 400, 'swing', function(){
$(this).scrollTop(0).find('div:last').after($('div:first', this));
});
}, 1000);
* {
box-sizing: border-box;
}
body {
height: 1000px;
}
#list {
overflow: hidden;
width: 100%;
height: 250px;
background: red;
padding: 10px;
margin-top: 100px;
}
#list div {
display: block;
height: 30px;
padding: 10px 10px;
margin-bottom: 10px;
background: yellow;
}
.item:last-child {
margin-bottom: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="list">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
<div>Item 5</div>
<div>Item 6</div>
<div>Item 7</div>
<div>Item 8</div>
<div>Item 9</div>
<div>Item 10</div>
</div>
For vertical scrollable bar use the x and y axis. Set the overflow-x:hidden; and overflow-y:auto; that will automatically hide the horizontal scroll bar and present only vertical scrollbar. Here the scroll div will be vertically scrollable.
We can use a combination of overflow-x and overflow-y to place the scrollbar vertically, horizontally, or both. For the vertical scrollbar, overflow-x:hidden and overflow-y:auto will be set in the CSS file, and vice versa for the horizontal scrollbar.
It's kind of a tricky problem, but one easy way to solve it is to follow a few simple steps: (1) List all of your items in individual div s. (2) Put all items into a containing div , and limit the height of that div to a fixed size. (3) Then, style the containing div to set overflow: scroll .
What an odd bug. From what I can tell, because of the removal/insertion
of the first element to the end of the list, this results in Chrome re-rendering the list causing the change in your scrollY
offset.
Personally, I can think of two ways to address the issue.
1) Remove the div (#list) from the DOM flow.
Note: This requires wrapping your #list
element in a containing
element.
Because your removing/appending elements to your #list
element, this is causing a reflow to occur (???). Initially, I thought using position: absolute
would address this, as you're no longer affecting the DOM flow. However, even position: absolute
still causes the scroll position to jump.
I then looked at position: fixed
, which DOES work (as it's basically sitting on top of everything) - This obviously isn't going to work as you want the list to scroll with the page! So, how can we get around this??
transform
to the rescue!Let's take advantage of a transform
quirk! Usually, when an element is position: fixed
, it's relative to the viewport. However, if an ancestor element has a transformation
, the element that is fixed
positioned will be relative to the transformed element. Let's give this a shot!
As I mentioned, you'll need to apply a wrapping/containing element to transform. In the code snippet below, I've wrapped your #list
element with a new div#transformed
.
CSS changes are straight forward and look like:
#transformed { transform: translateZ(0); }
#list { position: fixed; }
And that is it! Check out the snippet below to see it in action.
setInterval(function(){
$('#list').stop().animate({scrollTop:40}, 400, 'swing', function(){
$(this).find('div:last').after($('div:first', this));
});
}, 1000);
* {
box-sizing: border-box;
}
body {
height: 1000px;
}
#transformed {
transform: translateZ(0);
}
#list {
position: fixed;
overflow: hidden;
width: 100%;
height: 250px;
background: red;
padding: 10px;
margin-top: 100px;
}
#list div {
display: block;
height: 30px;
padding: 10px 10px;
margin-bottom: 10px;
background: yellow;
}
.item:last-child {
margin-bottom: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="transformed">
<div id="list">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
<div>Item 5</div>
<div>Item 6</div>
<div>Item 7</div>
<div>Item 8</div>
<div>Item 9</div>
<div>Item 10</div>
</div>
</div>
2) Sliding a layer
As for the second way to address the issue, this is more a discussion/opinion on how to approach this (so, unfortunately can't provide code).
Rather than manipulate the scrollTop
, I would look into a "sliding" layer approach (either using transformY
or top
).
You'll find JS-based carousels take a similar approach (funny enough, carousels tend to either use a scroll-offset approach - as similar to this question, or a "sliding" layer approach! They just tend to do it horizontally)
As for the layer you manipulate, again I would suggest removing it from the DOM flow (so position: absolute
). In theory, manipulating this layer shouldn't affect the scroll offset...
Anyway, hope this was helpful and the position: fixed
approach works for you :)
Regarding your comment, you can achieve a "downward" scroll by reversing your setInterval
logic. However, instead of scrolling and then moving the item that just scrolled out to the bottom of the list, you will need to move the element you want to scroll in to the top of the list, offset the scrollTop, and then scroll in.
Here's a snippet that demonstrates:
setInterval(function(){
$('#list')
.find('div:first')
.before($('div:last', '#list'))
.end()
.scrollTop(40)
.stop()
.animate({scrollTop:0}, 400, 'swing');
}, 1000);
* {
box-sizing: border-box;
}
body {
height: 1000px;
}
#transformed {
transform: translateZ(0);
}
#list {
position: fixed;
overflow: hidden;
width: 100%;
height: 250px;
background: red;
padding: 10px;
margin-top: 100px;
}
#list div {
display: block;
height: 30px;
padding: 10px 10px;
margin-bottom: 10px;
background: yellow;
}
.item:last-child {
margin-bottom: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="transformed">
<div id="list">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
<div>Item 5</div>
<div>Item 6</div>
<div>Item 7</div>
<div>Item 8</div>
<div>Item 9</div>
<div>Item 10</div>
</div>
</div>
Regarding up/down buttons, you can easily achieve that by combining the two scripts we've written so far! Here's are two snippets that animates up/down when you click (the first will scroll each time you click, and the second snippet will change the animation direction):
function animateList(direction) {
if (direction === 'down') {
$('#list')
.find('div:first')
.before($('div:last', '#list'))
.end()
.scrollTop(40)
.stop()
.animate({scrollTop:0}, 400, 'swing');
} else {
$('#list')
.animate({scrollTop:40}, 400, 'swing', function(){
$(this)
.find('div:last')
.after($('div:first', this));
});
}
}
$('button').on('click', function () {
var direction = $(this).attr('id');
animateList(direction);
});
* {
box-sizing: border-box;
}
body {
height: 1000px;
}
#transformed {
transform: translateZ(0);
}
#list {
position: fixed;
overflow: hidden;
width: 100%;
height: 250px;
background: red;
padding: 10px;
margin-top: 100px;
}
#list div {
display: block;
height: 30px;
padding: 10px 10px;
margin-bottom: 10px;
background: yellow;
}
.item:last-child {
margin-bottom: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="transformed">
<div id="list">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
<div>Item 5</div>
<div>Item 6</div>
<div>Item 7</div>
<div>Item 8</div>
<div>Item 9</div>
<div>Item 10</div>
</div>
</div>
<button id="up">Scroll Up</button>
<button id="down">Scroll Down</button>
And the snippet that changes the direction:
var interval;
function animateList(direction) {
// Reset interval
if (interval) {
clearInterval(interval);
}
if (direction === 'down') {
interval = setInterval(function () {
$('#list')
.find('div:first')
.before($('div:last', '#list'))
.end()
.scrollTop(40)
.stop()
.animate({scrollTop:0}, 400, 'swing');
}, 1000);
} else {
interval = setInterval(function () {
$('#list')
.animate({scrollTop:40}, 400, 'swing', function(){
$(this)
.find('div:last')
.after($('div:first', this));
});
}, 1000);
}
}
$('button').on('click', function () {
var direction = $(this).attr('id');
animateList(direction);
});
// Initial animation
animateList('up');
* {
box-sizing: border-box;
}
body {
height: 1000px;
}
#transformed {
transform: translateZ(0);
}
#list {
position: fixed;
overflow: hidden;
width: 100%;
height: 250px;
background: red;
padding: 10px;
margin-top: 100px;
}
#list div {
display: block;
height: 30px;
padding: 10px 10px;
margin-bottom: 10px;
background: yellow;
}
.item:last-child {
margin-bottom: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="transformed">
<div id="list">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
<div>Item 5</div>
<div>Item 6</div>
<div>Item 7</div>
<div>Item 8</div>
<div>Item 9</div>
<div>Item 10</div>
</div>
</div>
<button id="up">Scroll Up</button>
<button id="down">Scroll Down</button>
I have converted your inner divs to a unordered list. I added an overflow-y:scroll
initially, which also worked, but changed it to overflow-y:hidden
on seeing your comment. I found but both work. If you scroll down the page and then scroll back up, it doesn't 'start again' (unless it's had the time to iterate though all 10 list items). You may need to adjust the css/box height to get the red border at the bottom, but I'll leave this to you
Hope this helps
Overflow-hidden:
setInterval(function() {
$('#ulList').stop().animate({
scrollTop: 40
}, 400, 'swing', function() {
$(this).scrollTop(0).find('li:last').after($('li:first', this));
});
}, 1000);
* {
box-sizing: border-box;
}
body {
height: 1000px;
}
#list {
overflow-y:hidden;
width: 100%;
height: 250px;
background: red;
padding: 10px;
margin-top: 100px;
}
#list ul {
margin: 10px 10px;
padding: 10px 0px;
}
#list ul li {
display: block;
height: 30px;
margin-bottom: 10px;
background: yellow;
}
.item:last-child {
margin-bottom: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="list">
<ul id="ulList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
<li>Item 10</li>
</ul>
</div>
Overflow:scroll
setInterval(function() {
$('#ulList').stop().animate({
scrollTop: 40
}, 400, 'swing', function() {
$(this).scrollTop(0).find('li:last').after($('li:first', this));
});
}, 1000);
* {
box-sizing: border-box;
}
body {
height: 1000px;
}
#list {
overflow-y:scroll;
width: 100%;
height: 250px;
background: red;
padding: 10px;
margin-top: 100px;
}
#list ul {
margin: 10px 10px;
padding: 10px 0px;
}
#list ul li {
display: block;
height: 30px;
margin-bottom: 10px;
background: yellow;
}
.item:last-child {
margin-bottom: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="list">
<ul id="ulList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
<li>Item 10</li>
</ul>
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With