Automatically Scroll Message List - javascript

I am making a message app with express and socket.io. The messages are appended to a list as shown below:
<div id="messageContainer">
<ul id="messages"></ul>
</div>
socket.on('message', (content) => {
$('#messages').append(`<li>${content.bold} ${content.std}</li>`);
$('#messages').scrollTop = $('#messages').scrollHeight;
});
The messages are appended correctly appended however the scrolling does not occur. I get no error message and I am unsure why.
The full source code can be found on GitHub here.
Edit
I have added a console.log('scroll') to the bottom of the js call however nothing is shown.
socket.on('message', (content) => {
$('#messages').append(`<li>${content.bold} ${content.std}</li>`);
$('#messages').scrollTop = $('#messages').height();
console.log('scroll')
});
This makes me think that scrolling isn't being called even thought the messages are being appended

jQuery objects have no property scrollHeight, change that to a function call of height.
$('#messages').scrollTop = $('#messages').height();

Try this
socket.on('message', (content) => {
$('#messages').append(`<li>${content.bold} ${content.std}</li>`);
$("#messages").animate({ scrollTop: $("#messages")[0].scrollHeight}, 1000);
console.log('scroll')
});

I have tried to create simulation of chat box.
During the overflow, height of element does not change, that's why #expressjs123's answer did not work, you'll need to use height of the content inside the chat box, which is scollHeight of the element.
The Element.scrollHeight read-only property is a measurement of the
height of an element's content, including content not visible on the
screen due to overflow.
Source: Element.scrollHeight
scrollHeight is the propery of the element therefor i have used vanilla javascript code to get scollheight.
var element = document.getElementById("messages");
element.scrollHeight;
Note: I have given you the basic overview that how can achieve the vertical scroll 100%, the code may vary with your code/project, you may need to put some effort, but this is the basic idea for what you are looking for.
setInterval(function() {
var element = document.getElementById("messages");
$("#messages").append("<p class='m" +parseInt((Math.random()) * 5)+ "'>Message #" + new Date().getTime() + "</p>");
$("#messages").scrollTop(element.scrollHeight);
}, 500);
#messages {
height: 300px;
width: 200px;
overflow: auto;
border: 1px solid #ddd;
background: #f1f1f1;
padding: 5px;
font-family:monospace;
}
p {
border: 1px solid #ddd;
background: #fff;
padding:4px;
font-size: 11px;
width: auto;
float: left;
clear: both;
margin: 2px 0;
border-radius:3px;
}
p.m4,p.m3,p.m0{
background: #f1f8ff;
float: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="messages"></div>
In your git I'm seeing that you're using scroll method on #messageContainer not #messages, make sure to select correct element, else the code won't work.

scrolltop is a function, so you have to call it with (height) (jquery docs: https://api.jquery.com/scrolltop/#scrollTop2). (almost) every jquery thing is called with a function

Related

Jquery loading div before page starts actually loads

I have a page that has to do quite a bit of work (5-6 seconds) of loading data from all over the place on the initial connection. It is slow because of the api endpoints I am calling, I have no control over it.
Is there a way to get a loading div to show before it starts doing all of its data collection?
The below doesnt do anything. I believe its because the page already starts gathering data before it gets to the jquery. I could be wrong. myjs.js is the file name and it is the first thing loaded on my page.
$('body').on('load', function(){
$body.addClass("loading");
});
and does the same thing
$(document).ready(function() {
$body.addClass("loading");
});
In layman's terms:
User goes to https://somewebsite.com
Jquery loading div shows
other functions run to gather data
jquery loading div is removed.
This is in the laravel framework if that affects anything.
There is actually a pretty simple way to do this. I recently experienced something similar.
I did something like this:
window.addEventListener('load', (event) => {
setTimeout(() => {
$('.jumping-dots-loader').slideUp(650);
}, 1000);
});
.jumping-dots-loader {
width: 100vw !important;
height: 100vh !important;
background-color: white;
z-index: 99999999999999999999999;
display: block;
border: none;
border-radius: 0;
margin: 0;
position: fixed;
padding: 20% 35%;
text-align: center;
}
.jumping-dots-loader span {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 100%;
background-color: rgba(147, 194, 61, 1);
margin: 35px 0.85rem;
}
/* Add in animation */
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="jumping-dots-loader">
<span></span>
<span></span>
<span></span>
</div>
<h1>
Howsit going?
</h1>
If you go through Mozilla's docs about the document.readystatechange event, you will see how the browser handles the loading order and can use this to your advantage. In my example, I add a container div which will cover the user's viewport. Then style some dots (add your own animation to them) which will be displayed while the document is loading. When the load state is reached, the placeholder div will be hidden and the loaded page is displayed.

scrollTop (JS and jQuery version) won't scroll all the way to the bottom

I'm creating a chat app which when messages load (From Firebase), the div containing the messages scrolls to the bottom to display the most recent appended message div. scrollTop does somewhat work but it won't scroll all the way to the bottom, no matter what values I use for scrollTop. I've tried both the JS and the jQuery versions of scrollTop, but neither can get it to scroll to the bottom. Here's some of my code:
HTML
<div id="msgContainer">
<div id="msgFeed">
//Messages load here from a database
</div>
</div>
CSS
#msgContainer {
height: 165px;
overflow-x: hidden;
overflow-y: visible;
}
#msgFeed {
display: block;
background-color: white;
position: relative;
margin: 0;
overflow: hidden;
}
JS
function scrollToBottom (id) {
var div = document.getElementById(id);
div.scrollTop = div.scrollHeight - div.clientHeight;
}
or...
$('#scroll').scrollTop(1000000);
Doesn't seem to matter which version or what values I use, it just refuses to scroll that last approximately 5% of the way to the bottom. Any ideas what I might be doing wrong??
I dealt with a similar issue. I was receiving a value from a web socket to put into a chat box. Whenever I used scrollTop/Height, it always scrolled to the NEXT to last message (off just a bit). Even if I put in the max or a very high value, it would not scroll all the way.
This occurs b/c the dimensions of the container (with the added item) are not yet what we expect. A simple timeout will solve this problem:
setTimeout(() => {
el.scrollTop = el.scrollHeight;
}, 500);
If you're using Vue.js (probably something analagous in other reactive frameworks), you can also do the following ('this' is the Vue instance):
this.$nextTick(
() => (this.$refs.chat.scrollTop = this.$refs.chat.scrollHeight)
);
'nextTick' seems optimal, but not everyone will be using Vue. Hope this all helps someone solve this simple yet not so evident problem.
EDIT: nextTick doesn't always seem to work. setTimeout should always work.
I don't know what element you were referring to with #scroll since I don't see it in your html, but try this and let me know if it still falls ~5% short.
$(document).ready(function(){
function scrollToBottom (id) {
var div = document.getElementById(id);
/*TRY*/
div.scrollTop = div.scrollHeight - div.clientHeight;
/*OR*/
$('#'+id).scrollTop(div.scrollHeight - div.clientHeight);
}
scrollToBottom('msgContainer');
});
#msgContainer {
height: 165px;
overflow-x: hidden;
overflow-y: visible;
border: 3px solid red
}
#msgFeed {
display: block;
background-color: white;
position: relative;
margin: 0;
overflow: hidden;
border: 1px solid blue
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="msgContainer">
<div id="msgFeed">
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
//Messages load here from a database
</div>
</div>

