I have a recursive function that populates a list until the bottom of the screen is reached, then stops.
Each time the recursive function is called, the "start" global variable increments by the limit (5). For example, after 3 times, the "start" variable should be 15. This shows up correctly in the console.
However, outside the recursive function, it seems the "start" variable is still at "0". I thought using "var start = 0" outside of the function makes it global and should be changed by the Ajax success function. The code is below:
//****************************************************************
// Loading questions and answers listings
//****************************************************************
var tag = '';//initialize as blank
var search = '';//initialize as blank
var limit = 5;//number of records to autoload each time
var start = 0;//start at record 0
var action = 'inactive';//initial status to get initial load
var autoscroll = 'false';//check box to engage auto scroll or keep manual load more
var checked = '';//check box value for autoscroll
function load_data(limit, start, search, tag){//recursive function for auto loading
search = $('#search').val();
var loads;
$.ajax({
url:"/modules/posts/questions_all_autoscroll_fetchdata.php",
method:"POST",
data:{limit:limit, start:start, search:search, tag:tag},
cache:false,
success:function(data){
$('#load_data_message').show();//bring loading button back once load successful.
//It is removed during tag click search to avoid glitchy appearance of button showing at top left briefly.
$('#load_data').append(data);
//checks if quantity "limit" amount loaded. If not, that means end of data reached.
//Each data piece has id="container", so counting them tells how many data pieces returned in "loads"
loads = $(data).find('.card-header').length;
if(loads<limit){button_name = 'End of Data';}else{button_name='Loading More <i class="fas fa-spinner fa-lg fa-spin"></i>';}
if(autoscroll == 'false'){button_name = 'Load More'};
//append data returned to bottom of existing data
$('#load_data_message').html("<div id='please_wait' class='form-row text-center'>\
<div class='col-12'><button type='button' class='btn btn-warning'>\
"+button_name+"</button>\
<label style='cursor:pointer;'><input id='autoscroll' type='checkbox' name='autoscroll' value='true' "+checked+"\
style='transform:scale(2.0);margin-left:10px;margin-top:15px;cursor:pointer;'> Autoscroll</label><br></div></div>");
action = "inactive";
//recursive part - keep loading data until bottom of screen reached, stop when reached, or when end of data
start = start + limit;
console.log(start);
if(loads>=limit){
if($("#load_data").height() < $(window).height()){load_data(limit,start,search,tag);}
}
}
});
}
I do an initial load of the listing on page load with:
//for initial loading of data to the bottom of page (through recursive load_data function
if(action == 'inactive') {
action = 'active';
load_data(limit, start,search, tag);
}
Then if I want to initiate an auto/infinite scroll, I click the checkbox which initiates the code below. However, the "start" value is "0", where it should have been affected by the Ajax recursive function previously, and start at "15" instead. I get a repeat of previously posted data, which is not wanted.
//auto scroll checkbox clicked, putting into autoload/autoscroll mode
$(document).on("change", "#autoscroll", function(){
autoscroll = this.checked;
if(autoscroll == true){checked = 'checked';}else{checked = '';}
console.log(start);
load_data(limit, start, search, tag);
});
Then auto/infinite scroll works as follows. The "start" variable is continually updated successfully, but probably within the loop, rather than the recursive Ajax part.
//autoload portion. when scrolling reaches bottom of screen, triggers another load of data
$(window).scroll(function(){
if(autoscroll == true){
if($(window).scrollTop() + $(window).height() > $("#load_data").height() && action == 'inactive' && button_name != 'End of Data'){
action = 'active';
start = start + limit;
//load_data(limit, start, search, tag);
setTimeout(function(){
load_data(limit, start, search, tag);
}, 500);
console.log('scroll function called!');
}
}
});
Bottom line question - I would like to use the global variable "start" and have it changed by the recursive function's Ajax success function. How can this be achieved?
You have not mentioned what your 'data' response looks like in your ajax call.
I tried a response string that looked like this:
<div class='card-header'>hello</div>
<div class='card-header'>world</div>
<div class='card-header'>foo</div>
<div class='card-header'>bar</div>
<div class='card-header'>howdy</div>
In this case, loads = $(data).find('.card-header').length does not provide a length of 5. If loads is only what you loaded in that specific ajax call, then you need $(data).length. Then, the console log for start in the ajax call gets incremented.
Note: Jquery $.find works on a DOM tree. $(data) returns an array of individual nodes, and $(data).length will return the pieces of data returned.
The "loads = $(data).find('.card-header').length" does count the number of class='card-header' instances, contrary to what user2137480 has mentioned above, and everything does increment properly. The issue was that the "start" variable outside the Ajax success function was not being updated as the "load_data" function was called recursively.
Found a clumsy way to make this work. I used a session storage variable, "sessionStorage.start" that gets affected within the Ajax success function, even at recursive levels. Seems like session variables scopes are truly "global"! Then this sessionStorage.start variable is use elsewhere in the code to update the global "start" variable. This value is then maintained consistently throughout the code. Just have to remember that the session variables are text and must be converted to numbers (for this application anyways). See below if you are interested:
//****************************************************************
// Loading questions and answers listings
//****************************************************************
var limit = 5;//number of records to autoload each time
sessionStorage.start = 0;
var start = 0;//start at record 0
sessionStorage.start = start;
sessionStorage.scroll_function_enable = 'false';
var action = 'inactive';//initial status to get initial load
var autoscroll = 'false';//check box to engage auto scroll or keep manual load more
var checked = '';//check box value for autoscroll
function load_data(limit, start, search, tag){//recursive function for auto loading
console.log('load data begin');
console.log('recursive start: ' + start);
sessionStorage.scroll_function_enable = 'false';
search = $('#search').val();
var loads;
$.ajax({
url:"/modules/posts/questions_all_autoscroll_fetchdata.php",
method:"POST",
data:{limit:limit, start:start, search:search, tag:tag},
cache:false,
success:function(data){
$('#load_data_message').show();//bring loading button back once load successful.
//It is removed during tag click search to avoid glitchy appearance of button showing at top left briefly.
$('#load_data').append(data);
//checks if quantity "limit" amount loaded. If not, that means end of data reached.
//Each data piece has id="container", so counting them tells how many data pieces returned in "loads"
loads = $(data).find('.card-header').length;
//alert(loads);
if(loads<limit){button_name = 'End of Data';}else{button_name='Loading More <i class="fas fa-spinner fa-lg fa-spin"></i>';}
if(autoscroll == 'false'){button_name = 'Load More'};
//append data returned to bottom of existing data
$('#load_data_message').html("<div id='please_wait' class='form-row text-center'>\
<div class='col-12'><button id='load_button' type='button' class='btn btn-warning'>\
"+button_name+"</button>\
<label style='cursor:pointer;'><input id='autoscroll' type='checkbox' name='autoscroll' value='true' "+checked+"\
style='transform:scale(2.0);margin-left:10px;margin-top:15px;cursor:pointer;'> Autoscroll</label><br></div></div>");
action = "inactive";
//recursive part - keep loading data until bottom of screen reached, stop when reached, or when end of data
start = start + limit;
sessionStorage.start = start;//needed because recursive routine does not update the global "start" variable. Needed to pass value outside of recursive routine
console.log('recursive end: '+start);
if(loads>=limit){
if($("#load_data").height() < $(window).height())
{
load_data(limit,start,search,tag);
}else
sessionStorage.scroll_function_enable = 'true';
}
}
});
}
//for initial loading of data to the bottom of page (through recursive load_data function
if(action == 'inactive') {
action = 'active';
load_data(limit, start,search, tag);
start = Number(sessionStorage.start);//to get recursive value of start, otherwise will not retain its value
}
//auto scroll checkbox clicked, putting into autoload/autoscroll mode
$(document).on("change", "#autoscroll", function(){
autoscroll = this.checked;
if(autoscroll == true){checked = 'checked';}else{checked = '';}
start = Number(sessionStorage.start);//to get recursive value of start, otherwise will not retain its value
console.log('autoscroll start: ' + start);
load_data(limit, start, search, tag);
start = Number(sessionStorage.start);//to get recursive value of start, otherwise will not retain its value
});
//autoload portion. when scrolling reaches bottom of screen, triggers another load of data
$(window).scroll(function(){
if(autoscroll == true){
if($(window).scrollTop() + $(window).height() > $("#load_data").height() && action == 'inactive' && button_name != 'End of Data' && sessionStorage.scroll_function_enable == 'true'){
action = 'active';
start = Number(sessionStorage.start);//to get number where other processes have left off
console.log('scroll start: ' + start);
//load_data(limit, start, search, tag);
setTimeout(function(){
load_data(limit, start, search, tag);
}, 500);
start = Number(sessionStorage.start);//to get recursive value of start, otherwise will not retain its value
}
}
});
Related
I'm creating a webpage that will automatically refresh a content to always get the latest data in the database. This is my code in Javascript.
setInterval(function() {
$("#status").load('refresh.php');
},1000);
In my javascript, I'm automatically refreshing the #status with refresh.php every second.
But when I inspect the element of my webpage and click on the network. It is also requesting(spamming) the refresh.php every second which is I think a bad practice.
Can you help me how to fixed this? Or can you suggest better ajax code to auto refresh a content without requesting the php file too much?
You should probably use a websocket for this, get the latest status as data from the server and then change only that section of the page using javascript
This code will be request refresh.php later at timeouts.min every time when new (requested) value of #status is equal to current. If values are different, next timeout will be timeouts.min again.
var timeouts={
min:1000, // min await timeout = 1s
max:16000 // max await timeout = 16s
};
var currentTo = timeouts.min;
function setAutoRefresh( to ){
setTimeout( function() {
var current = $("#status").text();// or .html()
$("#status").load('refresh.php', function() {
// if we are load a new value
if( current !== $("#status").text()) {
currentTo = timeouts.min;
// if loaded value are same to current
} else {
currentTo += timeouts.min; // or currentTo *= 2; for example
if( currentTo > timeouts.max ){
currentTo = timeouts.max;
}
}
console.log('Next #status refresh after ' + currentTo + 'ms');
// Set a new timeout
setAutoRefresh( currentTo );
});
}, to);
}
// Set the first timeout
setAutoRefresh( timeouts.min );
Is there a way in react or javascript to after reloading a page make that page to be at the same place where the user was looking at? So far, location.reload reloads the page but places that page at its beginning.
Add additional true parameter to force reload.
document.location.reload(true)
document.location.reload() stores the position,
ordinary reload tries to restore the scroll position after reloading the page, while in forced mode (when parameter is set to true) the new DOM gets loaded with scrollTop == 0.
Reference : https://developer.mozilla.org/en-US/docs/Web/API/Location/reload
You can add a scroll listener, and on scroll, save the current position scrolled in sessionStorage. Then, on pageload, check to see if the sessionStorage item is populated, and if it is, set the current scroll position to the number saved in sessionStorage:
if (sessionStorage.scroll) document.documentElement.scrollTop = sessionStorage.scroll;
window.addEventListener('scroll', () => {
sessionStorage.scroll = document.documentElement.scrollTop;
});
https://jsfiddle.net/1rg37zfd/
(Cannot embed into stack snippet because it doesn't support sessionStorage)
You can use localStorage to set a value. then in your init() check for where the user was last seen. The nextBtn, in the function for that, that is where we increment the page number and save it. i usually use a very minimized sync function to handle any refreshing i may want to do.
HTML:
<div id='myPage'></div>
JavaScript:
(function () {
"use strict";
var myUI, userData, myPages;
myPages = [
"Home","Page 2","Page 3","Page 4","Page 5"//when the user's page number is summoned with this object, pages will reflect when the syncFunc is ran
];
userData = 0;//out item where we save the users page number
myUI = {
init: () => {
var uData = localStorage.getItem("userData");//we look to see what is the value
if (!uData || uData === null) {
localStorage.setItem("userData", 0);//this only sets the user's page to 0 on first load or if anything goes wrong
}
var pageHolder = document.createElement("h3"),
nextBtn = document.createElement("button");
nextBtn.innerHTML = " > ";
nextBtn.onclick = myUI.nextPage(userData, pageHolder);
pageHolder.innerHTML = myPages[uData];
myPage.appendChild(pageHolder);
myPage.appendChild(nextBtn);
myUI.syncPage(uData, pageHolder, nextBtn);
},
syncPage: (uData, pageHolder) => {//i mean, you could do a whole window reload and it still work, but why do that when you can just sync things on the fly
pageHolder.innerHTML = myPages[uData];
//alert(uData);
},
nextPage: (userData, pageHolder) => {
return () => {
var uData = localStorage.getItem("userData"),//checking for the data when the user hits a button.
newPage;
if (uData >= 4) { newPage = +uData - 4 } else { newPage = +uData + 1; }//iterate by 1 unless we reach our limit
localStorage.setItem("userData", newPage);//save it
uData = localStorage.getItem("userData");//force our script to recognize the newly saved value
setTimeout(() => {//timeout for dramatic effect if desired
myUI.syncPage(uData, pageHolder);//run a function to sync
}, 100);
}
}
};
window.onload = () => {
myUI.init();//initialize
};
})();
I'm a beginner to angularjs and ionic and i'm implementing infinite scroll, with my current script when it loads the second set of records, it not able to capture the lastid and the new generated records to pass it.
For example when the page loads for the first time (displaying 2 records at a time) and the lastid is 10 when a user scroll to the bottom of the page it passes the lastid 10 to get the next set of records when you scroll down again, it passes that same lastid of 10 instead of 8
$scope.lastid = "";
$http.get('http://localhost/myapp/feeds.php').success(function(data){
console.log(JSON.stringify(data));
$scope.feeds=data;
$scope.lastid = $scope.feeds[$scope.feeds.length - 1].user_id;
});
$scope.addMoreItem = function(){
$http.get('http://localhost/myapp/feeds_more.php?user_id='+$scope.lastid).success(function(data){
$scope.temp=data;
for(var i = 0; i < $scope.temp.length - 1; i++) {
$scope.feeds.push($scope.temp[i]);
if(i == $scope.temp.length - 1){
$scope.lastid = $scope.temp[i].user_id;
}
}
});
$scope.$broadcast('scroll.infiniteScrollComplete');
};
HTML
<ion-infinite-scroll on-infinite="addMoreItem()" distance="1%"> </ion-infinite-scroll>
And one thing is before the first record displays the chrome console read a lot of this error:Cannot read property 'push' of undefined and when the page is still it automatically reloads the same records again
There may not be a fix for this. I am using a jquery drop down menu that loads when the DOM is ready. From what I understand this means it waits until the page is fully loaded until it becomes ready to be used.
This is problematic for a menu system because people want to use the menu right away often before the entire page is loaded.
Here is my site where you can see this happening.
http://bit.ly/g1sn5t
This is my script that I am using for the menu
$(document).ready(function() {
function megaHoverOver(){
$(this).find(".sub").stop().fadeTo('fast', 1).show();
//Calculate width of all ul's
(function($) {
jQuery.fn.calcSubWidth = function() {
rowWidth = 0;
//Calculate row
$(this).find("ul").each(function() {
rowWidth += $(this).width();
});
};
})(jQuery);
if ( $(this).find(".row").length > 0 ) { //If row exists...
var biggestRow = 0;
//Calculate each row
$(this).find(".row").each(function() {
$(this).calcSubWidth();
//Find biggest row
if(rowWidth > biggestRow) {
biggestRow = rowWidth;
}
});
//Set width
$(this).find(".sub").css({'width' :biggestRow});
$(this).find(".row:last").css({'margin':'0'});
} else { //If row does not exist...
$(this).calcSubWidth();
//Set Width
$(this).find(".sub").css({'width' : rowWidth});
}
}
function megaHoverOut(){
$(this).find(".sub").stop().fadeTo('fast', 0, function() {
$(this).hide();
});
}
var config = {
sensitivity: 2, // number = sensitivity threshold (must be 1 or higher)
interval: 0, // number = milliseconds for onMouseOver polling interval
over: megaHoverOver, // function = onMouseOver callback (REQUIRED)
timeout: 0, // number = milliseconds delay before onMouseOut
out: megaHoverOut // function = onMouseOut callback (REQUIRED)
};
$("ul#topnav li .sub").css({'opacity':'0'});
$("ul#topnav li").hoverIntent(config);
});
function clearText(field){
if (field.defaultValue == field.value) field.value = '';
else if (field.value == '') field.value = field.defaultValue;
}
// JavaScript Document
Is there anyway to get this to load before everything else?
You can put those scripts wherever you whant in the case of understanding exactly what you are doing.
HTML are rendered sequentially, so scripts cannot get the DOM object or js variables defined later in the document. That's why we usually use document onload event to do the init thing since all elements are loaded.
In this case, I guess you can probably put the scripts without document.ready right after the closing of ul#topnav tag.
I am trying to get this to work in my iweb page hosted not on MobileMe. With the code below I continue to get the alert box on each refresh of the page instead of once per session. I am a total newbie here so be kind please.
//Alert message once script- By JavaScript Kit
//Credit notice must stay intact for use
//Visit http://javascriptkit.com for this script
//specify message to alert
var answer=confirm("Click OK if you have been cleared Sutter's HR department to start
volunteering.")
if (answer)
alert ("Excellent!! Please select your dates directly within the scheduling calendar.")
else
alert ("Let's get to it then. Contact Ruth in HR at 576-4208 to schedule an appointment so you can get started.")
///No editing required beyond here/////
//answer only once per browser session (0=no, 1=yes)
var once_per_session=1
function get_cookie(Name) {
var search = Name + "="
var returnvalue = "";
if (document.cookie.length > 0) {
offset = document.cookie.indexOf(search)
if (offset != -1) { // if cookie exists
offset += search.length
// set index of beginning of value
end = document.cookie.indexOf(";", offset);
// set index of end of cookie value
if (end == -1)
end = document.cookie.length;
returnvalue=unescape(document.cookie.substring(offset, end))
}
}
return returnvalue;
}
function alertornot(){
if (get_cookie('alerted')==''){
loadalert()
document.cookie="alerted=yes"
}
}
function loadalert(){
alert(alertmessage)
}
if (once_per_session==0)
loadalert()
else
alertornot()
</script>
Your code calls this once per session:
alert(alertmessage)
but the code on top is called on each load of the script.
Moreover - I don't see where alertmessage is defined...
So You probably want to put the code from the top inside the loadalert function resulting in this:
function loadalert(){
var answer=confirm("Click OK if you have been cleared Sutter's HR department to start volunteering.")
if (answer)
alert ("Excellent!! Please select your dates directly within the scheduling calendar.")
else
alert ("Let's get to it then. Contact Ruth in HR at 576-4208 to schedule an appointment so you can get started.")
}
EDIT:
And BTW - start using curly braces. It helps in debug and in understanding where You are. :)