-3

When I add some elements to my scrollable list with jQuery's prepend() function and I'm initially at the top of the list, the list automatically scrolls to the top of the first added element and doesn't keep the position. How can I prevent this?

This is my code example showing my problem:

jQuery(document).ready(function($) {
  $("button").click(function() {
    [1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(function() {
      $("div").prepend("<span class='new'></span>");
    });
  });
});
div {
  border: 1px solid;
  width: 200px;
  height: 350px;
  overflow-y: scroll;
  overflow-x: hidden;
}

span {
  height: 40px;
  width: 200px;
  display: block;
  background: gray;
}

span.new {
  background: red !important;
}

span:nth-child(odd) {
  background: lightgray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
<button>Prepend elements</button>
Mr. Jo
  • 4,946
  • 6
  • 41
  • 100
  • 3
    Your snippet does not demonstrate the behaviour you describe. If you scroll down before clicking the button the scroll position remains the same – Rory McCrossan Mar 16 '20 at 15:51
  • Yes, but without scrolling first. This case is my problem. – Mr. Jo Mar 16 '20 at 15:54
  • 1
    Your 'problem' is "How to scroll to bottom" – Wimanicesir Mar 16 '20 at 15:55
  • Does this answer your question? [Scroll to bottom of div?](https://stackoverflow.com/questions/270612/scroll-to-bottom-of-div) – Wimanicesir Mar 16 '20 at 15:55
  • 1
    @Wimanicesir I don't think so. Probably more like "How to keep the position" – Mr. Jo Mar 16 '20 at 15:56
  • @Wimanicesir No, it doesn't answer my question. – Mr. Jo Mar 16 '20 at 15:56
  • 2
    Well technically the scroll position did not change when you are at the top. Because after you add the items, it is still at the top. You would need to get the position of the initial first item, then scroll to it but then you would need to decide where the "first" item is before adding and what item qualifies as first when user scrolls that list – Huangism Mar 16 '20 at 15:59
  • @Huangism I know but this will be always 0 and when I add new items and scroll to 0, I'm going the wrong way I think – Mr. Jo Mar 16 '20 at 16:00
  • 1
    @Mr.Jo maybe the behaviour you are looking for is - before adding items, get position of the scroll, then, after adding items, get height of added items, and then scroll to the sum of original scroll position and the height of the added item so you are at where you were – Huangism Mar 16 '20 at 16:03
  • @Huangism Sounds complicated. Do you have an idea how to start with this in the code? Because I can't work with the list at all I think (position offset().top is 0) – Mr. Jo Mar 16 '20 at 16:10
  • @Mr.Jo not that complicated, breaking it down, there is get height of added elements(jquery height()) and for position of scrolled element https://stackoverflow.com/questions/2481350/how-to-get-scrollbar-position-with-javascript – Huangism Mar 16 '20 at 16:11
  • @Huangism But what if the elements have different heights? – Mr. Jo Mar 16 '20 at 16:13
  • @Mr.Jo doesn't matter the height is calculated on each item, we are not using the 40. In chrome, when you add items, the scroll position remains the same as before, the styles of the odd and even rows kind fo throws you off but if you are at second item from the top before adding, you are still there after adding, but because your row colour changed due to the addition, it looks like it moved. You can clearly see this if you add text to the spans – Huangism Mar 16 '20 at 16:22
  • @Mr.Jo see the answer, it is an easier way to do the same thing – Huangism Mar 16 '20 at 16:30

1 Answers1

2

If you want to maintain the scroll position you can track the scrollTop position and scrollHeight of the div then use scrollTop – like:

jQuery(document).ready(function($) {
  $("button").click(function() {
    let $div = $("div");
    let top = $div.scrollTop();
    let scrollHeight = $div.prop("scrollHeight");
    [1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(function() {
      $div.prepend("<span class='new'></span>")
    });
    $div.scrollTop(top + $div.prop("scrollHeight") - scrollHeight);
  });
});
div {
  border: 1px solid;
  width: 200px;
  height: 350px;
  overflow-y: scroll;
  overflow-x: hidden;
}

span {
  height: 40px;
  width: 200px;
  display: block;
  background: gray;
}

span.new {
  background: red !important;
}

span:nth-child(odd) {
  background: lightgray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
<button>Prepend elements</button>
Jakob E
  • 4,476
  • 1
  • 18
  • 21