Creating a textarea that resizes with volume of text (vanilla JS, no JQuery)

Server-side dev here, looking to dabble in a bit of vanilla JS to learn the ropes.
This question is about creating a form where textareas are initially hidden behind button clicks, and which also resize as the text entered into them grows larger. It's a challenging problem for someone at my level, and I'm trying to code it myself for learning purposes.
Here's what I've done so far.
Imagine a forum where users submit textual content. Each submission has a "reply" button under it. Pressing that opens a simple textarea that one can type their response into. I wrote simple JS to accomplish this:
var replyBtns = document.querySelectorAll('[id=rep]');
for(var i = 0; i < replyBtns.length; i++) {
replyBtns[i].addEventListener('click', function(e) {
e.preventDefault();
var textarea = document.getElementById("reply-message");
if (textarea) { textarea.parentNode.removeChild(textarea); }
var replyBox = document.createElement('textarea');
replyBox.setAttribute('id', 'reply-message');
replyBox.setAttribute('placeholder', 'Reply');
this.parentNode.insertAdjacentElement('beforeend', replyBox);
}, false);
}
As you can see, this JS creates a text area and adds it in the HTML. The CSS to go with it is:
textarea#reply-message {
display: block;
border: none;
color:#306654;
font-size:120%;
padding: 5px 10px;
line-height: 20px;
width:550px;
height: 20px;
border-radius: 6px;
overflow: hidden;
resize: none;
}
This works well.
My next endeavour was to make this textarea resize as the text starts overflowing. For that, I'm taking this route:
Grab the content loaded into the textarea
Create an invisible clone div
Give the clone the same width and typographical properties as the textarea
Place the content into the clone
Get the height of the clone
Apply the height of the clone to the height of the textarea
This strategy uses the property that any div element naturally stretches to fit the height of its content (assuming no floats or absolutely positioned elements). I owe this technique to this blog post.
Here's the JS for implementing the aforementioned technique:
let txt = document.getElementById('reply-message'),
hiddenDiv = document.createElement('div'),
content = null;
hiddenDiv.classList.add('hiddendiv');
document.body.appendChild(hiddenDiv);
txt.addEventListener('keyup', function () {
content = this.value;
hiddenDiv.innerHTML = content + '\n\n';
this.style.height = hiddenDiv.getBoundingClientRect().height + 'px';
}, false);
Where hiddendiv is:
.hiddendiv {
position: absolute;
left: -9999px;
visibility: hidden;
white-space: pre-wrap;
width: 550px;
height: 20px;
font-size: 120%;
padding: 5px 10px;
word-wrap: break-word;
overflow-wrap: break-word;
}
But thing is, I need this JS snippet to run only once the textarea actually exists.
Currently, I have it running on page load, where it's unable to have any effect since textareas don't yet exist. How do I ensure it only runs once they've been created? Being a newbie in JS, this is totally eluding me. Please advise.
This isn't exactly answering your question, but restructuring your code a bit to achieve the same result.
You should add the event listener to your textarea when you create it, before you add it to the page.
Insert code like this in your first glob of javascript:
replyBox.setAttribute('placeholder', 'Reply');
replyBox.addEventListener('keyup', function () {
/* do event handling here */
}, false);
A cleaner way to handle all of this would be to move all of the code pertaining to setting up your textareas into a separate function.
function setUpReplyBox(replyBox) {
replyBox.setAttribute('id', 'reply-message');
replyBox.setAttribute('placeholder', 'Reply');
hiddendiv.createElement('div');
content = null;
hiddendiv.classList.add('hiddendiv');
document.body.appendChild(hiddenDiv);
replyBox.addEventListener('keyup', function () {
content = this.value;
hiddenDiv.innerHTML = content + '\n\n';
this.style.height = hiddenDiv.getBoundingClientRect().height + 'px';
}, false);
}
Call this right after you create the item, at
var replyBox = document.createElement('textarea');
setUpReplyBox(replyBox);
this.parentNode.insertAdjacentElement('beforeend', replyBox);
Listen for the domContentLoaded Event
document.addEventListener("DOMContentLoaded", function(event) {
console.log("DOM fully loaded and parsed");
});
Reference Link
https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded
Maybe using a div with contenteditable instead of a textarea would make your life a bit easier.
#textarea {
-moz-appearance: textfield-multiline;
-webkit-appearance: textarea;
border: 1px solid gray;
padding: 2px;
width: 400px;
}
<div id="textarea" contenteditable>dummy textarea</div>
There is plugin for that autosize
Check the src at github, you will get idea on how to do it.
If your code is working in page load and not in text update, you need to call your resize function in every keyup, resize and input events.
window.addEventListener('resize', yourFunc);
textarea.addEventListener('paste', yourFunc);
textarea.addEventListener('keyup', yourFunc);
There is simpler method here
function auto_grow(element) {
element.style.height = "5px";
element.style.height = (element.scrollHeight)+"px";
}
textarea {
resize: none;
overflow: hidden;
min-height: 50px;
}
<textarea onkeyup="auto_grow(this)"></textarea>

