How to cancel jquery execution upon action - javascript

I have a table displaying rows of records.
The goal is for the user to hover their mouse over any given row for 1 second, display a popup with a loading animation, and load the popup with various info about that record. If they mouse over another records, it will remove the original popup and start the process anew.
The problem I'm having is controlling when to stop the execution of these record detail calls. If I hover my mouse over various records before the previous one fully loads, when I finally do stop moving my mouse the popup cycles through all the records previously requested.
I ideally I need the execution to stop when the mouse leaves the record row. Also, execution should stop and the popup should hide upon clicking the record row, as clicking the row performs other actions.
My code so far:
var timeout;
$("#tblQueueItems tr").mouseenter(function (cRow) {
var cRowCopy = $(this);
cRowCopy.addClass('hover');
hideBox();
clearTimeout(timeout);
timeout = setTimeout(function () {
hideBox();
var orderId = cRowCopy.attr('oid');
showBox("<div id='divLoading'><img src='/images/AjaxLoader.gif' /></div>", RowCopy, cRow);
$.get('/api/WorkQueue/GetWQIOrderModal', { orderId: orderId }, function (data) {
showBox(data, cRowCopy, cRow);
});
}, 1000);
cRowCopy.mouseleave(function () {
clearTimeout(timeout);
cRowCopy.removeClass('hover');
});
cRowCopy.click(function () {
clearTimeout(timeout);
hideBox();
cRowCopy.removeClass('hover');
});
});
$("#divOrderPopup").mouseenter(function () {
var orderId = $("[id^=hf_]").attr('id').split("_").pop();
$("#tr_" + orderId).addClass('hover');
});
$("#divOrderPopupMoving").mouseleave(function () {
var orderId = $("[id^=hf_]").attr('id').split("_").pop();
$("#tr_" + wqiId).removeClass('hover');
hideBox();
});
}
function showBox(text, obj, e) {
var left = (e.pageX + 25) + 'px';
var top = (e.pageY - 200) + 'px';
var node = "<div style=\"z-index: 25;background: #ffffff;width: 715px;padding: 10px;border: 2px solid black;\" id=\"popBox\">";
node += text;
node += "</div>";
$("#divOrderPopup").css('position', 'absolute');
$("#divOrderPopup").css('top', top);
$("#divOrderPopup").css('left', left);
$("#divOrderPopup").html(node);
}
function hideBox() {
window.node = document.getElementById('popBox');
$("#divOrderPopupMoving").html('');
window.on = false;
}

You need to save your AJAX request to a variable, for example to a global one:
req = $.get('/api/WorkQueue/GetWQIOrderModal', { orderId: orderId }, function (data) {
showBox(data, cRowCopy, cRow);
});
and on mouseleave you can call req.abort() :
$("#divOrderPopupMoving").mouseleave(function () {
req.abort();
var orderId = $("[id^=hf_]").attr('id').split("_").pop();
$("#tr_" + wqiId).removeClass('hover');
hideBox();
});
I think this was your question/problem so far because you didn't told us where you stuck.

Related

Timeout on mouse enter and leave

