jQuery AJAX problems, intermittently getting duplicate requests - javascript

Right then, I'm coming to the end of a rewrite of our JavaScript system, we're moving from Prototype to jQuery. We have a load of AJAX requests that fire when a certain element events occur, the one in the example below is a new event on a calendar, but it happens elsewhere too.
The problem I'm getting is when an event is fired sometimes two AJAX requests are made. The first one returns the correct value but (as you can see) it still says processing, it never returns the success message my JavaScript requires. The second request returns the correct result AND terminates correctly.
The problem I have is our jQuery screen blocker is set up to prevent user input during heavy AJAX requests, obviously because there an AJAX request still live the screen never unlocks. If I were to refresh this screen all will work as desired.
Can anyone shed any light on why this behavior is occuring.
alt text http://archive.ilmv.co.uk/images/jquery-duplicate-ajax-request.png
EDIT
"requests that fire when a certain element events occur" - this is the key phrase, I think. Please provide some info on how you set your events. Maybe it fires twice because you have multiple handlers set? – Igor Zinov'yev
Ok, the thing is when I hit refresh the problem usually resolves itself, so not sure how that could be a handler issue, here's the event handler we typically use for a change of a select box.
$("#my_select","#context").change(function(){
// uz_ajax is a wrapper function, keeps most of the same functionality as $.ajax()
uz_ajax({
target:"#my_target",
data:{
module:'my_module',
controller:'my_controller',
action:'my_action',
id:$(this).val(),
ajax:''
}
});
});
The problem I have is I have no idea how to replicate the problem, so I do not know if the event is being fired multiple times or whether the AJAX is requesting twice.
EDIT 2
If you are reloading the elements that
are bound then the change events could
be triggered recursively... We really
need to see more code (what do you do
on success of the ajax call etc) –
redsquare
Thanks redsquare, upon a successful AJAX request I usually apply the response (usually HTML of <option>s) to the target. I never trigger a change from the element that fired the AJAX, but I sometimes trigger a change on the target element, to allow for cascading AJAX requests. If this were the problem, surely this would happen all the time? My wrapper function uz_ajax is below:
var ajax_count = 0;
var ajax_response = null;
function uz_ajax(options) {
// set default options
var defaults = {
async: true,
type: "GET",
data: {},
url: "/",
cache: false,
force_change: true,
highlight: true,
action: "normal",
dataType: "html",
selected: {
value: null,
disabled: false
}
};
// merge the passed options with the defaults
var options = $.extend(defaults, options);
// start the jQuery ajax method
$.ajax({
async: options.async,
type: options.type,
url: options.url,
data: options.data,
beforeSend: function() {
// we only want to block the screen on the first pass
if(++ajax_count==1) {
$.blockUI({message:'Loading data, please wait'});
}
},
success: function(responseText) {
if(options.target!==undefined) {
// if target isn't an array, make it one
if(!$.isArray(options.target)) {
options.target = new Array(options.target);
}
var targets = options.target;
for ( var i in targets ) {
console_info("uz_ajax() --> Applying contents to "+targets[i]);
switch(options.action) {
case "normal":
if($(targets[i]).is("input")) {
$(targets[i]).val(trim(responseText));
} else {
$(targets[i]).html(trim(responseText));
}
break;
case "selected":
// preserve the current target value (e.g. list of options), but
// set the selected value to the ajax response
console_warn("Changing selected value of "+targets[i]+" to '"+responseText+"'");
// trim the response so we don't get any smarty induced errors such as ' 7'
$(targets[i]).val(trim(responseText));
break;
}
// set selected value
if(options.selected.value!=null) {
$(targets[i]).val(options.selected.value);
}
// highlight the target
// we don't want to highlight the target if it's a hidden input, as this will force a .show(
if($(targets[i]).attr('type')!='hidden' && options.highlight===true) {
$(targets[i]).effect("highlight",{},2000);
}
// force the target to change
if(options.force_change===true) {
$(targets[i]).trigger("change");
}
/* rebind certain elements that do not use conventional events */
/* We probably need to get all of these rebinds in a single function */
createDatePickers(targets[i]);
}
} else {
ajax_response = responseText;
console_warn("uz_ajax -> no targets specified");
// Well... we have no element to target, we need to return the value instead
// of course if we return here we're going
// we probably also need to check the ajax count as this will be the last executed part before we return
}
},
complete: function () {
/* if all ajax requests have completed, unblock screen */
if(--ajax_count===0) {
$.unblockUI();
/* could use this callBack to return a value *dun dun duuuuun* */
if (options.ajaxComplete) {
options.ajaxComplete(ajax_response);
}
}
},
cache: options.cache,
dataType: options.dataType
});
}
another way to stop multiple ajax
requests is to heck jQuery.active
prior to the call. jQuery keeps an
internal count of 'live' ajax requests
using this property. – redsquare
I'll look into this.
EDIT 3
So this is the result of the $('element').data(), but I can't understand what it's trying to say, does this mean there are two binds to it. If so how do I find out what these binds are and why do they both not fire when the event is fired.
alt text http://archive.ilmv.co.uk/images/firebug-jquery-data-events.png
EDIT 4
Here's another screenshot of the problem, this time in a different place in the system. The green arrow is the element thats triggers the three ajax requests, not in firebug how there's six, and that they pair up and share timestamps down to the millisecond?
The top three are the ones that have not completed, the bottom three have return the correct result.
alt text http://archive.ilmv.co.uk/images/jquery-duplicate-ajax-request-v2.png
EDIT 5
The image below demonstrates what happens when a select box is changed, it fires three different ajax requests.
I've managed to do a bit more debugging and I can tell you the following:
The problem occurs regardless of how many ajax requests the initial element calls
The problem occurs regardless of what plugins are used
This event is definitely only being fired once
My uz_ajax wrapper function is only being fired once per target (see the firebug console, "Line 29: uz_ajax()".
I've added console.info on each of the jquery $.ajax() callbacks to see what's firing and when, notice how the only callback to fire is beforeSend().
alt text http://archive.ilmv.co.uk/images/jquery-duplicate-ajax-request-v3.png
EDIT 6
Right then, after #redsquare suggested via Twitter about using console.trace() to find out "where the second event is firing from" is, like I've always maintained I'm sure there isn't two events firing so I put the trace in the $.ajax() method, here's what happened:
alt text http://archive.ilmv.co.uk/images/jquery-duplicate-ajax-request-v4.png
As you can see I get the duplicate ajax request problem even though the $.ajax() method has only fired once, again the timestamps are identical. Have I come across a bug with jQuery?
EDIT 7
It happens on StackOverflow too!
alt text http://archive.ilmv.co.uk/images/jquery-duplicate-ajax-request-v5.png

Have you tried using a .click() instead? I have had issues with irregular activity using .change() in javascript. Give that a try.

Right, so I've come to a conclusion, I'm pretty sure that my problem is an issue with Firebug, as I've tested it the app on FF without Firebug turned on and it works as it should do.
This isn't the first time something JavaScript related has led me on a merry dance when the symptoms thrown by the problem don't offer any reasonable hint to the solution, but there we have it.
Massive thanks to #redsquare, who's helped massively both here, but via twitter and the jQuery IRC channel, many thanks dude!

Try to add return false to your onchange function.
This will stop jQuery from bubbling the event.
You should make sure that $("#my_select","#context").change(function(){ is only called once.

I have tried this:
$(selector).unbind().on(eventname,function(){
$.ajax({
//ajax code here
});
});
It worked.

Related

Prevent XUL notificationBox from closing when button is hit

I have a problem concerning the notificationBox. I create a notification using
appendNotification( label , value , image , priority , buttons, eventCallback )
and supply a button in the buttons argument.
Now, I want to prevent the notificationBox from closing when I hit the button. The XUL Documentation states that this can be done by throwing an error in the eventCallback function:
This callback can be used to prevent the notification box from closing on button click. In the callback function just throw an error. (For example: throw new Error('prevent nb close');)
This does not work for me, however, it works when I add the throw-statement to the callback function of the button itself.
Is this a bug in XUL or an inconsistency with the documentation?
Is there any harm done by adding it to the button's callback function?
In my opinion, this is an error in the documentation not a bug in the code. However, throwing an error in your button callback to prevent closure is not the best way to accomplish that goal.
Looking at the source code, there were clearly multiple discrepancies between the code and the documentation regarding how buttons work on a notification.
There is a specifically coded method of preventing the notification closing from within the button callback (return true from the callback).
Throwing an error in order to accomplish a normal functionality is usually a bad programming practice. Doing so also results in an error showing in the console every time your button is pressed. Having errors intentionally showing in the console under normal operation is bad. It also can result in your add-on not being approved in review.
As it was documented (not as operational), if you wanted to close when one button was pressed and not close when another was pressed, you would have to store in a global variable which button callback was last called and then choose based on that information if you wanted to prevent closure when your notificationBox callback was executed. That would be an inappropriately complex way to design operation of these notification buttons.
Given all that, I would say that intentionally throwing an error in order to prevent closure is not the "correct" way to do it. While, trowing an error to prevent closure doesn't cause any harm to the operation of the notification box, it does show the error in the console, which is bad.
The correct way to prevent the notification from closing from within the notification button callback is to return a True value from the callback.
While it is possible that the previously inaccurately documented way of doing this the way they intended to have it operate, it is not the way it actually works. Given
It is easier to update the documentation than it is to make changes to the code.
The code works in a way that is better than the documented method.
There were other inaccuracies in the documentation that would have prevented people from using functionality which was supposedly working (popups/menu buttons).
I have, therefore, updated the documentation to reflect what is actually in the source code and copied, with some modification, the code from this answer to an example there.
Here is some code I used to test this:
function testNotificationBoxWithButtons() {
//Create some common variables if they do not exist.
// This should work from any Firefox context.
// Depending on the context in which the function is being run,
// this could be simplified.
if (typeof window === "undefined") {
//If there is no window defined, get the most recent.
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
}
if (typeof gBrowser === "undefined") {
//If there is no gBrowser defined, get it
var gBrowser = window.gBrowser;
}
function testNotificationButton1Callback(theNotification, buttonInfo, eventTarget) {
window.alert("Button 1 pressed");
//Prevent notification from closing:
//throw new Error('prevent nb close');
return true;
};
function testNotificationButton2Callback(theNotification, buttonInfo, eventTarget) {
window.alert("Button 2 pressed");
//Do not prevent notification from closing:
};
function testNotificationCallback(reason) {
window.alert("Reason is: " + reason);
//Supposedly prevent notification from closing:
//throw new Error('prevent nb close');
// Does not work.
};
let notifyBox = gBrowser.getNotificationBox();
let buttons = [];
let button1 = {
isDefault: false,
accessKey: "1",
label: "Button 1",
callback: testNotificationButton1Callback,
type: "", // If a popup, then must be: "menu-button" or "menu".
popup: null
};
buttons.push(button1);
let button2 = {
isDefault: true,
accessKey: "2",
label: "Button 2",
callback: testNotificationButton2Callback,
type: "", // If a popup, then must be: "menu-button" or "menu".
popup: null
};
buttons.push(button2);
//appendNotification( label , value , image (URL) , priority , buttons, eventCallback )
notifyBox.appendNotification("My Notification text", "Test notification unique ID",
"chrome://browser/content/aboutRobots-icon.png",
notifyBox.PRIORITY_INFO_HIGH, buttons,
testNotificationCallback);
}

Which of these event handlers should work to trigger a custom function on window close?

Okay, I have the data pipeline working fine, If I map the event handler to an input click trigger it sends the data via Jquery/Ajax to the PHP processing file and that writes the data to the SQL db.
However, none of the methods I have tried to trigger the Ajax send on window/page close seem to be working. I do not want to return an alert box, however all of the examples I have found online seem to demonstrate only the use of the onbeforeunload, beforeunload, and unload events to display an alert box. They also say you can launch a custom event, but I have not found a reliable example of such an event.
What am I doing wrong guys? Here is the code. All of my attempted triggers are near the top commented out except for the latest, so you guys can see what I have already tried.
var formData;
$(document).ready(function() {
//$("#driver").click(function() {
//$('a[rel!=ext]').click(function() { window.onbeforeunload = null; });
//$('form').submit(function() { window.onbeforeunload = null; });
//window.onbeforeunload = function() {
//jQuery(window).bind("beforeunload",function() {
//$(window).unload(function() {
$(window).bind("beforeunload", function() {
var date=new Date();
var formData = $("#testform :input[id!='card-type'][id!='paymentSelection_0']"+
"[id!='ccSelectedRadio'][id!='card-number'][id!='card-exp-month'][id!='card-exp-year'][id!='card-cvv'][id!='billing-first-name']"+
"[id!='billing-last-name'][id!='billing-company'][id!='billing-address1'][id!='billing-address2'][id!='billing-city']"+
"[id!='billing-state'][id!='billing-zip'][id!='billing-phone'][id!='billing-country'][id!='useShippingRadio'][id!='useBillingRadio']"+
"[id!='ppSelectedRadio'][name!='miscDS.shopperEmailAddress_ymixval'][name!='miscDS.shopperEmailAddress_ymixlabel']"+
"[name!='miscDS.shopperEmailAddress_secname'][name!='paymentSelectionDS.paymentSelection_ROW0_paymentPPSelected']").serializeArray();
$.post("jquery/process.php",
{
mydata: formData,
orderSubTotal: orderSubTotal,
orderTotal: orderTotal,
numOfItems: numOfItems,
items: items,
ids: ids,
codes: codes,
qtys: qtys,
price: price,
orderTax: orderTax,
orderShipping: orderShipping,
appliedPromoIdList: appliedPromoIdList,
coupon: coupon,
storeId: storeId,
activeShipPromotionCount: activeShipPromotionCount,
itemImages: itemImages,
date: date
}
);
});
});
Any method of attachment you've shown here is fine, as far as attaching events goes. The problem isn't your attachment approach, per se, but rather that your function performs an asynchronous task which takes "too long" to finish before the situation changes.
When a page unloads, any pending AJAX requests are cancelled. Because ajax requests are asynchronous by default, your request won't have a chance to even connect to the server before the browser cancels it.
The only route, for this use case, is to use a synchronous request. This may cause an undesired effect, though: while your request is pending, the browser interface will appear and behave as though it is "locked".
That is why an alert WILL work, because it is by nature a synchronous, "blocking" situation. The alert box effectively stops the entire browser UI to wait for input.
See also: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests

Chrome/jQuery Uncaught RangeError: Maximum call stack size exceeded

I am getting the error "Uncaught RangeError: Maximum call stack size exceeded" on chrome. here is my jQuery function
$('td').click(function () {
if ($(this).context.id != null && $(this).context.id != '') {
foo($('#docId').val(), $(this).attr('id'));
}
return false;
});
Note that there are tens of thousands of cells in the page. However, I generally associate stack overflows with recursion and in this case as far as I can see there is none.
Does creating a lambda like this automatically generate a load of stuff on the stack? is there any way round it?
At the moment the only workaround I have is to generate the onclick events explicitly on each cell when rendering the HTML, which makes the HTML much larger.
As "there are tens of thousands of cells in the page" binding the click-event to every single cell will cause a terrible performance problem. There's a better way to do this, that is binding a click event to the body & then finding out if the cell element was the target of the click. Like this:
$('body').click(function(e){
var Elem = e.target;
if (Elem.nodeName=='td'){
//.... your business goes here....
// remember to replace $(this) with $(Elem)
}
})
This method will not only do your task with native "td" tag but also with later appended "td". I think you'll be interested in this article about event binding & delegate
Or you can simply use the ".on()" method of jQuery with the same effect:
$('body').on('click', 'td', function(){
...
});
You can also get this error when you have an infinite loop. Make sure that you don't have any unending, recursive self references.
Mine was more of a mistake, what happened was loop click(i guess) basically by clicking on the login the parent was also clicked which ended up causing Maximum call stack size exceeded.
$('.clickhere').click(function(){
$('.login').click();
});
<li class="clickhere">
login
</li>
This problem happened with me when I used jQUery Fancybox inside a website with many others jQuery plugins.
When I used the LightBox (site here) instead of Fancybox, the problem is gone.
U can use
$(document).on('click','p.class',function(e){
e.preventDefault();
//Code
});
I recently just ran into this issue as well. I had a very large table in the dialog div. It was >15,000 rows. When the .empty() was called on the dialog div, I was getting the error above.
I found a round-about solution where before I call cleaning the dialog box, I would remove every other row from the very large table, then call the .empty(). It seemed to have worked though. It seems that my old version of JQuery can't handle such large elements.
I was getting this error because of my mistake that I forgot to declare one of the variable which was passed in Ajax data.Only mode was declaredat first.
data: {
tmp_id: tmp_id,
mode: mode
}
Declared the tmp_id variable also and it worked fine.
let tmp_id=$("#tmp_id").val();
let mode=$("#mode").val();
$.ajax({
url: 'my-url',
method: 'post',
data: {
tmp_id: tmp_id,
mode: mode
}
dataType: 'json',
success: function(response) {
console.log(response);
}
});
}
In My case I was html element insted of value
|
|
var uname=$("#uname");<-
let data={
uid:uid,
.....
};
$.post("url",data,(responseText)=>{
..
}).fail((xhr)=>{
..
});
then I updated
|
|
var uname=$("#uname").val()<-

Adding an Event Handler with Mootools' .addEvent() fails mysteriously in a certain place

I'm having a rather mind-boggling problem with some JS I'm working on for a web app. Unfortunately, I can't post the full code for this, as it's part of a not-yet-released project. This issue has both me and some colleagues I asked stumped - from what I can tell, it should work.
Now, to the actual problem: a user enters some info into some form fields and clicks a "confirm" image, whereupon an AJAX request is sent back to the server. The server does some processing, then sends back a response with a status and some attached data. A status message is displayed in the modal dialogue window the user was using, and an icon with a link is displayed. Here's the "onComplete" Handler for the Mootools Request.JSON object, with some error condition handling removed:
onComplete: function(response) {
if (response) {
if (response.status == 0) {
// this means the request was successful
licenses = response.licenses;
updateControls();
licenseList();
// here I add the status message...
$("createform_result").innerHTML = "<img src=\'/media/com_supportarea/images/db_success.png\' /> License created. Download:<br /><br />";
// ...and the download "link"
if (response.tlsid) {
$("createform_result").innerHTML += "<img src=\'/media/com_supportarea/images/download_small.png\' /> <em>TLS</em>";
// this line is here for debugging only, to make sure this
// block of code is run (it is) and the element is found (it is)
$("newtlslic-"+response.tlsid).style.border = "1px solid red";
$("newtlslic-"+response.tlsid).addEvent("click", function(e) {
// I've stripped out all other code, also for debugging
e.stop();
});
}
}
}
}
The message and icon with link appears, the style is applied (red border appears) and no error message appears in either Firefox or Chrome. However, clicking the icon results in a # being appended to the URL (the e.stop()) does nothing). According to the EventBug plugin, no click event is attached to the link. It seems like .addEvent() simply does nothing here.
Now, and here's they prize question: why is this and how can I fix it? Help!
strings in javascript are immutable. when you do stuff like:
$("createform_result").innerHTML += "<img src=\'/media/com_supportarea/images/download_small.png\' /> <em>TLS</em>";
you are referencing the innerHTML as a string. what this does is, it fetches the property into a string, concatenates it to the other strings you pass on and then returns a new string in the end, which gets set as the innerHTML.
in doing so, you are OVERWRITING the contents of the element every time, for every iteration.
events attached to elements are not done by a generic ID handler - they rely on the element being in the DOM, then the element UID (an internal property mootools assigns to all passed elements) is being read and the event callback is added into the element storage behind that UID.
you can see this work by doing console.log(element.retrieve("events"));
if you rewrite the innerHTML, the inner element is re-created and gets a NEW UID, which means the callback now points to an empty pointer as the UID is the key in element storage.
I may be wrong about what you are doing here as I don't actually see the bit where you rewrite it again, but there probably is one in the code you stripped, especially if you are running a loop.
the best way to deal with this is something else - use Event Delegation.
it can allow you to add the click event to the parent element instead via some selector. this will work for ANY element added in any way at any time that matches.
eg.
// add this once, outside the loop
$("createform_result").addEvent("click:relay(a.editLink)", function(event, element) {
console.log(this === element);
console.log(this === event.target);
console.log(this.get("data-id"));
});
// then as you loop the results, just inject the els or use innerHTML or whatever...
new Element("a.editLink", {
html: '<img src=\'/media/com_supportarea/images/download_small.png\' /></a> <em>TLS</em>',
"data-id": response.tlsid
}).inject($("createform_result"));
Event delegation is now a part of mootools core in 1.4.0 or is in mootools-more in previous versions.
have fun!

Is JavaScript single threaded? If not, how do I get synchronized access to shared data?

I have a web page with DIVs with a mouseover handler that is intended to show a pop-up information bubble. I don't want more than one info bubble to be visible at a time. But when the user moves the mouse rapidly over two items, I sometimes get two bubbles. This should not happen, because the code for showing a pop-up cancels the previous pop-up.
If this were a multi-threaded system then the problem would be obvious: there are two threads trying to show a pop-up, and they both cancel existing pop-ups then pop up their own pop-ups. But I assumed JavaScript is always run single-threaded, which would prevent this. Am I wrong? Are event handlers running asynchronously, in which case I need synchronized access to shared data, or should I instead be looking for bugs in the library code for cancelling pop-ups?
Edited to add:
The library in question is SIMILE Timeline and its Ajax library;
The event handler does call SimileAjax.DOM.cancelEvent(domEvt), which I assume based on the name cancels the bubbling of events;
Just to make thing s more complicated, what I am actually doing is starting a timeout that if not cancelled by a moustout shows the pop-up, this being intended to prevent pop-ups flickering annoyingly but annoyingly having the reverse effect.
I'll have another poke at it and see if I can work out where I am going wrong. :-)
Yes, Javascript is single-threaded. Even with browsers like Google Chrome, there is one thread per tab.
Without knowing how you are trying to cancel one pop-up from another, it's hard to say what is the cause of your problem.
If your DIVs are nested within one another, you may have an event propagation issue.
I don't know the library you are using, but if you are only trying to display one tooltip of somesort at a time... use a flyweight object. Basically a flyweight is something that is made once and used over and over again. Think of a singleton class. So you call a class statically that when first invoked automatically creates an object of itself and stores it. One this happens every static all references the same object and because of this you don't get multiple tooltips or conflicts.
I use ExtJS and they do tooltips, and message boxes as both flyweight elements. I'm hoping that your frameworks had flyweight elements as well, otherwise you will just have to make your own singleton and call it.
It is single threaded in browsers. Event handlers are running asynchroniously in one thread, non blocking doesn't allways mean multithreaded. Is one of your divs a child of the other? Because events spread like bubbles in the dom tree from child to parent.
Similar to what pkaeding said, it's hard to guess the problem without seeing your markup and script; however, I'd venture to say that you're not properly stopping the event propagation and/or you're not properly hiding the existing element. I don't know if you're using a framework or not, but here's a possible solution using Prototype:
// maintain a reference to the active div bubble
this.oActiveDivBubble = null;
// event handler for the first div
$('exampleDiv1').observe('mouseover', function(evt) {
evt.stop();
if(this.oActiveDivBubble ) {
this.oActiveDivBubble .hide();
}
this.oActiveDivBubble = $('exampleDiv1Bubble');
this.oActiveDivBubble .show();
}.bind(this));
// event handler for the second div
$('exampleDiv2').observe('mouseover'), function(evt) {
evt.stop();
if(this.oActiveDivBubble) {
this.oActiveDivBubble.hide();
}
this.oActiveDivBubble = $('exampleDiv2Bubble');
this.oActiveDivBubble .show();
}.bind(this));
Of course, this could be generalized further by getting all of the elements with, say, the same class, iterating through them, and applying the same event handling function to each of them.
Either way, hopefully this helps.
FYI: As of Firefox 3 there is a change pretty much relevant to this discussion: execution threads causing synchronous XMLHttpRequest requests get detached (this is why the interface doesn't freeze there during synchronous requests) and the execution continues. Upon synchronous request completion, its thread continues as well. They won't be executed at the same time, however relying on the assumption that single thread stops while a synchronous procedure (request) happening is not applicable any more.
It could be that the display isn't refreshing fast enough. Depending on the JS library you are using, you might be able to put a tiny delay on the pop-up "show" effect.
Here's the working version, more or less. When creating items we attach a mouseover event:
var self = this;
SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mouseover", function (elt, domEvt, target) {
return self._onHover(labelElmtData.elmt, domEvt, evt);
});
This calls a function that sets a timeout (pre-existing timeouts for a different item is cancelled first):
MyPlan.EventPainter.prototype._onHover = function(target, domEvt, evt) {
... calculate x and y ...
domEvt.cancelBubble = true;
SimileAjax.DOM.cancelEvent(domEvt);
this._futureShowBubble(x, y, evt);
return false;
}
MyPlan.EventPainter.prototype._futureShowBubble = function (x, y, evt) {
if (this._futurePopup) {
if (evt.getID() == this._futurePopup.evt.getID()) {
return;
} else {
/* We had queued a different event's pop-up; this must now be cancelled. */
window.clearTimeout(this._futurePopup.timeoutID);
}
}
this._futurePopup = {
x: x,
y: y,
evt: evt
};
var self = this;
this._futurePopup.timeoutID = window.setTimeout(function () {
self._onTimeout();
}, this._popupTimeout);
}
This in turn shows the bubble if it fires before being cancelled:
MyPlan.EventPainter.prototype._onTimeout = function () {
this._showBubble(this._futurePopup.x, this._futurePopup.y, this._futurePopup.evt);
};
MyPlan.EventPainter.prototype._showBubble = function(x, y, evt) {
if (this._futurePopup) {
window.clearTimeout(this._futurePopup.timeoutID);
this._futurePopup = null;
}
...
SimileAjax.WindowManager.cancelPopups();
SimileAjax.Graphics.createBubbleForContentAndPoint(...);
};
This seems to work now I have set the timeout to 200 ms rather than 100 ms. Not sure why too short a timeout causes the multi-bubble thing to happen, but I guess queuing of window events or something might still be happening while the newly added elements are being laid out.

Categories