so basicaly, i'm doing a chat in vuejs + socket.io
and I want that every time a message is sent, received, or when you come to the site, you arrive directly to the bottom of the page, where the latest message is
I've done a function that fires on the onMounted hook, when I retrieve the data, and on the socket.on('message') :
const scrollToLatest = () => {
let container = document.getElementById("messages")
console.log("scrolltop",container.scrollTop)
console.log("scrollheight",container.scrollHeight)
container.scrollTop = container.scrollHeight;
console.log("scrolltop after", container.scrollTop)
}
Here are what the console.log returns :
(if you can't load the image :)
scrolltop 0
scrollheight 5594
scrolltop after 0
I've tried to use window.scrollTo(0, container.scrollHeight) but it doesn't work either
I hope that someone will be able to help me, I've searched a lot on this issue, but I wasn't able to make it work properly
pass a custom class to your div in your <template> first:
<div :class="`messages`">
</div>
and then get the position of the div as a number:
getPositionOfElement() {
return (document.getElementsByClassName("messages").getBoundingClientRect().top - window.innerHeight);
}
This would be the method for scrolling to the position where your div is located - adapt it to your needs as described in the official documentation:
smoothScroll(vertPos) {
window.scrollTo({
top: vertPos,
left: 0,
behavior: 'smooth'
});
}
combine both methods like this from the place where you need to perform the scrolling - calling this either from another method or directly from the template...
this.smoothScroll(this.getPositionOfElement());
Please let me know if this works for you.
Related
so i'm making a react webApp that has a group-chat feature. Everything is going so well apart from one thing. I wan't the chat to default to the bottom of the screen once entered, then be scrolled upward to view older messages. i've tried the whole useEffect scroll to bottom nonsense and it works alright, except it would be a lot nicer if i could have my chat more like whatsApp. i need a cleaner fix and all i'm finding is half assed solutions, anyone know how whatsApp do it?
Here's what i'm using currently, incase anyone wants a quick fix. i just throw this into useEffect and at the end of my 'getMessages' fetch.
const scrollToBottom = () => {
window.scrollTo({
top: document.documentElement.scrollHeight,
behavior: 'auto'
/* you can also use 'auto' behaviour
in place of 'smooth' */
})
};
I use this
function ScrollToBottom(){
const elementRef = useRef();
useEffect(() => elementRef.current.scrollIntoView());
return <div ref={elementRef} />;
};
Then you just call it at the end of the messages.
<p>some message</p>
<p>another message</p>
<ScrollToBottom />
I have a chat application in react and have used npm package react-scroll-to-bottom until now. But it's not maintained and throwing warnings so i thought it should not be too hard to write this myself.
I'm following various answers on stackoverflow but cant seem to get it right.
It should just scroll to the bottom of the container as the page loads.
Any ideas what i'm doing wrong? Thanks in advance!
EDIT: now i have a div at the end of the tree which has the ref because i thought it would be easier to scroll to that instead of trying to tell a div to scroll inside it.
const chatHistoryRef = React.useRef();
React.useEffect(() => {
window.scrollTo(0, chatHistoryRef.current.offsetTop);
}, [chatHistoryRef]);
Your useEffect does not have any dependencies, so if the ref changes (as in, the div gets rendered) the effect will not be run. Dependencies are set in a second parameter as an array, like so:
useEffect(() => {
// do something with chatHistoryRef.current
}, [chatHistoryRef.current])
Found the solution. As i'm working with polling in my app right now i only get chat messages every two seconds. So my function was in fact scrolling, but there just wasnt anything to scroll away from. :)
This is my solution. Thanks everyone for the help. By adding length to message dependency this is only fired if there are new chat messages
const chatHistoryRef = React.useRef();
React.useEffect(() => {
chatHistoryRef.current.scrollIntoView({
behavior: 'smooth',
block: 'end'
});
}, [chatHistoryRef, messages.length]);
I made a very shabby "Infinite-Scroll" directive, all other solutions were incompatible with a md-grid-list so I went ahead and made my own alternative workaround.
app.directive('scrollTrigger', function($window) {
return {
link : function(scope, element, attrs) {
var e = angular.element(element[0]);
e.bind('mousewheel', function() {
var parent = e.offsetParent()[0];
if (parent.scrollTop + parent.offsetHeight > (70/100*parent.scrollHeight)) {
scope.$apply(attrs.scrollTrigger);
}
});
}
};
});
You simply put scroll-trigger on the html element that will be scrollable. The code checks if it has been scrolled to atleast 70% down, if it has, then it will trigger whatevers called on the scroll-trigger, for example: scroll-trigger="loadMore()"
This works fine but it ONLY works if you use the Mousewheel, but what if users without a mousewheel try using my website? They cant continue loading more items.
The ways that dont work are:
Manually dragging the scrollbar
Swiping/Dragging Up/Down on a Touch Device
Working ways:
Using the Mousewheel
And yes, I do know why, im only binding with e.bind('mousewheel') this is because "scroll" wouldnt actually work for me at all.
So im kinda unsure where to go from here.
i have finished this code off but im stuck on teh last thing i have to do, when a item is added it auto scrolls to the bottom which is great but what i cannot work out how to do and im sure its simple is when i use the scroll bar i want to stop it jumping to the bottom until i stop using the scroll bar.
you will see in the example ive provided if you click on scrollbar and drag up it keeps jumping down when it adds a new item, i just need to pause that until i stop using the scroll bar if that makes sense.
it uses api.scrollToBottom(); to scroll to the bottom once the new item is added which is correct, but unsure on the condition for mouseover etc... to stop that when those are the conditions.
Hope someone can help me on this, many thanks in advance! :)
JSFiddle here: http://jsfiddle.net/GT7sV/2/
JS
var settings = {
showArrows: true,
verticalArrowPositions: 'os',
horizontalArrowPositions: 'os',
animateScroll: true
};
var pane = jQuery('.scroll-pane');
pane.jScrollPane(settings);
var api = pane.data('jsp');
var i = 1;
api.scrollToBottom();
setInterval(
function()
{
api.getContentPane().append("<li>testing this new item</li>").children(':last').hide().fadeIn(1000);
api.reinitialise();
api.scrollToBottom();
},
1000
);
});
Thanks!
I dont know if this is ok for you but you could try it in different direction. You could load new rows when user scrolls to the bottom:
conversationHeadersScrollPane = $('.scroll-pane').bind(
'jsp-scroll-y',
function(event, scrollPositionY, isAtTop, isAtBottom) {
if (isAtBottom) {
//add new rows for example with ajax call
}
});
edit: there is a function getPercentScrolledY()
so you could before calling scrollToBottom check if user is for example bellow 90%, if not let him read while he scrolls on his own to the bottom.
I'm building Windows 8 app in JavaScript. What I'm trying to do is to slide the html element out of the screen and then change its "display" property to "none":
var panelContainer = $('#panelContainer');
panelContainer.animate({ right: '-400px' }, 200, function () {
panelContainer.hide();
});
But this code doesn't work correctly: it just immediately hides the element without animation.
I've also tried:
var panelContainer = $('#panelContainer');
panelContainer.animate({ right: '-400px' }, 200, function () {
panelContainer.hide(200);
});
and it works, but it's a hack: I don't want to change the opacity when animating and I don't need to have additional timeout for hiding.
I've found that jQuery UI library has extended show and hide methods that do that, but I would like not to reference this library just for one call. I'm aware that there is a WinJS.UI.Flyout that performs similar operation, but it's not applicable in my case. Any ideas how this can be done?
The problem was that jQuery does not put hide animation into its animation queue by default. That's why my initial code was hiding the html element first and then animating it. The solution for that is to call hide with the parameter that explicitly specifies that hide call should be queued:
panelContainer.hide({queue: true});