Upon scrolling to the top of the div, ajax will be loaded - javascript

I am currently working on a django chat application which involves using ajax to load the list of chat messages and appending them to one of the divs in my template via jquery (e.g. chatmsg_div).
Since the chat app will be used by many users, I am required to do pagination for the chat messages which I have been successful in doing so. Now, only the last 20 chat messages in the chatroom will be loaded for the users to see (with the latest message being at the bottom of the div).
However, there is another requirement that I need to do which is to load the chat history (e.g. previous 20 messages that is in another page) upon scrolling to the top of the chatmsg_div.
My questions will be:
I did some research on google, but could not find any jquery function that allows me to trigger an ajax call upon reaching the top of a specific div. I am pretty new to jquery, so please pardon me even if the answer I am looking for is obvious.
After loading the previous 20 chat messages, I want the div to remain at the position I last scrolled to instead of going to the top of the div
So far, I have tried:
$('#chatmsg_div').scroll(function () { if ($(this).scrollTop() > 100) { debugger; }
});
Thanks in advance for anyone who can answer my questions.

For checking if you need to scroll when a new messages arrived and it's not visible because the user have scrolled to top, you can do:
var shouldScroll = (content.scrollTop + content.offsetHeight >= content.scrollHeight);
Where the content is your DOM container of messages
For detecting if the page is scrolled to the top most:
if (content.scrollTop == 0) {
// load message history here
}
Now, for implementing auto scroll for new messages and the current scroll is at the bottom of the page, you could do:
if (shouldScroll) {
content.scrollTop = content.scrollHeight;
}
This should answer all your questions provided that you have enough knowledge to do the other task. No need to use jQuery

Related

Can I implement infinite-scroll on a live website by running a script over the page?

Now a days in most of the news websites when we scroll to the bottom we see some bunch of recommended articles or news, Can i instead of that just open up the next recommended article?
Take an example of this article from The News Minute here when you scroll past the first article the next article directly leads up in the same tab the only thing changing is the query param.
I want to replicate the same feature but on a live website by running a script in the console.
I've tried doing infinite scrolling but, I'm not able to implement it fully in a console.
window.addEventListener('scroll', () => {
if (
window.scrollY + window.innerHeight >= document.body.offsetHeight - 1000
) {
//GET ARTICLE
}
});
I am not able to write the GET_ARTICLE( ) function to work according to my use case.Which is to grab the next recommended article and display it in the same tab just by scrolling past a point and changing the query params of the variable.

Scrolling down in a particular div instead in the window itself in vanilla JS

In facebook.com there's your conversations page.
In desktop view, you can scroll down this div to see all your messages.
The div's class is .uiScrollableAreaBody.
I desire to go into the conversations page, execute in console the relevant code, and start to scroll down until the possible end, until I'll be scrolled down maximally (after all scroll down "stops" or "stations" have been passed by my script).
That's instead me need to site there and scroll down, "stop after stop" or "station after station" (sorry or the terminology, I know nothing better than these terms to describe it).
Here's one of the codes I tried to achieve it automatically:
document.querySelector(".uiScrollableAreaBody").scrollHeight;
// Outputted: 1398.
And:
document.querySelector(".uiScrollableAreaBody").scrollTop=100;
// Outputted: 100.
Do you know how to do so in vanilla JS?
BTW, I just give facebook.com's conversations page as an example - I call no one to scrap that site or any other site.
I think by your "stop after stop" or "station after station" terminology you are meaning their infinite scroll loading system.
And yes you had the right idea, setting scrollTop will trigger a scroll event which seems to do a load more request if needed.
You simply were targeting the wrong element. The element that needs it's scrollTop property set is uiScrollableAreaWrap
document.querySelector(".uiScrollableAreaWrap").scrollTop=100;
As for making it exhaust the infinite scroll, you can setup a MutationObserver to watch for additions into the content area and when it does, do the next scroll.
let contentScroller = document.querySelector(".uiScrollableAreaWrap");
let observer = new MutationObserver(mutations => {
//In the case of FB message window you scroll up to load more
//change this to the scrollHeight value if needing to scroll down.
if(contentScroller.scrollTop > 0) {
contentScroller.scrollTop = 0;
}
});
observer.observe(contentScroller, {childList:true, subtree:true});
Patrick was right, I used the wrong selector, but sadly, the mutation observer didn't work (to repeat the action).
What helped me was this (note the 10000, instead 1000):
setInterval(()=>{
document.querySelector(".uiScrollableAreaWrap").scrollTop=10000;
}, 1000);