I have an unordered list, which changes position when hovering each child element. If I don't put a timeout on mouseover it jumps quickly through the list due to the position changing. What I've noticed is when hovering one li then jumping to the next li, the timeout doesn't finish. I have to leave the li element, wait then re-hover for the timeout to cancel.
I want to be able to hover each li element to update the ul position, but with a timeout so it's not constantly jumping through the list.
I'm open to other suggestions, if this isn't the best way around resolving this.
var time, allow = true;
$("ul").children("li").each(function(index) {
$(this).on('mouseover', function() {
if(allow == true) {
var i = index + 1;
var calc = $('ul').height() / $('ul').children("li").length * i;
$("ul").css('transform', 'translate(-50%, -'+ calc +'px)');
allow = false;
}
}).mouseout(function () {
time = setTimeout(function () {
allow = true;
}, 1000);
});
});
Update: When leaving the current element then hovering the next element the 'allow' isn't finishing the mouseout delay.
var time, allow = true;
$("ul").children("li").each(function(index) {
$(this).find('a').mouseover(function() {
if(allow == true) {
allow = false;
var i = index + 1;
var calc = $('ul').height() / $('ul').children("li").length * i;
$("ul").css('transform', 'translate(-50%, -'+ calc +'px)');
}
});
$(this).mouseout(function () {
time = setTimeout(function () {
allow = true;
}, 1000);
});
});
.bind() is deprecated, so if you dont have to for compatibility reasons, use .on() instead.
Nevertheless what you do is calling .mouseout() on the return value of bind(). There is no documented return value
for bind or on, so you should probably make a separate call like so and while your at it just use the shorthands mouseover() and mouseout() both times:
$("ul").children("li").each(function(index) {
$(this).mouseover(function() {
if(allow == true) {
// do stuff
allow = false;
}
})
$(this).mouseout(function () {
time = setTimeout(function () {
allow = true;
}, 1000);
});
});

Can't read undefined property for the second time

I've run into some problems while creating solitaire game.
I'm working on the remaining deck, on which, when clicked, will be show remaining cards, so player can cycle through them while clicking on the deck.
The problem itself: when you click on the deck code generates a picture file name taking parameters from 2 different arrays.
The first time after click the card appears without any error, but when one tries to click for the second time error appears: "Cannot read property 'Taskai' of undefined".
Code:
$.widget("Game.RemainingDeck", {
options: {
remainingDeck: []
},
_create: function() {
var game = this;
game.flipACard();
},
flipACard: function() {
var i = 0;
var game = this;
$("#remDeck").click(function() {
var remainDeck = game.options.remainingDeck;
var remDeck = $('#remDeck');
var oppenedCard = $('#openCard');
var card = $('<div class=" card"></div> ');
var imageName = remainDeck[i].Taskai + '_of_' + remainDeck[i].Simbolis + '.png';
var imagePath = 'texture/' + imageName;
card.css('background-image', 'url("' + imagePath + '")');
if (i = game.options.remainingDeck.lenght) {
aler("kaladë prasideda ið naujo");
}
i++;
oppenedCard.append(card);
});
}
})
i needs to be an outer var, otherwise it is 0 every time the click handler runs.
Also, I don't think the click handler should be attached in .flipcard(), otherwise it will be attached over and over, every time flipCard() is run. Try attaching the click handler in .create_().
$.widget("Game.RemainingDeck", {
options: {
remainingDeck: []
},
_create: function() {
var game = this;
var i = 0;
$("#remDeck").on('click', function() {
$('#openCard').append($('<div class=" card"></div>').css('background-image', 'url("texture/' + game.options.remainingDeck[i].Taskai + '_of_' + game.options.remainingDeck[i].Simbolis + '.png")'));
if(i === game.options.remainingDeck.length) {
alert("kaladë prasideda ið naujo");
}
i++;
});
this.flipACard();
},
flipACard: function() {
$("#remDeck").click();
}
})
Presumably you .pop() (or .shift()) cards off remainingDeck at some point? You could possibly do so on flipping, in which case the i counter is unnecessary.

jquery delay function with IF statement

Can anyone tell me why my IF statement is firing before updating the UI with the each loop?
The code basically wants to delay adding css classes to the UI then once each one has been added, redirect the user. It currently just directs immediately?!
$("#logo").click(function() {
//define variables:
var eventDuration = 500;
var elementArray = ['ribbon', 'left-panel', 'wid-id-1', 'wid-id-2'];
var animationArray = ['slideOutRight', 'slideOutLeft', 'rotateOutUpRight', 'rotateOutUpRight'];
//Loop through elements and update UI with timer function:
$.each(elementArray, function(index, value) {
//Increments the delay of the element updates:
var delaytimer = index * eventDuration + eventDuration;
//Adds animation css classes to onpage elements:
$('#' + value).delay(delaytimer).queue(function() {
$(this).addClass('animated ' + animationArray[index]).dequeue();
});
//Once complete redirect to the home page:
if (index === 3) {
$(this).delay(delaytimer + 500).queue(function() {
window.location.replace('/').dequeue;
});
}
});
});
Your if statement is being executed immediately because it isn't inside the delay function. Try moving it in there.
$('#' + value).delay(delaytimer).queue(function() {
$(this).addClass('animated ' + animationArray[index]).dequeue();
//Once complete redirect to the home page:
if (index === 3) {
$(this).delay(delaytimer + 500).queue(function() {
window.location.replace('/').dequeue;
});
}
});

