element.scrollTop always returns 0 - javascript

I'm trying to get the scrollTop position of an element, but it always returns 0. What am I doing wrong, and how can I fix it?
JSFiddle
var inner = document.getElementById('inner');
window.addEventListener('scroll', function() {
console.log(inner.scrollTop);
})
#outer {
background-color: tan;
height: 1000px;
}
#first {
background-color: bisque;
height: 200px;
}
#inner {
background-color: aquamarine;
height: 100px;
}
<div id="outer">
<div id="first"></div>
<div id="inner">scrollTop always returns 0</div>
</div>

As #FelixKling pointed out in the comments:
inner.offsetTop is what to use for this. scrollTop returns the amount you scrolled in that particular container. So because inner doesn't have a scrollbar, it never scrolls, and therefore scrollTop is 0.
But offsetTop, on the other hand, returns the distance of the current element relative to the top of the offsetParent node.
So the formula to get the amount scrolled of an element based on window, would be:
inner.offsetTop - document.body.scrollTop;

For some reason, removing 'height: 100%' from my html and body tags fixed this issue.
I hope this helps someone else!

I am not sure why, but the only thing I could get to work was using window.pageYOffset:
window.addEventListener('scroll', function() {
let scrollPosition = window.pageYOffset;
if (scrollPosition >= 30) {
headerContainer.classList.add('lg:bg-green-lightest');
} else {
headerContainer.classList.remove('lg:bg-green-lightest');
}
});

I found a simplest way to do this by adding eventListener to window object this way:
window.addEventListener("click", () => {
let value = window.scrollTop;
console.log(value);
});
This isn't the cleanest way, but It's the only way I got it work as it should.

Removing overflow: hidden from my html tag fixed this for me.

Related

On scroll, logo color changes, but scrolling back up it stays the same

