Loading content scrolling bottom - javascript

I am using the below functions to load more results when scrolling to the bottom.
Everything is working well; the only problem is that when some content is loaded, the window scrolls back to the top instead, as if to keep the same position that it had before rather than loading new results. Actually, it does not go back completely to the top, instead it goes back to the height of the first loaded result. Therefore, to see the content that was just loaded, you must scroll back to the bottom. This is where this 'cycle' will start over again...
$(window).scroll(function () {
if ($(document).height() <= $(window).scrollTop() + $(window).height()) {
document.getElementById("loader_bar").style.display = 'block';
load_result();
}
});
function load_result() {
counter = check_counter();
type = $("#search_type").html();
key = $("#search_key").html();
$("#load_result").html("<p id='loader_bar' style='width:100%; height:32px'></p>");
var par = "key=" + key + "&type=" + type + "&counter=" + counter;
$.ajax({
url: "ajax/search-columns-result.php",
success: show_result,
data: par,
type: "POST"
});
function show_result(rs) {
if (rs != '') {
$("#load_result").html(rs);
$('#loader_bar').css('display',"none");
}
}
}
function check_counter() {
if( typeof check_counter.counter == 'undefined' ) {
check_counter.counter = 3;
}
return check_counter.counter++;
}

It looks to me like each time you call show_results, you are overwriting any previous results from the auto load:
$("#load_result").html(rs);
This would cause your the elements to be removed and then the window would scroll up (because the height of the entire document is now shorter).
I think you instead want to call
$("#load_result").append(rs);
You would also need to change how you are creating/showing your loader. Instead of:
$("#load_result").html("<p id='loader_bar' style='width:100%; height:32px'></p>");
You would have a the #loader_bar in the DOM after the #load_result element, and simply toggle display: block/display: none
Demo of your code (slightly modified to get it to render, not sure what your DOM structure is like).
Demo of fix

after ajax success you can set different properties scroll bar.you can set scrollbar position after ajax call success/loading new content e.g
window.scrollTop(0); //for auto scroll to top

Related

How to execute javascript code after full load of PHP table?

My page has a somewhat large table (~ 300 lines), loaded from a database in PHP. When the user roll the table with any scrollbar (horizontal/vertical), I save both positions to go exactly there after page reload.
Problem is: sometimes the vertical position won't be fully restored, as if the code tried to execute before the PHP table is fully drawn.
I've tried to attach the code in different ways:
<body onload='load()'>
window.onload = load;
window.addEventListener('load',load);
document.addEventListener('DOMContentLoaded',load);
document.addEventListener('readystatechange', event => {
if (event.target.readyState === 'complete') {
load();
}
});
But none of the above works all the time. After every reload, the page sometimes scroll back to the right position, other times it scrolls upwards, until a point (~ 1000 px, which I can see in the console) where it's able to keep position indefinitely.
The code to save the scrollbar position is:
<div id='tab' onscroll='moved(this)'>
function moved(w) {
localStorage.setItem('db.scrLeft',w.scrollLeft);
localStorage.setItem('db.scrTop',w.scrollTop);
console.log(w.scrollTop);
}
And to retrieve the scrollbar position, inside load() is:
if (document.getElementById('tab')) {
var sl = parseInt(localStorage.getItem('db.scrLeft'),10);
var st = parseInt(localStorage.getItem('db.scrTop'),10);
if (sl && st) {
document.getElementById('tab').scrollLeft = sl;
document.getElementById('tab').scrollTop = st;
console.log(st);
}
}
Why is none of the above options respecting the proper table load, as they should? And how do I achieve it?
Solved it. The scrollbar was rolling during load, causing the moved function to be called. I added a condition (loaded) to allow its execution only after load.
var loaded = false;
function moved(w) {
if (loaded) {
localStorage.setItem('db.scrLeft',w.scrollLeft);
localStorage.setItem('db.scrTop',w.scrollTop);
}
}
function load() {
if (document.getElementById('tab')) {
var sl = parseInt(localStorage.getItem('db.scrLeft'),10);
var st = parseInt(localStorage.getItem('db.scrTop'),10);
if (sl && st) {
document.getElementById('tab').scrollLeft = sl;
document.getElementById('tab').scrollTop = st;
loaded = true;
}
}
}

javascript scrollTop doesn't seem to work