How do I make the UI in IE 8 update as lines of JavaScript are executed in a function, rather than at the end of of the function?

I'm trying to display a progress bar while ajax calls populate a form. I have a function called LOADFORM(). It launches a jquery dialog box, displays a progress bar, calles a few other non-async ajax calls to get data. With each data call complete it advances the progress bar and at the end it hides the progressbar and displays the form. This works perfecly in Firfox, but in IE, it just shows the completed form. I doesn't update the UI until the function is done running and by that time everything is complete, but the user has to sit at a uneventful screen for several seconds. How do I make the UI in IE 8 update as lines of JavaScript are executed in a function?
Example:
ActionReportForms.prototype.LoadFormData = function (constId, formType) {
//HOOK UP DATE PICKER
$('#' + this.TPLDATEFIELD_ID).datepicker();
$('#' + this.CRDATEFIELD_ID).datepicker();
//CLEAR FIELDS
this.ClearFormFields();
//HIDE ERRORS
this.ShowError(false, "");
//SHOW PROGRESS BAR
this.ShowProgress(true, 30, "loading...");
this.ShowDialogBox();
//POPULATE FIELDS
this.GetAccountName(constId);
this.ShowProgress(true, 60, "loading...proposals");
this.GetProposlas(constId);
this.ShowProgress(true, 90, "loading...action types");
this.GetActionTypes();
this.ShowProgress(true, 100, "loading...complete");
this.ConstituentID = constId;
$("#" + this.CONSTITUENTID_ID + ":input").val("");
$("#" + this.CONSTITUENTID_ID + ":input").val(constId);
//HIDE
$('#' + this.TPLDATEFIELD_ID).datepicker("hide");
$('#' + this.CRDATEFIELD_ID).datepicker("hide");
//TOGGLE FORM
if (formType != "") {
ToggleForms(formType);
}
//HIDE PROGRESS BAR
this.ShowProgress(false, 0, "");
}
Thanks,
T
You could chain the different stages together with window.setTimout(). This gives the browser a little time to redraw the page. In this example I set the timeout to 1ms. I am not shure if that will do the trick. Note that I start the chain at the end of the outer function.
ActionReportForms.prototype.LoadFormData = function (constId, formType) {
//HOOK UP DATE PICKER
$('#' + this.TPLDATEFIELD_ID).datepicker();
$('#' + this.CRDATEFIELD_ID).datepicker();
//CLEAR FIELDS
this.ClearFormFields();
//HIDE ERRORS
var hideErrors = function()
{
this.ShowError(false, "");
window.setTimeout(showProgessBar, 1);
};
//SHOW PROGRESS BAR
var showProgressBar = function()
{
this.ShowProgress(true, 30, "loading...");
this.ShowDialogBox();
window.setTimeout(populateFields, 1);
};
//POPULATE FIELDS
var populateFields = function()
{
this.GetAccountName(constId);
this.ShowProgress(true, 60, "loading...proposals");
window.setTimeout(loadProposals, 1);
};
var loadProposals = function()
{
this.GetProposlas(constId);
this.ShowProgress(true, 90, "loading...action types");
window.setTimeout(loadActionTypes, 1);
};
var loadActionTypes = function()
{
this.GetActionTypes();
this.ShowProgress(true, 100, "loading...complete");
this.ConstituentID = constId;
$("#" + this.CONSTITUENTID_ID + ":input").val("");
$("#" + this.CONSTITUENTID_ID + ":input").val(constId);
//HIDE
$('#' + this.TPLDATEFIELD_ID).datepicker("hide");
$('#' + this.CRDATEFIELD_ID).datepicker("hide");
//TOGGLE FORM
if (formType != "") {
ToggleForms(formType);
}
//HIDE PROGRESS BAR
this.ShowProgress(false, 0, "");
};
// Start with hide errors.
hideErrors();
};
Chaining via callbacks. Its all about the callbacks. I've been avoiding them because they're ugly and hard to read, but they allow you update the DOM while the rest of your code runs, IN IE. This is un
function LoadFormCall(constId, formType) {
//HOOK UP DATE PICKER
$('#' + this.TPLDATEFIELD_ID).datepicker();
$('#' + this.CRDATEFIELD_ID).datepicker();
arfObj.hiddenStuff1 = constId;
arfObj.hiddenStuff2 = formType;
//CLEAR FIELDS
arfObj.ClearFormFields();
//HIDE ERRORS
arfObj.ShowError(false, "");
//SHOW PROGRESS BAR
arfObj.ShowProgress(true, 30, "loading...");
//OPEN DIALOG
ShowDialogBox();
//POPULATE FIELDS
//THIS CALLS A JQUERY $.ajax call with a callback execute in the success function
//AND SO BEGINS THE CHAIN.
arfObj.GetAccountName1(constId, LoadFormCall2);
};
function LoadFormCall2() {
constId = arfObj.hiddenStuff1;
arfObj.ShowProgress(true, 60, "loading...proposals");
arfObj.GetProposals1(constId, LoadFormCall3);
};
function LoadFormCall3() {
arfObj.ShowProgress(true, 90, "loading...action types");
arfObj.GetActionTypes1(LoadFormCall4);
}
function LoadFormCall4(){
arfObj.ShowProgress(true, 100, "loading...complete");
arfObj.ConstituentID = constId;
$("#" + arfObj.CONSTITUENTID_ID + ":input").val("");
$("#" + arfObj.CONSTITUENTID_ID + ":input").val(constId);
//HIDE
$('#' + arfObj.TPLDATEFIELD_ID).datepicker("hide");
$('#' + arfObj.CRDATEFIELD_ID).datepicker("hide");
//TOGGLE FORM
if (arfObj.hiddenStuff2 != "") {
ToggleForms(arfObj.hiddenStuff2);
}
//HIDE PROGRESS BAR
arfObj.ShowProgress(false, 0, "");
}
This only appears to work in Firefox because it fakes synchronous XMLHttpRequest using asynchronous XMLHttpRequest. And while it's waiting for the request to complete, it updates the screen, because it hasn't got anything better to do.
But really, you should stop using synchronous XMLHttpRequest.

