Reputation: 627
I'm writing a messenger application in Meteor, and I want to set it up such that when either user types a message it scrolls down to the bottom of the div for both of them. I am storing the message in a list called messages
in a conversation document in the collection Conversations
. I am using cursor.observeChanges
, and it seems that the callback fires before the data is rendered on the client side so it doesn't scroll all the way to the bottom.
Here is the html:
<template name="chat">
{{> chatBox}}
</template>
<template name="chatBox">
<div class="chat-box">
<div id="chat-messages">
{{#each chatMessages}}
<div class="individual-message">
{{message}}
</div>
{{/each}}
</div>
<form id="chat-input">
<input class="add-message" autocomplete="off" placeholder="Write something..." name="text" type="text">
</form>
</div>
</template>
Here's the relevant css:
#chat-messages {
overflow-y: scroll;
width: 250px;
height: 450px;
padding: 15px;
position: absolute;
}
And here's the js:
Tracker.autorun(function(){
...
Conversations.find(conversationId).observeChanges({
changed: function(id, fields){
$("#chat-messages").scrollTop($("#chat-messages").prop("scrollHeight"));
}
});
});
Upvotes: 4
Views: 2120
Reputation: 3535
Different Approach via tracking of Template helpers:
I took advantage of the Template helper, as it already tracks reactively all changes (and new) messages. Hence, there you can place your scroll-down command.
I assume you have something like this in your JS file:
chatBox.js
Template.chatBox.helpers({
chatMessages: function() {
return Conversations.find({conversationId: conversationId},
{sort: {d: -1}, limit: 20}).fetch().reverse();
},
});
(Being chatMessages.d
the date of posting and conversationId
your reactive variable for the specific chat-room, sorted by date from the end, and displayed by reverse order, so that last chat would appear at the end of your chat-messages div
, limited to the last 20 messages)
Just add your scroll-down command there, hence you would have:
Template.chatBox.helpers({
chatMessages: function() {
//scroll down not animated
$('#chat-messages').scrollTop($('#chat-messages').prop('scrollHeight'));
return Conversations.find({conversationId: conversationId},
{sort: {d: -1}, limit: 20}).fetch().reverse();
}
});
Or make it animated for smooth scrolling:
Template.chatBox.helpers({
chatMessages: function() {
//scroll down with animation
$('#chat-messages').animate({scrollTop: $('#chat-messages').prop('scrollHeight')}, 500);
return Conversations.find({conversationId: conversationId},
{sort: {d: -1}, limit: 20}).fetch().reverse();
}
});
This would trigger your "scroll-down" at any change in your Conversations-Collection.
Hope this helps.
Upvotes: 0
Reputation: 16045
Whenever I run into an issue where Blaze hasn't had a chance to render something in time for a Javascript function to be evoked on it, I use Tracker.afterFlush
. This waits until the render cycle is done before running some code, e.g.:
// Inside a Meteor event callback
Tracker.afterFlush(function () {
var $someItem = $('....');
$(window).scrollTop($someItem.offset().top);
});
http://docs.meteor.com/#/full/tracker_afterflush
Upvotes: 9