I use a technique where I have a table within a div so as to limit the space covered by the table and scroll instead.
Within the table are checkboxes. These checkboxes effect how the table is rendered. When one is clicked, the table is re-rendered within the div. This always causes the scrollbar to go back to the top which is annoying.
So after I render the table in javascript I do a setTimeout call to asynchronously call a function that sets the scrollTop value back to where it was before the re-render.
Here's the code snipit in question:
Note: (ge() == geMaybe() == document.getElementById())
o.renderAndScroll = function() {
var eTestSection = geMaybe(o.id + '-testSection');
var scrollTop = 0;
if (eTestSection) {
scrollTop = eTestSection.scrollTop;
}
o.render();
if (eTestSection) {
setTimeout(
function() {
console.log('Scrolling from ' + ge(o.id).scrollTop + ' to ' + scrollTop);
ge(o.id).scrollTop = scrollTop;
console.log('Scrolled to ' + ge(o.id).scrollTop);
},
1000);
}
}
My console log output is this each time I change a checkbox state:
Scrolling from 0 to 1357
Scrolled to 0
Any other way to make this work? Note that I made the timeout a full second just to make sure the render was moved to the DOM by the time my scroll code is called. I am using chrome mainly but need it to eventually work cross-browser. I don't use jQuery. If I try to catch the onscroll event in the debugger or even log stuff from an onscroll handler, the chrome debugger crashes when the scrollbar is moved with the mouse.
The correct code is:
o.renderAndScroll = function(fForce) {
var scrollTop = 0;
var eTestSection = geMaybe(o.id + '-testSection');
if (eTestSection) {
scrollTop = eTestSection.scrollTop;
}
o.render(fForce);
setTimeout(
function() {
// re-lookup element after being rendered
var eNewTestSection = ge(o.id + '-testSection');
eNewTestSection.scrollTop = scrollTop;
},
1);
};

Resizing function appends elements multiple times

I have a bit of code that needs to detect scrollbars on resize and load. This is straightforward enough, the issue arises when I run the append() function, it appends the elements over and over again.
Below is the jQuery I am using (the demo is here)
$.fn.hasScrollBar = function() {
return this.get(0).scrollWidth > this.width();
}
$(window).on("resize", function () {
var tableWidth = $("table").width();
var wrapper = "<div class='top-scroll'><div class='inner'></div></div>";
var instruction = "<span class='instructions'>Scroll left to see more information</span>";
// This sets the overflow message and a placeholder width to match the table
if( $('.no-overflow').hasScrollBar()){
$('.no-overflow').before(wrapper);
$('.top-scroll').before(instruction);
$('.top-scroll').children().attr("style", "width:" + tableWidth + "px;");
}
// This synchronises the scrollbars
$(".top-scroll").scroll(function(){
$(".no-overflow")
.scrollLeft($(".top-scroll").scrollLeft());
});
$(".no-overflow").scroll(function(){
$(".top-scroll")
.scrollLeft($(".no-overflow").scrollLeft());
});
}).resize();
I'd like the append to only happen once and also remove when there are no scrollbars present.
To check for scrollbars:
if ($(document).height() > $(window).height()) {
//There is a vertical scrollbar
} else {
//There is no vertical scrollbar
}
To only create one .top-scroll:
if( $('.no-overflow').hasScrollBar() && $('.top-scroll').length < 1) { // Check .top-scroll doesn't exists currently
$('.no-overflow').before(wrapper);
$('.top-scroll').before(instruction);
$('.top-scroll').children().attr("style", "width:" + tableWidth + "px;");
}
I would also put your 3 variable declarations within your if statement so they will only be set if the statement is true. Unless of course you are using them elsewhere.

Javascript vertical scrolling function