Move entire document into iframe

I'm adding a chat feature to a couple of our websites. The chat will connect users with people at our help desk to help them use the websites. Our help desk folks want the chat window to appear like a tab on the side of the page and slide out, rather than popping up in a new window. However, I want to allow the user to navigate around the site without losing the chat.
To do this, I've been trying to move the entire page into an iframe once the chat starts (with the chat outside the iframe), so the user can navigate around the site within the iframe without losing the chat.
I used this answer to get started, and that works great visually. However, some of the javascript in the background breaks.
One of the sites is ASP.NET web forms. The other is MVC. I've been working with the web forms one first. Stuff like calling __doPostBack breaks once the page is moved into the iframe since the javascript context is left behind.
Once the user clicks on a link (a real link, not a __doPostBack) and the iframe refreshes, then everything works perfectly.
How I see it, I have a few options:
Copy all javascript variables from window.top into the iframe somehow. Hopefully without having to know all the variable names. I tried this.contentWindow.__doPostBack = window.top.__doPostBack, which works, but other variables are missing so it ultimately fails:
Somehow switch the iframe's context to look at the top window context, if that's even possible? Probably not.
Another thought was to not move the page into an iframe right away, but to wait until the page changes and then load the new page into a new iframe. But I'm not sure how to hook into that event and highjack it.
Something else?
These are sites for use by our employees only, so I only have to support IE11 and Chrome.
Update:
Thanks to LGSon for putting me on the track of using the target attribute (so I can use approach #3). Below is what I ended up doing. When I pop out the chat, I call loadNextPageInIframe(). I'm using jQuery here since we already use it on our site, but everything could be done without. I set the target on all links that don't already have a target pointing to another frame or _blank. I left _parent out, but I don't think we use it anyway.
I have a reference to my chat window div in a global variable called 'chatwindow'.
There still could be some cases where this doesn't work, such as if there is some javascript that sets window.location directly. If we have anything in our sites that does this, I'll have to add a way to handle it.
function loadNextPageInIframe() {
var frame = $("<iframe id=\"mainframe\" name=\"mainframe\" />").css({
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
border: "none",
display: "none"
}).appendTo("body");
$("form, a:not([target]), a[target=''], a[target='_top'], a[target='_self']").attr("target", "mainframe");
var firstload = true;
frame.load(function () {
//Runs every time a new page in the iframe loads
if (firstload) {
$(frame).show();
//Remove all elements from the top window except the iframe and chat window
$("body").children().not(frame).not(window.top.chatwindow).remove();
firstload = false;
}
//Make the browser URL and title reflect the iframe every time it loads a new page
if (this.contentWindow && this.contentWindow.location.href && window.top.location.hostname === this.contentWindow.location.hostname && window.top.location.href !== this.contentWindow.location.href) {
var title = this.contentDocument.title;
document.title = title;
if (window.top.history.replaceState) window.top.history.replaceState(null, title, this.contentWindow.location.href);
}
});
}
May I suggest you do the following
get all links
attach an event click handler to intercept when someone click a link
on click event, check if chat is in progress, and if, feed the iframe with the new link
var links = querySelectorAll("a");
for (var i = 0; i < links.length; i++) {
links[i].addEventListener('click', function(e) {
if (isChatInProgress()) {
e.preventDefault(); //stop the default action
document.getElementById("your_iframe_id").src = e.target.href;
// anything else here, like toggle tabs etc
}
});
}
Update
To handle forms I see 4 ways at the moment
1) Add an onsubmit handler to your forms
function formIsSubmitted(frm) {
if (isChatInProgress()) {
frm.target = "the iframe";
}
return true;
}
<form id="form1" runat="server" onsubmit="return formIsSubmitted(this)">
2) Add a click handler to your buttons
function btnClick(btn) {
if (isChatInProgress()) {
btn.form.target = "the iframe";
}
return true;
}
<asp:Button runat="server" ID="ButtonID" Text="ButtonText"
OnClick="Button_Click" OnClientClick="return btnClick(this);" />
3) When a chat start, you iterate through each form and alter its target attribute
function startChat() {
var forms = querySelectorAll("form");
for (var i = 0; i < forms.length; i++) {
forms[i].target = "the iframe";
});
}
4) Override the original postback event (Src: intercept-postback-event)
// get reference to original postback method before we override it
var __doPostBackOriginal = __doPostBack;
// override
__doPostBack = function (eventTarget, eventArgument) {
if (isChatInProgress()) {
theForm.target = "the iframe";
}
// call original postback
__doPostBackOriginal.call(this, eventTarget, eventArgument);
}
Update 2
The absolute best way to deal with this is of course to use AJAX to load both page and chat content, but that likely means a quite bigger work load if your sites aren't already use it.
If you don't want to do that, you can still use AJAX for chat and if a user were to navigate to a new page, the chat app recreate the ongoing chat window with all its content again.
I suggest instead of loading content to and from iframes - build the chat as an iframe and use a jQuery modal popup on the page for chat.
You can fix the jquery modal to a fixed location and page scrolling is enabled by default. You need to modify css accordingly to make the popup remains on the same location.
If you go down your current path - you will need to worry a lot about how content is moved to the iframe and it might be difficult to re-use the chat on different pages depending on the content. For example, imagine you playing a video on the page and the user clicks chat - if you load the content to the iframe - the user will lose the status on how far he has viewed, etc.
as per my opinion, adding the whole website as an 'I-Frame' is not a good design practice, and not a good solution for the problem. My suggestion would be:
Ensure that the 'Chat' application is loaded in all the pages, across your website
Whenever the 'Chat' is started, either establish the 'web-socket' connection or somehow, maintain the State on the Server
Have the configuration of the 'Chat' as 'Minimized', 'Open' etc and store them in your cookie or session storage
On every page load, call the 'Chat' application too. Read the Chat related configuration from sessionstorage or cookie and maintain it's state as 'Minimized' or 'Open' etc, including the X and Y position, if you want to make it as 'Floated'
Every time, either fetch the entire conversation from the server via Ajax or try to store and fetch from 'Local Storage' and do Ajax only for any Updates from the other party
Use CSS based 'Float' related properties to make it float and sit at some side.
This will ensure that your chat is available for the user and yet he can navigate all through the site.