setInterval with other jQuery events - Too many recursions

I'm trying to build a Javascript listener for a small page that uses AJAX to load content based on the anchor in the URL. Looking online, I found and modified a script that uses setInterval() to do this and so far it works fine. However, I have other jQuery elements in the $(document).ready() for special effects for the menus and content. If I use setInterval() no other jQuery effects work. I finagled a way to get it work by including the jQuery effects in the loop for setInterval() like so:
$(document).ready(function() {
var pageScripts = function() {
pageEffects();
pageURL();
}
window.setInterval(pageScripts, 500);
});
var currentAnchor = null;
function pageEffects() {
// Popup Menus
$(".bannerMenu").hover(function() {
$(this).find("ul.bannerSubmenu").slideDown(300).show;
}, function() {
$(this).find("ul.bannerSubmenu").slideUp(400);
});
$(".panel").hover(function() {
$(this).find(".panelContent").fadeIn(200);
}, function() {
$(this).find(".panelContent").fadeOut(300);
});
// REL Links Control
$("a[rel='_blank']").click(function() {
this.target = "_blank";
});
$("a[rel='share']").click(function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
}
function pageURL() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
$.get("loader.php", query, function(data) {
$("#load").fadeIn("fast");
$("#content").fadeOut(100).html(data).fadeIn(500);
$("#load").fadeOut("fast");
});
}
}
This works fine for a while but after a few minutes of the page being loaded, it drags to a near stop in IE and Firefox. I checked the FF Error Console and it comes back with an error "Too many Recursions." Chrome seems to not care and the page continues to run more or less normally despite the amount of time it's been open.
It would seem to me that the pageEffects() call is causing the issue with the recursion, however, any attempts to move it out of the loop breaks them and they cease to work as soon as setInterval makes it first loop.
Any help on this would be greatly appreciated!
I am guessing that the pageEffects need added to the pageURL content.
At the very least this should be more efficient and prevent duplicate handlers
$(document).ready(function() {
pageEffects($('body'));
(function(){
pageURL();
window.setTimeout(arguments.callee, 500);
})();
});
var currentAnchor = null;
function pageEffects(parent) {
// Popup Menus
parent.find(".bannerMenu").each(function() {
$(this).unbind('mouseenter mouseleave');
var proxy = {
subMenu: $(this).find("ul.bannerSubmenu"),
handlerIn: function() {
this.subMenu.slideDown(300).show();
},
handlerOut: function() {
this.subMenu.slideUp(400).hide();
}
};
$(this).hover(proxy.handlerIn, proxy.handlerOut);
});
parent.find(".panel").each(function() {
$(this).unbind('mouseenter mouseleave');
var proxy = {
content: panel.find(".panelContent"),
handlerIn: function() {
this.content.fadeIn(200).show();
},
handlerOut: function() {
this.content.slideUp(400).hide();
}
};
$(this).hover(proxy.handlerIn, proxy.handlerOut);
});
// REL Links Control
parent.find("a[rel='_blank']").each(function() {
$(this).target = "_blank";
});
parent.find("a[rel='share']").click(function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
}
function pageURL() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
var content = $("#content");
$.get("loader.php", query, function(data) {
$("#load").fadeIn("fast");
content.fadeOut(100).html(data).fadeIn(500);
$("#load").fadeOut("fast");
});
pageEffects(content);
}
}
Thanks for the suggestions. I tried a few of them and they still did not lead to the desirable effects. After some cautious testing, I found out what was happening. With jQuery (and presumably Javascript as a whole), whenever an AJAX callback is made, the elements brought in through the callback are not binded to what was originally binded in the document, they must be rebinded. You can either do this by recalling all the jQuery events on a successful callback or by using the .live() event in jQuery's library. I opted for .live() and it works like a charm now and no more recursive errors :D.
$(document).ready(function() {
// Popup Menus
$(".bannerMenu").live("hover", function(event) {
if (event.type == "mouseover") {
$(this).find("ul.bannerSubmenu").slideDown(300);
} else {
$(this).find("ul.bannerSubmenu").slideUp(400);
}
});
// Rollover Content
$(".panel").live("hover", function(event) {
if (event.type == "mouseover") {
$(this).find(".panelContent").fadeIn(200);
} else {
$(this).find(".panelContent").fadeOut(300);
}
});
// HREF Events
$("a[rel='_blank']").live("click", function(event) {
var target = $(this).attr("href");
window.open(target, "_blank");
event.preventDefault();
});
$("a[rel='share']").live("click", function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
setInterval("checkAnchor()", 500);
});
var currentAnchor = null;
function checkAnchor() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
$.get("loader.php", query, function(data) {
$("#load").fadeIn(200);
$("#content").fadeOut(200).html(data).fadeIn(200);
$("#load").fadeOut(200);
});
}
}
Anywho, the page works as intended even in IE (which I rarely check for compatibility). Hopefully, some other newb will learn from my mistakes :p.

Categories