I am trying to detect a scroll on my page using JavaScript. So that I can change classes and attributes of some elements when user has scrolled certain amount of page. This is my JS function:
function detectScroll() {
var header = document.querySelector(".headerOrig"),
header_height = getComputedStyle(header).height.split('px')[0],
fix_class = "changeColor";
if( window.pageYOffset > header_height ) {
header.classList.add(fix_class);
}
if( window.pageYOffset < header_height ) {
header.classList.remove(fix_class);
}
var change = window.setInterval(detectScroll, 5000);
}
and I am calling it when the page is loaded:
<body onload="detectScroll();">
However, I have this problem - I need to set up a really small interval so that the function gets called and the class is changed immediately. BUT then the page freezes and everything except the JS function works very slowly.
Is there any better way of achieving this in JavaScript?
Thanks for any advice/suggestion.
You are going to want to change a couple things. First, we can use onscroll instead of an interval. But you are also going to want to cache as much as possible to reduce the amount of calculations on your scroll. Even further, you should use requestAnimationFrame (or simply "debounce" in general for older browsers -- see the link). This ensures your work only happens when the browser is planning on repainting. For instance, while the user scrolls the actual scroll event may fire dozens of times but the page only repaints once. You only care about that single repaint and if we can avoid doing work for the other X times it will be all the more smoother:
// Get our header and its height and store them once
// (This assumes height is not changing with the class change).
var header = document.querySelector(".headerOrig");
var header_height = getComputedStyle(header).height.split('px')[0];
var fix_class = "changeColor";
// This is a simple boolean we will use to determine if we are
// waiting to check or not (in between animation frames).
var waitingtoCheck = false;
function checkHeaderHeight() {
if (window.pageYOffset > header_height) {
header.classList.add(fix_class);
}
if (window.pageYOffset < header_height) {
header.classList.remove(fix_class);
}
// Set waitingtoCheck to false so we will request again
// on the next scroll event.
waitingtoCheck = false;
}
function onWindowScroll() {
// If we aren't currently waiting to check on the next
// animation frame, then let's request it.
if (waitingtoCheck === false) {
waitingtoCheck = true;
window.requestAnimationFrame(checkHeaderHeight);
}
}
// Add the window scroll listener
window.addEventListener("scroll", onWindowScroll);
use onscroll instead of onload so you don't need to call the function with an interval.
Your dedectScroll function will be triggered automatically when any scroll appers if you use onscroll
<body onscroll="detectScroll();">
Your function is adding an interval recursively, you should add an event listener to the scroll event this way :
function detectScroll() {
var header = document.querySelector(".headerOrig"),
header_height = getComputedStyle(header).height.split('px')[0],
fix_class = "changeColor";
if( window.pageYOffset > header_height ) {
header.classList.add(fix_class);
}
if( window.pageYOffset < header_height ) {
header.classList.remove(fix_class);
}
}
window.addEventListener("scroll",detectScroll);

infinite-scroll jquery plugin

