Ajax infinite scroll feature firing twice. - javascript

For some weird reason the ajax function below is firing twice. The loadMoreResults(); function is used to retrieve data from server. I tracked the data sent to the server using google developer tool it showed that the ajax request was fired twice in a row. I guess this happens when the user scroll fast.
//Executed when user has scrolled bottom of the page
$(window).scroll(function (e) {
if ($(window).scrollTop() == $(document).height() - $(window).height()) {
loadMoreResults();
}
});
Any idea on how to prevent this from happening?
Appreciate your help.

As you said, it could be because the user is scrolling too fast and the document does not get to be updated with the new results.
Try using a flag that will prevent calling the loadMoreResults() while the function is still executing.
You set it to true when the function starts and at the end, after you get your results, you set it to false. The check of the flag can be placed right at the beginning of the loadMoreresults() function, before setting the flag to true.
eg:
function loadMoreResults() {
if (flag) return;
flag = true;
[...]
flag = false;
}

For some reason I can't figure out. Setting a flag wasn't working for me. The function call would fire twice still.
I had to use a timeout in my success callback
.then(function(resp) {
var i, len, ref, t;
ref = resp.data.users;
for (i = 0, len = ref.length; i < len; i++) {
t = ref[i];
$scope.users.push(t);
}
return setTimeout((function() {
if (resp.data.users.length > 0) {
return $scope.busy = false;
}
}), 500);

Related

JQuery $.post callback firing a function that never finishes

Here's the problem. I'm making a callback to the server that receives an MVC partial page. It's been working great, it calls the success function and all that. However, I'm calling a function after which iterates through specific elements:
$(".tool-fields.in div.collapse, .common-fields div.collapse").each(...)
Inside this, I'm checking for a specific attribute (custom one using data-) which is also working great; however; the iterator never finishes. No error messages are given, the program doesn't hold up. It just quits.
Here's the function with the iterator
function HideShow() {
$(".tool-fields.in div.collapse, .common-fields div.collapse").each(function () {
if (IsDataYesNoHide(this)) {
$(this).collapse("show");
}
else
$(this).collapse("hide");
});
alert("test");
}
Here's the function called in that, "IsDataYesNoHide":
function IsDataYesNoHide(element) {
var $element = $(element);
var datayesnohide = $element.attr("data-yes-no-hide");
if (datayesnohide !== undefined) {
var array = datayesnohide.split(";");
var returnAnswer = true;
for (var i in array) {
var answer = array[i].split("=")[1];
returnAnswer = returnAnswer && (answer.toLowerCase() === "true");
}
return returnAnswer;
}
else {
return false;
}
}
This is the way the attribute appears
data-yes-no-hide="pKanban_Val=true;pTwoBoxSystem_Val=true;"
EDIT: Per request, here is the jquery $.post
$.post(path + conPath + '/GrabDetails', $.param({ data: dataArr }, true), function (data) {
ToggleLoader(false); //Page load finished so the spinner should stop
if (data !== "") { //if we got anything back of if there wasn't a ghost record
$container.find(".container").first().append(data); //add the content
var $changes = $("#Changes"); //grab the changes
var $details = $("#details"); //grab the current
SplitPage($container, $details, $changes); //Just CSS changes
MoveApproveReject($changes); //Moves buttons to the left of the screen
MarkAsDifferent($changes, $details) //Adds the data- attribute and colors differences
}
else {
$(".Details .modal-content").removeClass("extra-wide"); //Normal page
$(".Details input[type=radio]").each(function () {
CheckOptionalFields(this);
});
}
HideShow(); //Hide or show fields by business logic
});
For a while, I thought the jquery collapse was breaking, but putting the simple alert('test') showed me what was happening. It just was never finishing.
Are there specific lengths of time a callback function can be called from a jquery postback? I'm loading everything in modal views which would indicate "oh maybe jquery is included twice", but I've already had that problem for other things and have made sure that it only ever includes once. As in the include is only once in the entire app and the layout is only applied to the main page.
I'm open to any possibilities.
Thanks!
~Brandon
Found the problem. I had a variable that was sometimes being set as undefined cause it to silently crash. I have no idea why there was no error message.

How to start javascript setinterval once cleared?

I have a setinterval that runes every 5 seconds. this works fine on page load.
I have the following scenarios:
Load page with interval (WORKS)
press button and load new content and stopp interval(WORKS)
Once the new content is no longer desiered, dissmiss it, return to first content and start interval again(DOES NOT WORK)
I have saftys suchs as events for window.blur that also stops the interval so that the browser does not commponsate for all the missing intervals if i would change tabs or something. Keep in mind that step 3 did not work BUT if i would after step 3 change a tab and then return to my original page(execute blur) the interval would start working again.
NOTE all content loading here exept page load is done with ajax calls.
My code:
initializing:
$.automation.worker.bindIntervalEvent("#TanksContent", "/Tank/GetTanks", function() {
$.automation.tanks.tableInit();
});
binding function:
bindIntervalEvent: function (target, url, callback) {
$(window)
.on("focus.mine",
function() {
$.automation.worker.setUpdateInterval(target, url, callback);
})
.on("blur",
function() {
$.automation.worker.stopUpdateInterval();
}).trigger("focus.mine");
}
interval function:
setUpdateInterval: function (target, url, callback) {
if ($.automation.globals.globalInterval.value.length === 0) {
$.automation.globals.globalInterval.value.push(window.setInterval(
function () {
var options = {
loadTarget: target
}
$.automation.worker.getView(url,
function() {
if (callback)
callback();
},
options);
},
5000));
}
}
the function that stops the interval:
stopUpdateInterval: function () {
if ($.automation.globals.globalInterval.value.length === 0)
return;
console.log("deleting");
for (var i = 0; i <= $.automation.globals.globalInterval.value.length; i++) {
window.clearInterval($.automation.globals.globalInterval.value[i])
$.automation.globals.globalInterval.value.splice(i, 1);
console.log($.automation.globals.globalInterval.value.length);
}
}
when stopping the interval i also remove the window bindings:
unBindIntervalEvent: function() {
$(window).off("focus.mine");
$(window).unbind("blur");
}
Back to step 3:
My sucess method in the callback to my getviewfunction is identical to what i execute in the beginning
code:
$(".updatelatest")
.on("click",
function () {
var _this = $(this);
var options = {
loadTarget:"#TanksContent"
}
$.automation.worker.getView("/Tank/GetTanks",
function (data) {
$(_this).switchClass("col-md-5", "col-md-1", 1000, function() {
$(_this).addClass("hidden");
$(".search").switchClass("col-md-5", "col-md-12", 1000, "easeInOutQuad");
})
$.automation.tanks.tableInit();
$.automation.worker.bindIntervalEvent("#TanksContent", "/Tank/GetTanks", function () {
$.automation.tanks.tableInit();
});
$(window).trigger("blur");
}, options);
});
but this does not start the interval. it is clearly initialized since it works when window.blur is executed for example when I change tab but for some reason this is not working beyond that.
i tried triggering the windows blur event and nothing happened, i tried triggering my custom window event "focuse.mine" but nothing happens.
I did not notice this while developing since I had firebug open and every time i checked scripts or css or the console the blur function was executed so I assumed that my code worked as intended but now that it is deployed I notice this.
My head is pounding beyond reason and I can't for figure out where I have gone wrong.
Well this was a fun one. I simply found that when calling the setUpdateInterval(); function directly it gave me the desiered result.
I realized that the reason I had them split like I did was becaouse of the blur event. "Focus.mine" is triggered to start the inteval again ocne a user comes back to the page.

Prevent calling ajax on scroll when already called

I have a plugin that tells me if an element is visible in the viewport with $('#element').visible() (set to true when visible).
Now I want to create a function that I scroll down a page and load new content with ajax. I have this so far:
window.onscroll = function() {
console.log($('#ele').visible());
if ($('#ele').visible()) {
//ajax call comes here
}
};
As soon as I see the element my log shows true:
I don't have problems implementing the ajax-request now, but shouldn't I block this function to occur only once? How could I prevent that a new element that already has been loaded to load again (prevent using ajax again)?
I thought of using a boolean-variable, but my problem is that I don't know how to implement that because if I set a variable, how would the browser know it's value? Because on every move of my mousewheel it cant remember what that variable's value was?
EDIT:
I tried the code of Ismail and it never reaches the ajax call (alert won't show).
window.onscroll = function() {
var ajaxExecuted = false;
var ele = $('#load_more').visible();
console.log(ele);
return function() {
if (ajaxExecuted) return;
if (ele) {
alert("OK");
var ajaxArray;
ajaxArray = { page: 2 }
ajaxLoadContent(ajaxArray, "load_more", "ajax_load");
ajaxExecuted = true;
}
}
};
You can use this:
window.onscroll = (function() {
var ajaxExecuted = false;
return function() {
if(ajaxExecuted) return;
if ($('#ele').visible()) {
$.ajax({...}).success(function() {
//Your code here;
ajaxExecuted = true;
});
}
}
})();
One easy solution: set a boolean to true when the element first becomes visible and set it to false when it stops being visible. Only fire the request if those states mismatch (i.e. if it's visible but the boolean is false - that means it's the first time you've seen the window. You'd then set the bool afterwards so it won't fire off anymore until it disappears and reappears again).

Infinite scroll fired twice on refresh from the bottom of page

I have an infinite scroll set up with the following piece of code.
$(window).scroll(function () {
if ($(window).scrollTop() >= $("#home_content").height() - $(window).height()) {
if (isLastPage) {
foo();
} else {
bar(); // JQuery AJAX call
}
}
});
This is inside document.ready();
The ajax call doesn't happen when the server sends a flag for the last page. This works fine in a normal scenario. But when I press F5(Refresh) from the bottom of the page, two simultaneous scroll events are fired,and it bypasses the flag (as the second call happens even before the flag is set) and duplicate data is loaded.
The only thing i know is it happens at the end of document.ready() function. Anyone, any idea??
Thanks in advance.
EDIT
There is no much relevant code other than this.
And this happens only in FF 17.
In IE 9 when I do a fast scroll down, same scroll is fired twice
You can use this debounce routine for all sort of event calls. Clean and reusable.
// action takes place here.
function infinite_scrolling(){
if ($(window).scrollTop() >= $("#home_content").height() - $(window).height()) {
if (isLastPage) {
foo();
} else {
bar(); // JQuery AJAX call
}
}
}
// debounce multiple requests
var _scheduledRequest = null;
function infinite_scroll_debouncer(callback_to_run, debounce_time) {
debounce_time = typeof debounce_time !== 'undefined' ? debounce_time : 800;
if (_scheduledRequest) {
clearTimeout(_scheduledRequest);
}
_scheduledRequest = setTimeout(callback_to_run, debounce_time);
}
// usage
$(document).ready(function() {
$(window).scroll(function() {
infinite_scroll_debouncer(infinite_scrolling, 1000);
});
});
This is just a workaround as we cannot see your complete code, but maybe thats can help:
var timeout;
$(window).scroll(function(){
clearTimeout(timeout);
timeout = setTimeout(function(){
if ($(window).scrollTop() >= $("#home_content").height() - $(window).height()){
if (isLastPage){
foo();
}else{
bar();//AJAX call
}
}
},0);
});

Using Javascript to detect the bottom of the window and ignoring all events when a request is loading

I have an anonymous function to detect the user has scrolled to the bottom of the window. Inside of the anonymous function, I have a call to a database that takes a while to complete.
var allowing_more = 1;
$(window).scroll(function() {
if (allowing_more == 1){
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
allowing_more = 0;
//query
allowing_more = 1;
}
}
});
In this time, if the user scrolls to the bottom of the window again, it seems a queue is made holding the occurences the user scrolled to the bottom of the window while the query was loading. Upon completing of the query, these occurences are then executed.
I have a boolean statement to detect if the anonymous function is accepting more query requests but this seems to be ignored.
Is there some sort of way to ignore an anonymous function temporarily and re-enable it?
All you need is you have to stop querying your database until the previous request completes is this correct?
if this is correct
var chkFlg=0;
$(window).scroll(function() {
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
if(chkFlg===1){
//query your database after you get your result from db assign 1 to chkFlg
chkFlg = 1;
}
}
});
var flag = 0;
$(window).scroll(function() {
if(flag===0){
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
//query
flag = 1;
}
flag = 0;
}
});
This should work
Just follow this kind of pattern and your boolean condition should work.
safeToQuery = true; //
$(window).scroll(function() {
...
if (safeToQuery) {
safeToQuery = false;
//send query request
$.ajax({...}).done(function(data,etc){
//do whatever with the results
}).always(function (){
//regardless of whether or not the last request resulted
//in an error, make it safe to query again
safeToQuery = true;
});
}
});
Note: safeToQuery doesn't have to be a global, you could declare it as a local variable in an Immediately-Invoked Function Expression (IIFE) or just make it a property of some object that lives at least as long as you need to keep the scroll handler active.

Categories