Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create vertically scrolling divs in jquery?

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>
like image 365
Inam Avatar asked Apr 26 '19 18:04

Inam


People also ask

How do I make my div scrollable vertically?

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.

How do I make a div scrollable vertically and horizontally?

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.

How do you make a scrollable list in HTML?

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 .


2 Answers

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 :)

Edit

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>

Edit # 2:

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>
like image 157
Jack Avatar answered Oct 12 '22 23:10

Jack


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>
like image 43
Rachel Gallen Avatar answered Oct 12 '22 23:10

Rachel Gallen