Linking into a page with a hash fragment (so user lands at specific content on page) issues

The Feature I Want:
I want to give a user a link like mysite.com/foo#bar so when they hit this link they land on the page foo and are scrolled half way down the page to the content with the id bar. It should be noted that this link will always be clicked from off site or typed into the address bar manually, the user will not already be on the page with all assets loaded.
Also fyi I am using angular and the page in question each bit of content is in an element directive, and in each directive template there are images
The Issue:
Easy enough right? Well I'm running into some problems, most of the time it works, but maybe 40% of the time it doesn't and the user lands above the content, I believe this is because the browser scrolls to the correct point on the page, but then slightly afterward images are loaded in above it pushing the rest of the page down, leaving the user in a random unintuitive spot. (For some reason the failure rate seems to be worse on iPhones...)
What I've Tried So Far:
In a run function I look for a hash fragment on any route and scroll to it if it exists.
if($location.hash()) {
$anchorScroll();
}
I've tried:
Wrapping it in a timeout
This works sometimes but is obviously not consistent, sure I can set it to 500ms and on a great wifi connection it's fine, but not on a mobile with poor signal
listening for $viewContentLoaded
Too fast, ui-router seems to fire this event way before the page is rendered
Emitting an event in each post-link function for all directives on the page.
link: function ($scope) {
$scope.$emit('loadingFinished');
Then picking that up again in the run function
$scope.$on('loadingFinished', function () {
$timeout(doAnchorScroll, 500);
}
This raised the success rate but still wasn't foolproof. And I witnessed it leaving the user stranded in weird spots mainly on iPhones.
Can anyone suggest a way of detecting a moment where it is safe to scroll, or perhaps some other way of ensuring landing in the correct spot?
Two things - first is a user-scroll should prevent it - you don't want to be scrolling the view if they've already scrolled. Second is listening for the images to finish loading.
Listening for the images is quite easy:
$("img").one("load", $anchorScroll);
Then in $anchorScroll I'd suggest checking if all images have loaded, and returning immediately if not (bonus points if you only check images above the anchor - but only doing a quick reply):
var scrolled = false;
function $anchorScroll() {
var allLoaded = true;
$("img").each(function() {
if (!this.loaded) {
return allLoaded = false;
}
});
if (scrolled || !allLoaded) {
return;
}
...
The scrolling is possibly slightly harder - you can check for scroll events, but they are slightly different between platforms - and might even get fired for the manual scroll - if you find that happens then simply have a global that says "I'm scrolling here" and ignore it, otherwise:
$(document).one("scroll", function() { // Use ".on" if this gets fired for code...
scrolled = true;
});
Note that you probably don't need to care about failed image loads, or ones that have already loaded, since your initial call to $anchorScroll() will catch them.

Chat app Scrollable div or Iframe

What is the advised method to make a chat window scrollable, using an iframe or a scrollable div? What are the pros&cons of the two techniques? Which would you opt for and why?
Thanks
You can create a script that will embed a chat into a third-party website creating both <div> or <iframe>
The main interesting differences
iframe
Code: All user events (clicks, key events, hovers etc) are handlable exclusively from your external chat app page. Without a complicated API the user will not be able to easily modify or target desired events to suit their needs (Why should they after all). The sensitive backend code and logic can stay hidden on your side.
Styling: Your chat app will look exactly like you defined it. With an extended API the user will only be able to select some predefined styles. (I personally hate that.) So more coding for you.
Uses Mostly used by free chat apps where they force the app to be just the way they want it to be, preventing custom styles and possibly the removal of the App logo, link to the from site, or some random ads. Also used if you want to provide the data storage on your side, or provide silent application updates.
Scroll and heights are unaware of the surrounding items which ends mostly having an API where the user chooses some predefined chat heights.
DIV
Code: All user events (clicks, key events, hovers etc) are easily accessible and modifiable to the programmer. You can still have a nice plugin / API that will simplify customizations to the user.
Styling: The DIVs being rendered inside the user page will inherit that page styles.
The good part it that the chat app will have a design that suits perfectly the page design.
The hard part is that in your CSS you'll have to probably prevent some chat sensitive styles to be overwritten by the host page styles. Be careful.
Uses: people are gonna love it. If you want users to keep your link or logo you can ask them to keep the copyright or the link. You cannot count that this will happen. If you sell your app, or you just don't care, than I find this use the proper one.
Scroll and heights of chat elements are aware of the surrounding document. My suggestion here is to create a fluid chat app using %. That way your app will fit inside every container, and if it's a fluid page... more love for you.
So even if I would personally choose the <div> one, it's totally up to your needs.
Regarding scrollability I've created a nice UI technique:
Create a variable-flag that will register if the scrollable area is hovered
after you ping the server for the new message, run a function that will scroll the area to bottom
if the scrollable area is hovered means that the user is reading old chats
on mouseleave = scroll automatically the chat to the bottom (last conversation)
See it in action here
HTML:
<div class="chat">
<div class="messages">
<div>Old message</div>
</div>
<textarea></textarea>
<button>Post</button>
</div>
BASIC CSS (more CSS in the demo link):
.chat{
position:relative;
margin:0 auto;
width:300px;
overflow:hidden;
}
.chat .messages{
width:100%;
height:300px;
overflow:hidden;
}
.chat .messages:hover{
overflow-y:scroll;
}
.chat .messages > div{
padding:15px;
border-bottom:1px dashed #999;
}
jQuery:
var $chat = $('.chat'),
$printer = $('.messages', $chat),
$textArea = $('textarea', $chat),
$postBtn = $('button', $chat),
printerH = $printer.innerHeight(),
preventNewScroll = false;
//// SCROLL BOTTOM
function scrollBottom(){
if(!preventNewScroll){ // if mouse is not over printer
$printer.stop().animate( {scrollTop: $printer[0].scrollHeight - printerH }, 600); // SET SCROLLER TO BOTTOM
}
}
scrollBottom(); // DO IMMEDIATELY
function postMessage(e){
// on Post click or 'enter' but allow new lines using shift+enter
if(e.type=='click' || (e.which==13 && !e.shiftKey)){
e.preventDefault();
var msg = $textArea.val(); // not empty / space
if($.trim(msg)){
$printer.append('<div>'+ msg.replace(/\n/g,'<br>') +'</div>');
$textArea[0].value=''; // CLEAR TEXTAREA
scrollBottom(); // DO ON POST
// HERE Use AJAX to post msg to PHP
}
}
}
//// PREVENT SCROLL TO BOTTOM WHILE READING OLD MESSAGES
$printer.hover(function( e ) {
preventNewScroll = e.type=='mouseenter' ? true : false ;
if(!preventNewScroll){ scrollBottom(); } // On mouseleave go to bottom
});
$postBtn.click(postMessage);
$textArea.keyup(postMessage);
//// TEST ONLY - SIMULATE NEW MESSAGES
var i = 0;
intv = setInterval(function(){
$printer.append("<div>Message ... "+ (++i) +"</div>");
scrollBottom(); // DO ON NEW MESSAGE (AJAX)
},2000);
I will myself always go for a div for a chat application, Why?
Here is basic benefit. You can handle the events on a div, that you cannot handle using an iframe. You can try it for yourself, try to handle click, mouseover events inside an iframe, you won't get anything.
$('div').click(function () {
alert('Div was clicked!');
}
While iframe won't let you access events on the child elements of it.
While div will provide each and every event to the parent or even the js to handle and do the coding as necessary. For iframe you need to handle the events inside the iframe, lets say the page from where the iframe was loaded, its events are inside the code that was used to create it.
$('iframe').click(function () {
// code..this will execute when click is on iframe, not for a child
}
But you cannot do something as
$('iframe html body div').click(function () {
/* techniques for iframes are different and harder as
* compared to ones used for div, to get a child event
*/
})
But the elements inside the div can be embedded for your webpage. And you can always change its child or parent elements. So chat app will be better, if you can handle all the element events.
<div>
Some text
</div>
jQuery
$('div').on('event', function () { // on an event..
// so on, adding more and more event handlers and blah blah
})
In a div, you can just update the content using ajax request, and then add it to the div and you can also use jQuery API to scroll it. No matter how much page size, you can use % or exact place where to scroll to. So divs are simpler.
$('div').load('chat_page.php'); // load a page in the div
Or just update it using,
$.ajax({ // create ajax request
url: 'chat_message', // url
success: function (resp) { // if OK
$('div').html(resp); // update the page
}
});
Iframes are generally used to let others use your functionality, such as embedding chat application in a third party site, where you don't need them to edit or reuse your code. So you give them an iframe and a link.
Scolling thing was not understood by me! :( Sorry about that, I think I am going to write vague answer for that, so I will let that part go but this is how you can scroll the element
$('div').scrollTo(10); // scroll 10px down..
(You asked for browser support in comments) However, jQuery is supported cross-browser and cross platform. And the remaining part is HTML which is supported everywhere!
http://jquery.com/browser-support/ Here is a link to know the browser support
I prefer to use div as you can easily manage everything about it and it is easier to refresh, using less data for download for the server. Just a personal opinion.
PROS or DIV include less data, insert anywhere any time, and ability to easily use data for other tasks if needed on the page.
Pros of IFRAME easier to setup and code and easier ability to make it stand alone.
Cons of Iframe and it is harder to access data within and requires more code to do so if needed and cons of div are getting all the css and code right and inplace for the div and its parents and its children for it to flow correctly and nicely.

Categories

Resources