I am trying to set up infinite-scroll on a site I am developing with Coldfusion, I am new to javascript and jquery so I am having some issues wrapping my head around all of this. Do I need to have pagination on my site in order to use the infinite-scroll plugin, or is there a way to do it with out it?
You do not need infinite scroll plug-in for this. To detect when scroll reaches end of page, with jQuery you can do
$(window).scroll(function () {
if ($(window).scrollTop() >= $(document).height() - $(window).height() - 10) {
//Add something at the end of the page
}
});
Demo on JsFiddle
I'm using Hussein's answer with AJAX requests. I modified the code to trigger at 300px instead of 10px, but it started causing my appends to multiply before the AJAX request was finished since the scroll call triggers much more frequently in a 300px range than a 10px range.
To fix this, I added a trigger that would be flipped on successful AJAX load. My code looks more like this:
var scrollLoad = true;
$(window).scroll(function () {
if (scrollLoad && $(window).scrollTop() >= $(document).height() - $(window).height() - 300) {
scrollLoad = false;
//Add something at the end of the page
}
});
then in my AJAX response, I set scrollLoad to true.
I built on top of Hussein's little example here to make a jQuery widget. It supports localStorage to temporarily save appended results and it has pause functionality to stop the appending every so often, requiring a click to continue.
Give it a try:
http://www.hawkee.com/snippet/9445/
$(function(){
$(window).scroll(function(){
if($(document).height()<=$(window).scrollTop()+$(window).height()+100){
alert('end of page');
}
});
});
Some one asked for explanation so here is the explanation
here $(document).height()-->is the height of the entire document.In most cases, this is equal to the element of the current document.
$(window).height()-->is the height of the window (browser) means height of whatever you are seeing on browser.
$(window).scrollTop()-->The Element.scrollTop property gets or sets the number of pixels that the content of an element is scrolled upward. An element's scrollTop is a measurement of the distance of an element's top to its topmost visible content. When an element content does not generate a vertical scrollbar, then its scrollTop value defaults to 0.
$(document).height()<=$(window).scrollTop()+$(window).height()+100
add $(window).scrollTop() with $(window).height() now check whether the result is equal to your documnet height or not. if it is equal means you reached at the end.we are adding 100 too because i want to check before the 100 pixels from the bottom of document(note <= in condition)
please correct me if i am wrong
I had same problem but didn't find suitable plugin for my need. so I wrote following code. this code appends template to element by getting data with ajax and pagination.
for detecting when user scrolls to bottom of div I used this condition:
var t = $("#infiniteContent").offset().top;
var h = $("#infiniteContent").height();
var ws = $(window).scrollTop();
var dh = $(document).height();
var wh = $(window).height();
if (dh - (wh + ws) < dh - (h + t)) {
//now you are at bottom of #infiniteContent element
}
$(document).ready(function(){
$.getJSON("https://jsonplaceholder.typicode.com/comments", { _page: 1, _limit:3 }, function (jsonre) {
appendTemplate(jsonre,1);
});
});
function appendTemplate(jsonre, pageNumber){
//instead of this code you can use a templating plugin like "Mustache"
for(var i =0; i<jsonre.length; i++){
$("#infiniteContent").append("<div class='item'><h2>"+jsonre[i].name+"</h2><p>"+jsonre[i].body+"</p></div>");
}
if (jsonre.length) {
$("#infiniteContent").attr("data-page", parseInt(pageNumber)+1);
$(window).on("scroll", initScroll);
//scroll event will not trigger if window size is greater than or equal to document size
var dh = $(document).height() , wh = $(window).height();
if(wh>=dh){
initScroll();
}
}
else {
$("#infiniteContent").attr("data-page", "");
}
}
function initScroll() {
var t = $("#infiniteContent").offset().top;
var h = $("#infiniteContent").height();
var ws = $(window).scrollTop();
var dh = $(document).height();
var wh = $(window).height();
if (dh - (wh + ws) < dh - (h + t)) {
$(window).off('scroll');
var p = $("#infiniteContent").attr("data-page");
if (p) {
$.getJSON("https://jsonplaceholder.typicode.com/comments", { _page: p, _limit:3 }, function (jsonre) {
appendTemplate(jsonre, p);
});
}
}
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div id="infiniteContent"></div>
If you have a scrollable element, like a div with scroll overflow, but no scrollable document/page, you can take this way.
$(function () {
var s = $(".your-scrollable-element");
var list = $("#your-table-list");
/* On element scroll */
s.scroll(function () {
/* The scroll top plus element height equals to table height */
if ((s.scrollTop() + s.height()) == list.height()) {
/* you code */
}
});
});
I wrote this function using Hussein and Nick's ideas, but I wanted it to use promises for the callback. I also wanted the infinite scrolling area to be on a fixed div and not just the window if the div is sent into the options object. There is an example of that in my second link below. I suggest using a promise library like Q if you want to support older browsers. The cb method may or may not be a promise and it will work regardless.
It is used like so:
html
<div id="feed"></div>
js
var infScroll = infiniteScroll({
cb: function () {
return doSomethingPossiblyAnAJAXPromise();
}
});
If you want the feed to temporarily stop you can return false in the cb method. Useful if you have hit the end of the feed. It can be be started again by calling the infiniteScroll's returned object method 'setShouldLoad' and passing in true and example to go along with the above code.
infScroll.setShouldLoad(true);
The function for infinite scrolling is this
function infiniteScroll (options) {
// these options can be overwritten by the sent in options
var defaultOptions = {
binder: $(window), // parent scrollable element
loadSpot: 300, //
feedContainer: $("#feed"), // container
cb: function () { },
}
options = $.extend(defaultOptions, options);
options.shouldLoad = true;
var returnedOptions = {
setShouldLoad: function (bool) { options.shouldLoad = bool; if(bool) { scrollHandler(); } },
};
function scrollHandler () {
var scrollTop = options.binder.scrollTop();
var height = options.binder[0].innerHeight || options.binder.height();
if (options.shouldLoad && scrollTop >= (options.binder[0].scrollHeight || $(document).height()) - height - options.loadSpot) {
options.shouldLoad = false;
if(typeof options.cb === "function") {
new Promise(function (resolve) {resolve();}).then(function() { return options.cb(); }).then(function (isNotFinished) {
if(typeof isNotFinished === "boolean") {
options.shouldLoad = isNotFinished;
}
});
}
}
}
options.binder.scroll(scrollHandler);
scrollHandler();
return returnedOptions;
}
1 feed example with window as scroller
2 feed example with feed as scroller

Categories

Resources