jquery blind and exposing child div behind?

I'm trying to achieve the effect of a sliding div using the jquery animate option. I'm able to "slide" the parent div up but I'm having issues with showing the div behind the slider.
I've created this jsfiddle to show my issue.
Try uncommenting the photoOptions div. I'm trying to hide this div so it's only revealed when the parent div is slid up.
<div class="photoWrapper">
<!-- <div class="photoOptions"> This is your data. </div>-->
<div class="photoHolder">
Image Here
</div>
<div class="photoMeta">More data here</div>
<div class="photoCredits">
Trigger
</div>
</div>
Code
jQuery.fn.blindToggle = function(speed, easing, callback) {
var h = this.height() + parseInt(this.css('paddingTop')) + parseInt(this.css('paddingBottom'));
return this.animate({
marginTop: parseInt(this.css('marginTop')) < 0 ? 0 : -h
}, speed, easing, callback);
};
$(document).ready(function(){
$(".trigger").click(function(){
$('.photoHolder').blindToggle('slow');
});
});
Current CSS:
.photoWrapper {
width:200px;
border: solid 1px #ddd;
}
.photoHolder {
border: solid 1px #eee;
width:200px;
height:266px;
}
.photoOptions {
padding-top: 50px;
height: 266px;
width: 200px;
background: #eee;
position:absolute;
}
Any thoughts on how I can achieve this?
The browser renders elements based on there place in the DOM, if an element preceeds another element in the dom, it is rendered under it.
To change this default behaviour, you should use the CSS rule z-index, by defining a lower z-index on your .photoOptions div, it will be rendered below it.
as seen in this fiddle
Also be aware that z-index values may be handled differently for elements that are positioned absolute, due to the fact that they are not rendered in the normal flow.
Using the callback on .blindToggle() can achieve that effect but you're going to have to edit your CSS so that .photoCredits is visible and just start off with .photoOptions hidden:
$(document).ready(function () {
$(".trigger").click(function () {
$('.photoHolder').blindToggle('slow', function () {
$(".photoOptions").show();
});
});
});
.photoOptions {
padding-top: 50px;
height: 266px;
width: 200px;
background: #eee;
position:absolute;
display:hidden;
}