Im creating a fixed header where on load, the logo is flat white. On scroll, it changes to the full color logo.
However, when scrolling back to the top, it stays the same colored logo instead of going back to white.
Here's the code (and a pen)
$(function() {
$(window).scroll(function() {
var navlogo = $('.nav-logo-before');
var scroll = $(window).scrollTop();
if (scroll >= 1) {
navlogo.removeClass('.nav-logo-before').addClass('nav-logo-after');
} else {
navlogo.removeClass('.nav-logo-after').addClass('nav-logo-before');
}
});
});
http://codepen.io/bradpaulp/pen/gmXOjG
There's a couple of things here:
1) You start with a .nav-logo-before class but when the logo becomes black you remove that class and then try to get the same element using a class selector that doesn't exist anymore
2) removeClass('.nav-logo-before') is different than removeClass('nev-logo-before), notice the "." in the first selector.
3) You get the element using the $('.selector')in every scroll event, this can be a performance issue, it's better to cache them on page load and then use the element stored in memory
4) It's not a good practice to listen to scroll events as this can be too performance demanding, it's usually better to use the requestAnimationFrame and then check if the scroll position has changed. Using the scroll event it could happen that you scroll up really fast and the scroll event doesn't happen at 0, so your logo won't change. With requestAnimationFrame this can't happen
$(function() {
var navlogo = $('.nav-logo');
var $window = $(window);
var oldScroll = 0;
function loop() {
var scroll = $window.scrollTop();
if (oldScroll != scroll) {
oldScroll = scroll;
if (scroll >= 1) {
navlogo.removeClass('nav-logo-before').addClass('nav-logo-after');
} else {
navlogo.removeClass('nav-logo-after').addClass('nav-logo-before');
}
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
});
body {
background-color: rgba(0,0,0,.2);
}
.space {
padding: 300px;
}
.nav-logo-before {
content: url(https://image.ibb.co/kYANyv/logo_test_before.png)
}
.nav-logo-after {
content: url(https://image.ibb.co/jYzFJv/logo_test_after.png)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<img class="nav-logo nav-logo-before">
</div>
<div class="space">
</div>
Dont need to add the dot . in front of the class name in removeClass and addClass:
Use this:
navlogo.removeClass('nav-logo-before')
Secondly, you are removing the class that you are using to get the element in the first place.
I have an updated codepen, see if this suits your needs: http://codepen.io/anon/pen/ZeaYRO
You are removing the class nav-logo-before, so the second time the function runs, it can't find any element with nav-logo-before.
Just give a second class to your navlogo element and use that on line 3.
Like this:
var navlogo = $('.second-class');
working example:
http://codepen.io/anon/pen/ryYajx
You are getting the navlogo variable using
var navlogo = $('.nav-logo-before');
but then you change the class to be 'nav-logo-after', so next time the function gets called you won't be able to select the logo using jquery as it won't have the '.nav-logo-before'class anymore.
You could add an id to the logo and use that to select it, for example.
Apart from that, removeClass('.nav-logo-before') should be removeClass('nav-logo-before') without the dot before the class name.
The problem is that you removes nav-logo-before and then you want to select element with such class but it doesn't exist.
I've rafactored you code to avert it.
Another problem is that you uses dot in removeClass('.before') while it should be removeClass('before') - without dot
$(function() {
var navlogo = $('.nav-logo');
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 1) {
navlogo.removeClass('before').addClass('after');
} else {
navlogo.removeClass('after').addClass('before');
}
});
});
body {
background-color: rgba(0,0,0,.2);
}
.space {
padding: 300px;
}
.before {
content: url(https://image.ibb.co/kYANyv/logo_test_before.png)
}
.after {
content: url(https://image.ibb.co/jYzFJv/logo_test_after.png)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<img class="nav-logo before">
</div>
<div class="space">
</div>

Why is the scrollWidth of a valid text area undefined?

I have found many answers suggesting I use scrollWidth and scrollHeight to get the content width and height, respectively, of a text area. However, my code returns the scrollWidth as undefined. Any idea why this could be the case?
function changewidth(o) {
if ($(o).val().length>8)
{
var current = $(o).css("width");
console.log($(".name").scrollWidth+'px');
$(o).css("width",$(".name").scrollWidth+'px');
}
}
And this javascript function is being called by this text area:
<textarea rows="1" onkeyup="changewidth(this)" class="name" type="textarea" placeholder="name"></textarea>
The console prints undefinedpx. Based on research, I have tried to alter the CSS and currently have the following:
.name {
width: 100px;
margin: 0;
padding: 0;
border: none;
font-size: 20px;
color: #00B45E;
resize: none;
overflow: hidden;
}
You have to try this, No need to wrap currently passed object into $() unless you need it.
function changewidth(o) {
if ($(o).val().length > 8) {
var current = $(o).css("width");
console.log(o.scrollWidth + 'px'); // o.scrollWidth
$(o).css("width", o.scrollWidth + 'px'); // use object as o direclty
}
}
With pure Javascript
function changewidth(o) {
if (o.value.length>8)
{
var current = o.style.width;
console.log(o.scrollWidth+'px');
o.style.width = o.scrollWidth+'px';
}
}
Fiddle
With pure javascript
Demo
scrollWidth
scrollWidth only applies to plain Javascript, it seems you are using it on jQuery.
instead of $(".name").scrollWidth try changing it to document.getElementsByClassName("name").scrollWidth

Each element height if higher then function

Good day, I'm trying to figure out how to do small thing.
I have several elements on page that needs to checked on height and if height is higher, for example > 100, then I need to call function for them.
So far I came up to this:
var filterHeight = $('div.class').height();
if (filterHeight > 100) {
$('div.class').css('height', '100px');
$('div.class').css('overflow-y', 'scroll');
}
Problem with that as you know is that it will add those params to all element because I strictly point to that.
I guess it I will need to do something with .each(), but...
Thank you for your suggestions and help.
Because I can't answer my own question I'm updating here.
I used your solution and change it a little bit, instead adding inline css I added class that fetch css.
So what I did is:
1) When page loads
$('div.class').each(function() {
if ($(this).height() > 200) {
$(this).addClass('scroller');
}
});
.scroller {
height: 200px;
overflow-y: scroll !important;
}
So it checked all blocks with height more than 200px and adds class.
2) After I'm calling for Ajax/JOSN and it gives me new data for those blocks I was in need to check for those elements heigh changes. So on .ajax complete I removed class and check again.
complete: function () {
$('.box.refine .middle ul').removeClass('scroller')
$('.box.refine .middle ul').each(function() {
if ($(this).height() > 200) {
$(this).addClass('scroller');
}
});
}
Thats all.
You need to look at jQuery .each()
Something like:
$('.check-these').each( function(){
if ($(this).height() > 100) {
$(this).css('height', '100px');
$(this).css('overflow-y', 'scroll');
}
});
This isn't tested but should put you on the right track.
$('div.class').each(function(){
if($(this).height() > 100)
{
$(this).css({height: "100px", overflow-y: "scroll"});
}
});
Loop through the elements and check each individually:
$('div.class').each(function() {
if ($(this).height() > 100) {
$(this).css('height', '100px').css('overflow-y', 'scroll');
}
});
But why not just put that in the css to begin with?
div.class {
height: 100px;
overflow-y: scroll;
}

Need to find height of hidden div on page (set to display:none)

I need to measure the offsetHeight of a div that is inside of a hidden element.
<div id="parent" style="display: none;">
<div id="child">Lorem Ipsum dolor sit amet.</div>
</div>
The parent div must be set to "display:none". I have no control over that. I realize that the offsetHeight of the child div is going to be 0. I need to find a workaround.
Something I've toyed with is when the page loads, I copy the childnodes of parent, inject in a div on the page that is set to "visiblity:hidden". Then I measure the height of those elements, and remove the nodes when done.
Any other thoughts?
Update:
What I wound up having to do was this:
Using YUI 2, on page load, I found all elements of that given classname that were either set to display:none, or whose height and width was 0 (that's one way of measuring whether an element exists, or a parent is set to display:none). I then set that element to display:block. I then checked it's parent for the same thing and showed the parents until it finds a visible parent. Once highest display:none ancestor is set to display:block, I can measure my element.
Once all elements are measured I reset all of the elements back to display:none.
You need to make element's parent visible for that one very short moment while you're getting element's dimensions. In a generic solution, all ancestors are usually traversed and are made visible. Then their display values are set back to original ones.
There are performance concerns of course.
We considered this approach in Prototype.js implementation but ended up with getWidth and getHeight making only actual element visible, without traversing ancestors.
The problem with alternative solutions - such as taking element out of "hidden" parent - is that certain styles might no longer apply to an element once it's out of its "regular" hierarchy. If you have a structure like this:
<div class="foo" style="display:none;">
<div class="bar">...</div>
</div>
and these rules:
.bar { width: 10em; }
.foo .bar { width: 15em; }
then taking element out of its parent will actually result in wrong dimensions.
If you use style.display = "none", the element will have 0 width and height,
but using the style.visibility = "hidden" instead, the element will have the width and height calculated by the browser (as normally).
A workaround is to set the height to 0
.hidden {
height: 0;
overflow: hidden;
}
Then to get the elements scrollHeight.
document.querySelector('.hidden').scrollHeight
The scrollHeight will correctly give you the height though the element does not appear. I don't think it affects element flow either.
Example: https://jsfiddle.net/de3vk8p4/7/
Reference: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements#How_big_is_the_content.3F
You could clone the element, absolutely position it at -10000,-10000, measure the clone and destroy it.
Made a pure js solution with no Jquery and with no cloning (which I guess is faster)
var getHeight = function(el) {
var el_style = window.getComputedStyle(el),
el_display = el_style.display,
el_position = el_style.position,
el_visibility = el_style.visibility,
el_max_height = el_style.maxHeight.replace('px', '').replace('%', ''),
wanted_height = 0;
// if its not hidden we just return normal height
if(el_display !== 'none' && el_max_height !== '0') {
return el.offsetHeight;
}
// the element is hidden so:
// making the el block so we can meassure its height but still be hidden
el.style.position = 'absolute';
el.style.visibility = 'hidden';
el.style.display = 'block';
wanted_height = el.offsetHeight;
// reverting to the original values
el.style.display = el_display;
el.style.position = el_position;
el.style.visibility = el_visibility;
return wanted_height;
}
here is the demo
https://jsfiddle.net/xuumzf9k/1/
Please let me know if you can find any improvements to this (as I use this in my main projects)
So here's working jQuery solution based on lod3n's answer and with help of 999's comment:
var getHiddenElementHeight = function(element){
var tempId = 'tmp-'+Math.floor(Math.random()*99999);//generating unique id just in case
$(element).clone()
.css('position','absolute')
.css('height','auto').css('width','1000px')
//inject right into parent element so all the css applies (yes, i know, except the :first-child and other pseudo stuff..
.appendTo($(element).parent())
.css('left','-10000em')
.addClass(tempId).show()
h = $('.'+tempId).height()
$('.'+tempId).remove()
return h;
}
Enjoy!
A helper function ---
function getElementHeight(el) {
var clone = el.cloneNode(true);
var width = el.getBoundingClientRect().width;
clone.style.cssText = 'position: fixed; top: 0; left: 0; overflow: auto; visibility: hidden; pointer-events: none; height: unset; max-height: unset; width: ' + width + 'px';
document.body.append(clone);
var height = clone.getBoundingClientRect().height + 'px';
clone.remove();
return height;
}
Creates a clone, appends it to the DOM (hidden), takes the height, then removes it.
Position of fixed and the top/left are in case your app allows scrolling at the body-level - it attempts to prevent a scrollbar rave party - can remove if you handle scrolling in children elements.
Overflow, height, and max-height settings to attempt to 'reset' height settings and let it be it's natural height on the clone.
Visibility for the obvious and pointer-events as a 'just in case' the rendering of the element takes a while and don't want to interrupt user-input.
An example having an 'accordion-like' animated open/close allowing for dynamic heights.
function getElementHeight(el) {
var clone = el.cloneNode(true);
clone.style.cssText = 'position: fixed; top: 0; left: 0; overflow: auto; visibility: hidden; pointer-events: none; height: unset; max-height: unset';
document.body.append(clone);
var height = clone.getBoundingClientRect().height + 'px';
clone.remove();
return height;
}
var expanded = false;
var timeout;
function toggle() {
var el = document.getElementById('example');
expanded = !expanded;
if (expanded) {
el.style.maxHeight = getElementHeight(el);
// Remove max-height setting to allow dynamic height after it's shown
clearTimeout(timeout);
var openTimeout = timeout = setTimeout(function() {
el.style.maxHeight = 'unset';
clearTimeout(openTimeout);
}, 1000); // Match transition
} else {
// Set max height to current height for something to animate from
el.style.maxHeight = getElementHeight(el);
// Let DOM element update max-height, then set to 0 for animated close
clearTimeout(timeout);
var closeTimeout = timeout = setTimeout(function() {
el.style.maxHeight = 0;
clearTimeout(closeTimeout);
}, 1);
}
}
#example {
overflow: hidden;
max-height: 0;
transition: max-height 1s;
}
<button onclick="toggle()">Toggle</button>
<div id="example">
<textarea>Resize me</textarea>
</div>
In the JS please use 'scrollHeight'
Example Code
Assume that this div is hidden in DOM
<div class="test-div">
//Some contents
<div>
Javascript to find this div height
const testHeight = document.querySelector('.test-div');
testHeight.scrollHeight
Use z-index to hide element under non-transparent element, show it, and get height.
Until the element is rendered, it has no height. Even if you clone the parent object and display it somewhere that can't be seen by the user, there's not guarantee that the clone will have the same height as the final size of the hidden object.
There are many things that can affect the height that wouldn't necessarily be rendered in the clone - anything in the DOM and its interaction with the CSS rules could cause a change in rendering any other element of the DOM. Short of cloning the entire document (and even that's not fool-proof) you have no way of determining the height of the hidden object.
If you must know the height before it's displayed to the user, you'll have to "hack" it by displaying it for as short of a time as possible then hiding it again. Most likely, the user will see this hiccup and not be pleased by the result.
So, you cannot even change the display:none; to height:0; overflow:hidden; ? Maybe you could override that in your own stylesheet like so:
div#parent { display: block !important; height:0; overflow:hidden; }
And then as you are using YUI (assuming YUI 2) you could use this:
var region = YAHOO.util.Dom.getRegion('child');
To get the dimensions and offset of the child.
Try to use:
#parent{ display:block !important; visibility:hidden; position:absolute}
What I wound up having to do was this:
Using YUI 2, on page load, I found all elements of that given classname that were either set to display:none, or whose height and width was 0 (that's one way of measuring whether an element exists, or a parent is set to display:none). I then set that element to display:block. I then checked it's parent for the same thing and showed the parents until it finds a visible parent. Once highest display:none ancestor is set to display:block, I can measure my element.
Once all elements are measured I reset all of the elements back to display:none.
Did you try this ?
setTimeout('alert($(".Var").height());',200);

Scroll to bottom of div?

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>

Categories