I've got a table with dozens of rows (up to 100), and each row has 1 element with a mouseover event attached to it. On mouseover, I open a tooltip and need to make an AJAX request to fill it with some data.
My problem is fairly simple: if the user moves his mouse up and down over all the elements that have that event attached, I'm firing tons of requests at a time. I wanna throttle it one way or another, but I'm unsure how to.
I'm gonna have to check whether that same event has been executed in the last n seconds, but how do I keep a link between the firing of event 1 and firing of event 2?
You need a flag in the start of your AJAX call, if the flag is ZERO, return from the function. How you scope the varialbe is up to you.
if(AJAXOK==0) {
return;
}
AJAXOK = 1;
Set the variable in the AJAX call, so that is resets itself after a timeout:
window.setInterval(function() { AJAXOK = 1 },5000);
AJAXOK = 0;
If you're interested in trying a different approach, reactive extensions are designed for exactly this sort of thing:
http://reactive-extensions.github.com/RxJS
Related
(This is a jQuery/JS question)
I have +/- buttons for product quantity on an eCommerce site.
A click on those, changes the value of a number-input field and then triggers change event for this number-input field (with quantityInput.change()), which in turn triggers an ajax to update the cart.
I want to give a chance to the user to click the buttons several times before triggering the ajax.
I now use setTimeout to delay the ajax which made the user experience a little bit better, but this is only a half solution as eventually the ajax is triggered as many times as the buttons had been clicked - and this is redundant.
I'm looking for a way to trigger the ajax only on the "last" click - for example, the click which has no subsequent click for 3 seconds.
For example, if the current quantity for a product "Apple" is 1, I want to allow the user to make 3 fast clicks to change it to 4, and only then trigger an ajax to update the cart.
Again, the flow:
Click on +/- buttons ->> number-input value is updated ->> a change event is triggered (with quantityInput.change()) ->> an ajax is called.
Thanks,
Asaf
You're almost there - on every click, also use clearTimeout to clear the existing timeout, if there is one, to ensure that the timeout callback runs only once there have been no clicks for 3 seconds. For example:
let timeout;
quantityInput.on('change', () => {
clearTimeout(timeout);
timeout = setTimeout(someFnThatUsesAjax, 3000);
});
(make sure timeout is persistent and outside the handler)
I have come accros this problem before. This time it is an HTML5 color input giving the problem. Here's the issue. I want to use an onChange event to send the user selected value from a color input to the server via Ajax - for an almost real time update to a database. However, when the user drags his mouse over the color swatch, it will trigger loads of onChange events in rapid succession, for each value change, so the server will get inundated with hundreds of pointless requests until the user stops sliding their mouse!
I could use onMouseOut for this particular issue, but I have had this issue with other things like detecting browser window resizing when a user click/drags to resize and the event is triggered with every pixel change. So I'm looking for an answer that will trigger on the final value only, for all similar scenarios.
What is the best method for dealing with this and just triggering when the data has finished changing. I.e. ignore every change until user has settled on a final value. An onAfterChange method event would be nice, but I can't find one!
You can prevent this by debouncing your event handler function.
Every time a debounced function is called, it "pushes back" its execution by nms. This means that while triggered numerous times within t ms (eg: while the mouse is moving), it will only execute the handler after not being triggered for t ms (eg: when the mouse has stopped moving).
Edit:
Ok, the jquery link was a bit out-of-date.. a better example of how to debounce can be found in the Underscore.js source here.
How about this:
Set a timeout to send request to server, after the first onChange triggering, and change the first flag to false.
Every onChange is triggered, reset the timeout count.
After sending request, reset the first flag.
Edit:
This is the sample code:
$(document).ready(function(){
var myInterval = undefined;
var mytimeoutcount = NaN;
var i =0; // for debug
$("#myinput").keyup(function(){
console.log("event trigger"); // for debug
mytimeoutcount = 300;
if(myInterval == undefined){
myInterval = setInterval(function(){
mytimeoutcount -= 10;
if(mytimeoutcount <= 0){
// do request, for debug
i+=1;
console.log(i);
window.clearInterval(myInterval);
myInterval = undefined;
}
}, 10);
}
});
});
#myinput{
width:100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input id="myinput" type="text" value="test" />
I'm creating a web form using AngularJS. This form contains, among other things, three buttons that, when clicked, sort the list items in an unordered list in 3 different ways. I've observed some weird behavior during a lag spike, whereas clicking another of these 3 buttons while the first's method call is still in flight causes a race condition that messes up the unordered list. In order to prevent this, I'm trying to make each of these 3 buttons disabled during the period of time when any of their onClick functions are running.
To really push this fix to its limits, I have automated tests running that are designed to click between two of these buttons alternately and repeatedly. The idea is that, even with the test software clicking between the two buttons at super-human speed, the second button will not be clickable while the first button's method call is in flight.
I've tried several methods to do this - using the ng-disabled property on each button, tied to a scope-level variable that each method toggles to true and false at the beginning and end of their methods, manually disabling the buttons at the beginning and end of each function - and yet in each case, the test reports that the race condition is still being triggered.
Considering that I'm manually disabling these buttons at the very beginning of their method calls, it appears that even this isn't enough. Does anyone know the absolute earliest point where I can disable the button after it's been clicked?
Theorem:
The problem you are having is that the script that is running effectively queues the click. The browser window is unresponsive as long as the event handler is running, but the click is still recorded, and processed as soon as the handler is done,
So what you do is
In handler1:
Disable button2
Do all the processing
Enable button 2
Only after that, the click is processed (even though you clicked it during the event handling). Because button2 is enabled at that time, handler2 will also run.
Proof:
With a piece of HTML, you can check that:
<button id="test1" onclick="window.test1();">Test1</button>
<button id="test2" onclick="window.test2();">Test2</button>
And script:
window.test1 = function()
{
document.getElementById('test2').disabled = true;
for (var i=0; i<1000000000; i++);
document.getElementById('test2').disabled = false;
alert('Test1 done'); // Notice: Alert after enabling button 2.
}
window.test2 = function()
{
document.getElementById('test1').disabled = true;
for (var i=0; i<1000000000; i++);
alert('Test2 done'); // Notice: Alert before re-enabling button 1.
document.getElementById('test1').disabled = false;
}
Two buttons, each of them will execute a similar, time consuming script. If you click button 1 and then quickly button 2 while the loop is running, you will notice that both handlers are called, but also you'll see that the other button is not (visually) disabled during the loop, because the signal to disable the button is also processed later, just like the click event.
If you press button 2 first (in which the message box comes before enabling the other button), you will notice that button 1 is not disabled while the loop runs, but it is disabled before the message box shows, and will remain so until after the message box is closed.
http://jsfiddle.net/u334tz36/1/
So, I think there is nothing to solve. Actually the two event handlers don't run at the same time but after each other, so there probably isn't even a problem at all.
Solution:
If you still think this is anoying and you want to silently consume the clicks, you can do this using a timer. In the following code, the button is not enabled in the handler, but in a timeout that runs 1ms later. That way, once the event handler is done, the (responsive) window will process all the delayed messages. Button 2 will be displayed as disabled, the click is consumed (and discarded) by the disabled button, and 1ms afterwards, the button is enabled:
window.enableTest2 = function()
{
document.getElementById('test2').disabled = false;
}
window.test1 = function()
{
document.getElementById('test2').disabled = true;
for (var i=0; i<1000000000; i++);
setTimeout(window.enableTest2, 1);
}
http://jsfiddle.net/u334tz36/2/
Fun thing is, you just have to give the window the opportunity to prosess the messages that are sent when you click the button. To do this, you don't need seconds at all, 1 millisecond will be enough, and you could even do with less. All you need is to change the order of events: first process all updates that relate to disabling the button and then perform the event handler. Since setTimeout also is message based, it also relies on the same update mechanism (and can actually be delayed by running code. So basically what you do by setting a timeout, is to put the code in the timeout at the end of the queue, no matter how short the given delay is. It's not a matter of time, it's a matter of execution order.
So, that is a possible way to solve it, but like I said, I don't think there is a problem in the first place.
I have a function, that adjusts element width/height on a page. This function is triggered by a custom dimensionchange event.
I'm
$(window).trigger("dimensionchange")
whenever I'm loading content via AJAX or changing a page (I'm using jquery mobile).
My problem is on some pages, a bunch of Ajax requests get triggered (like a search page, which ajax loads criteria and intial results), so I'm ending up with several "dimensionchange" events, which all trigger my layout update function. This slows down the page considerably and is not necessary, because I only need to capture the last dimensionchange and then update the layout.
Question:
Is there a way to capture the last occurence of an event when the event fires a random amount of times? The only thing I could think of is to set a timeout on every event occurence and if there is no further event in ... 500ms... trigger the page update. But this seems pretty awkward, so I'm curious to know if there is a better way?
Thanks for help!
You want to use jQuery's Global Ajax Event Handlers ajaxStart() and ajaxStop() methods.
There is another recent post about Using AjaxStop and AjaxStart. The main thing you need to know is that you can be notified when the first ajax query begins, and when the last one ends. You could set a flag like this:
$(document).ready(function() {
var ajaxBusy = false;
$(document).ajaxStart( function() {
ajaxBusy = true;
}).ajaxStop( function() {
ajaxBusy = false;
});
});
jsFiddle
I use a customized drop-down menu which runs on jQuery events and animations.
The problem occurs when I activate the drop-down via mouseenter several times, which results in the menu sliding down then sliding up several times. I tried to fix it by adding .stop(true), which was successful, but it resulted in other problems like this.
I followed that advice(jsFiddle Here), but it causes more unattractive problems.
I need is a way to stop a function from firing redundantly, but still be able to stop a "slide down" immediately and then "slide up" if the user triggers .mouseleave.
I tangled with custom queues for a good 5 hours, with no success :(
Any ideas, advice, and criticism is welcome.
Basically it boils down to delaying the execution of the event handler.
var mouseoverTimer = null;
$('.elem').mouseover(function(){
clearTimeout(mouseoverTimer); //ignore previous trigger
mouseoverTimer = setTimeout(function(){ //wait to execute handler again
//execute actual handler here
}, 10);
});
If the same handler was called within the specified interval the pending execution is cancelled and queued again to execute 10ms later hoping that there's no subsequent trigger within that interval.