Javascript - make popup div open below clicked link

Sorry for the unclear title, I can't formulate a better concise explanation.
I have a list, and within each list item is a link which opens an othersiwse hidden <div> using the following jQuery:
$('a.showreranks').click(function () {
$('body').append('<div class="overlay"></div>');
$('#rerank_details').slideToggle(300);
return false;
});
rerank_details being the id of the div and showreranks being the class of all the links.
This is the CSS of the div:
#rerank_details {
display: none;
background: white;
position: absolute;
border: 1px solid black;
border-radius: 6px;
width: 305px;
padding: 15px;
overflow: hidden;
top: 50%;
left: 50%;
height: 200x;
text-shadow: none;
z-index: 50;
}
So, you see, the opened div is centered when it's opened. It is then populated with info relating to the list item that was clicked but you don't need to worry about that. What I need help with is the following - I don't want the div to be centered on the screen. Instead I'd like it to be positioned right below the link that was clicked. Note that there could be a lot of links on the page, one below the other and the vertical distances could be irregular. How do I accomplish this?
I think that this is what you are trying to do:
http://jsfiddle.net/SO_AMK/r7ZDm/
The answer has already been accepted, but perhaps this is a cleaner version. The animations are all fixed.
if it doesn't have to be within the normal flow of the DOM just use absolute positioning and the event object.
function(event){
var box = //get a handle to box
box.style.position = 'aboslute';
box.style.left = event.page.x;
box.style.top = event.page.y;
}

Categories

Resources