AJAX on Button Click runs incrementally - javascript

I've implemented a simple AJAX call that is bound to a button. On click, the call takes input from an and forwards the value to a FLASK server using getJSON. Using the supplied value (a URL), a request is sent to a website and the html of a website is sent back.
The issue is the AJAX call seems to run multiple times, incrementally depending on how many times it has been clicked.
example;
(click)
1
(click)
2
1
(click)
3
2
1
Because I am sending requests from a FLASK server to another website, it effectively looks like I'm trying to DDOS the server. Any idea how to fix this?
My AJAX code;
var requestNumber = 1; //done for testing purposes
//RUNS PROXY SCRIPT
$("#btnProxy").bind("click", function() . //#btnProxy is the button
{
$.getJSON("/background_process", //background_process is my FLASK route
{txtAddress: $('input[name="Address"]').val(), //Address is the input box
},
console.log(++requestNumber), //increment on function call
function(data)
{$("#web_iframe").attr('srcdoc', data.result); //the FLASK route retrieves the html of a webpage and returns it in an iframe srcdoc.
});
return false;
});
My FLASK code (Though it probably isn't the cause)
#app.route('/background_process')
def background_process():
address = None
try:
address = request.args.get("txtAddress")
resp = requests.get(address)
return jsonify(result=resp.text)
except Exception, e:
return(str(e))
Image of my tested output (I've suppressed the FLASK script)
https://snag.gy/bikCZj.jpg

One of the easiest things to do would be to disable the button after the first click and only enable it after the AJAX call is complete:
var btnProxy = $("#btnProxy");
//RUNS PROXY SCRIPT
btnProxy.bind("click", function () //#btnProxy is the button
{
btnProxy.attr('disabled', 'disabled');//disable the button before the request
$.getJSON("/background_process", //background_process is my FLASK route
{
txtAddress: $('input[name="Address"]').val(), //Address is the input box
},
function (data) {
$("#web_iframe").attr('srcdoc', data.result); //the FLASK route retrieves the html of a webpage and returns it in an iframe srcdoc.
btnProxy.attr('disabled', null);//enable button on success
});
return false;
});

You can try with preventDefault() and see if it fits your needs.
$("#btnProxy").bind("click", function(e) {
e.preventDefault();
$.getJSON("/background_process",
{txtAddress: $('input[name="Address"]').val(),
},
console.log(++requestNumber),
function(data)
{$("#web_iframe").attr('srcdoc', data.result);
});
return false;
});

Probably you are binding the click event multiple times.
$("#btnProxy").bind("click", function() { ... } );
Possible solutions alternatives:
a) Bind the click event only on document load:
$(function() {
$("#btnProxy").bind("click", function() { ... } );
});
b) Use setTimeout and clearTimeout to filter multiple calls:
var to=null;
$("#btnProxy").bind("click", function() {
if(to) clearTimeout(to);
to=setTimeout(function() { ... },500);
});
c) Clear other bindings before set your calls:
$("#btnProxy").off("click");
$("#btnProxy").bind("click", function() { ... } );

Related

Handling multithreadiing issue in javascript/jquery?

$(".getDetails").click(function() {
// some stuff like fetching response from server
})
when user clicks getDetails button on UI multiple times within fraction of second , jquery generates two calls for click function and my logic fails.
I think solution to this will be to disable the button on first click itself(so that use can't click multiple times). Once i get the response or just before returning
from click method i make it enable. Is there any better solution ?
If no, how can i make button disable as soon as user click button first time. I think it needs to be done before calling click method or some where in html element ?
Java provides synchronized keyword so that only one thread enters at time inside method , i am not sure is similar thing exist in javascript or not ?
Assuming the click handler executes an AJAX request you can set the button as disabled before making the request, then enable it again once the request completes. Try this:
$(".getDetails").click(function(){}
var $btn = $(this).prop('disabled', true);
$.ajax({
url: '/foo'
success: function() {
console.log('It worked!');
},
error: function() {
console.log('It failed!');
},
complete: function() {
$btn.prop('disabled', false);
}
});
});
you can try unbinding click event and after ajax call again bind click to that class
$(".getDetails").click(function(){}
$(".getDetails").unbind('click');
// some stuff like fetching response from server
)
You can use simple flag to prevent firing your logic multiple times:
var flag = true
$(".getDetails").click(function() {
if (flag) {
flag = false;
//your logic...
//when your code ends (in after-AJAX callback for example)
flag = true;
}
});
$(".getDetails").click(function(e){
var $target = $(e.currentTarget);
// assuming the click listener is on the button
$target.prop('disabled',true);
// request, stuff...and when done:
$target.prop('disabled',false);
})
try Prevent Default and return false to avoid any other event propagation
This is solution is like semaphore or monitor
var progress = false;
$(".getDetails").on('click', function(e) {
if(!progress){
progress = true;
// some stuff like fetching response from server
//also after sucessfull fetch make true to false again
}else{
console.log('something in progress');
}
e.preventDefault();
return false;
})
This should make sure that your button will not fire the async request twice, until you have a response.
function doAjaxReq() {
/*
Add your ajax operation here
as a return value of doAjaxReq
like so:
return $.ajax({
url: '/foo',
type: 'POST',
data: data
})
Since i can't use ajax here let's smilulate
it useing a promise.
*/
promise = new Promise(function(res, rej) {
setTimeout(function(){
res({foo: "bar"});
}, 1000)
})
return promise;
}
/*
Inside here you add the click handlder
only once use `elem.one('click'...`
*/
function addClickHandler(elem) {
elem.one('click', function() {
// do your ajax request and when its
// done run `addClickHanlder` again
// i'm using `.then` because of the promise,
// you should be using `.done`.
doAjaxReq().then(function(data) {
console.log(data);
addClickHandler(elem);
});
})
}
addClickHandler($(".getDetails"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="getDetails">Get Details</button>

ajaxComplete with getJSON causing loop

I am using ajaxComplete to run some functions after dynamic content is loaded to the DOM. I have two separate functions inside ajaxComplete which uses getJSON.
Running any of the functions once works fine
Running any of them a second time causes a loop cause they are using getJSON.
How do I get around this?
I'm attaching a small part of the code. If the user has voted, clicking the comments button will cause the comments box to open and close immediately.
$(document).ajaxComplete(function() {
// Lets user votes on a match
$('.btn-vote').click(function() {
......
$.getJSON(path + 'includes/ajax/update_votes.php', { id: gameID, vote: btnID }, function(data) {
......
});
});
// Connects a match with a disqus thread
$('.btn-comment').click(function() {
var parent = $(this).parents('.main-table-drop'), comments = parent.next(".main-table-comment");
if (comments.is(':hidden')) {
comments.fadeIn();
} else {
comments.fadeOut();
}
});
});
Solved the problem by checking the DOM loading ajax request URL
$(document).ajaxComplete(event,xhr,settings) {
var url = settings.url, checkAjax = 'list_matches';
if (url.indexOf(checkAjax) >= 0) { ... }
}

Limit ajaxStart function to only 1 of 2 ajax functions

I have a loading.gif that launches each time the user makes an AJAX powered search. However, I've got some search fields that automatically show suggestions while the user types, also powered by AJAX.
Now my loading.gif appears on the user search as well as the search suggestions while typing. How do I limit my function that shows the loading.gif to only show when it's a user AJAX search and not a search-suggestion-while-typing AJAX search?
This is my function:
$(document).ajaxStart(function () {
$(".se-pre-con").fadeIn("fast");
}).ajaxStop(function () {
$(".se-pre-con").fadeOut("fast");
});
how about bind it with condition like if user is still on the search input then dont show the loading.gif else if the user is out of the search input or first contact on the search input then show the loading.gif (refer below)
first the global variable
var input_focus = false;
and then when the specified input is on focus
$("#specified_input").focus(function(){
//set the variable named 'input_focus' to true to reject the showing of the loader (loading.gif) or hide it.
input_focus = true;
}).blur(function(){
//when the specified input lose it focus then set the variable 'input_focus' to false so that the loader (loading.gif) is allowed to show
input_focus = false;
});
$.ajax({
url : 'my-url',
type : 'post',
data : {},
beforeSend : function(){
//check if input is on focus
if(input_focus !== true){
//show the loading.gif, assume that #loader
$("#loader").show();
}else{
//hide the loading.gif, assume that #loader
$("#loader").hide();
}
},
complete : function(){
//when the ajax request is complete
},
success : function(response){
//the response function
}
});
I'd tackle it by either of the following:
1) Add a global variable such as showLoadingAnimation and set it to true or false depending on the need. Within your ajaxStart and ajaxStop do the following:
$(document).ajaxStart(function () {
if (showLoadingAnimation) $(".se-pre-con").fadeIn("fast");
}).ajaxStop(function () {
if (showLoadingAnimation) $(".se-pre-con").fadeOut("fast");
});
2) Instead of changing the jQuery global settings, Wrap the jQuery method with your own method:
//only listen to ajaxStop event so that we can hide the animation
$(document).ajaxStop(function () {
$(".se-pre-con").fadeOut("fast");
});
function myAjax(params, showAnimation) {
if (showAnimation) $(".se-pre-con").fadeIn("fast");
$.ajax(params);
}
//in your code you instead of calling $.ajax({...}) simply use `myAjax({...})`
Hope this helps.

Error Loading AJAX on Document Ready Using JQuery

I'm trying to create a PHP page that periodically updates values of several elements on the page. I'm using a host that limits my hits per day, and each hit to any page they're hosting for me counts against my total. Therefore, I'm trying to use JQuery/AJAX to load all of the information that I need from other pages at one time.
I'm calling the following index.php. This method achieves the desired affect exactly the way I want it, but results in three hits (dating.php, dgperc.php, and pkperc.php) every two seconds:
var focused = true;
$(window).blur(function() {
focused = false;
});
$(window).focus(function() {
focused = true;
});
function loadData() {
if (focused) {
var php = ["dating", "dgperc", "pkperc"];
$.each(php, function(index, value) {
$('#'+this).load(this+'.php');
});
}
}
$(document).ready(function() {
loadData();
});
setInterval(function() {
loadData();
}, 2000);
I'm calling the following index1.php. This is where I'm at as far as a method that only results in one hit every two seconds. My workaround is that I have combined the three php pages that I was loading into one, dating1.php. I load this into a div element, #cache, all at once. This element is set to hidden using CSS, and then I just copy its inner HTML into the appropriate elements:
var focused = true;
$(window).blur(function() {
focused = false;
});
$(window).focus(function() {
focused = true;
});
function loadData() {
if (focused) {
var php = ["dating", "dgperc", "pkperc"];
$('#cache').load('dating1.php');
$.each(php, function(index, value) {
$('#'+this+'1').html($('#'+this).html());
});
}
}
$(document).ready(function() {
loadData();
});
setInterval(function() {
loadData();
}, 2000);
Dating1.php will produce different outputs every time it's run, but here is an example of the output:
<span id = "dating">4 years, 7 months, 3 weeks, 10 seconds ago.</span>
<span id = "dgperc">21.9229663059</span>
<span id = "pkperc">22.2121099923</span>
On document ready, index1.php does not function properly: the #cache element isn't filled at all, so the other elements don't get filled either. However, after two seconds, the loadData() function runs again, and then the #cache element is filled correctly, and so are the other elements. For some reason, this isn't a problem on my index.php page at all, and I'm not sure why there's a difference here.
How can I get #cache to load the first time so that the page loads correctly? Or is there a better way to do this?
Each AJAX call is basically a page visit in the background. Like telling your assistant three different times to get you one coffee. Or telling them one to get you three coffees.
If you don't want to combine your three PHP pages into one - thus keeping code separate and easier to maintain. Consider creating one "cache.php" script and inside it:
cache.php:
$outputData = array('dating' => false, 'dgperc' => false, 'pkperc' => false);
foreach($outputData as $file => &$data)
{
//buffer output
ob_start();
//run first script (be smart and file_exists() first)
include_once($file . '.php');
$data = ob_get_clean();
}
//output JSON-compliant for easy jQuery consumption
echo json_encode($outputData);
Then in your javascript:
function loadData() {
if (focused) {
//call ajax with json and fill your spans
$.ajax({
async: true,
cache: false,
dataType: 'json',
success: function(data, textStatus, jqxhr) {
$('#dating').html(data.dating);
$('#dgperc').html(data.dgperc);
$('#pkperc').html(data.dgperc);
// NOTE... do a console.dir(data) to get the correct notation for your returned data
},
url: 'cache.php'
});
}
You are calling cache.php once every two seconds, saving on the 3-hits of calling the php files individually. Using a middle-man file you keep your scripts separate for maintainability.

automatic clicks on links and doing something with every page's DOM

i have some links in a web page ,what i want to do :
Trigger click event on every link
When the page of every link is loaded , do something with page's DOM(fillProducts here)
What i have tried :
function start(){
$('.category a').each(function(i){
$.when($(this).trigger('click')).done(function() {
fillProducts() ;
});
})
}
Thanks
What you want to do is much more complicated than you seem to be giving it credit for. If you could scrape webpages, including AJAX content, in 7 lines of js in the console of a web browser you'd put Google out of business.
I'm guessing at what you want a bit, but I think you want to look at using a headless browser, e.g. PhantomJs. You'll then be able to scrape the target pages and write the results to a JSON file (other formats exist) and use that to fillProducts - whatever that does.
Also, are you stealing data from someone else's website? Cause that isn't cool.
Here's a solution that may work for you if they are sending their ajax requests using jQuery. If they aren't you're going to need to get devilishly hacky to accomplish what you're asking (eg overriding the XMLHttpRequest object and creating a global observer queue for ajax requests). As you haven't specified how they're sending the ajax request I hope this approach works for you.
$.ajaxSetup({
complete: function(jQXHR) {
if(interested)
//do your work
}
});
The code below will click a link, wait for the ajax request to be sent and be completed, run you fillProducts function and then click the next link. Adapting it to run all the clicks wouldn't be difficult
function start(){
var links = $('.category a');
var i = 0;
var done = function() {
$.ajaxSetup({
complete: $.noop//remove your handler
});
}
var clickNext = function() {
$(links.get(i++)).click();//click current link then increment i
}
$.ajaxSetup({
complete: function(jQXHR) {
if(i < links.length) {
fillProducts();
clickNext();
} else {
done();
}
}
});
clickNext();
}
If this doesn't work for you try hooking into the other jqXHR events before hacking up the site too much.
Edit here's a more reliable method in case they override the complete setting
(function() {
var $ajax = $.ajax;
var $observer = $({});
//observer pattern from addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjquery
var obs = window.ajaxObserver = {
subscribe: function() {
$observer.on.apply($observer, arguments);
},
unsubscribe: function() {
$observer.off.apply($observer, arguments);
},
once: function() {
$observer.one.apply($observer, arguments);
},
publish: function() {
$observer.trigger.apply($observer, arguments);
}
};
$.ajax = function() {
var $promise = $ajax.apply(null, arguments);
obs.publish("start", $promise);
return $promise;
};
})();
Now you can hook into $.ajax calls via
ajaxObserver.on("start", function($xhr) {//whenever a $.ajax call is started
$xhr.done(function(data) {
//do stuff
})
});
So you can adapt the other snippet like
function start(){
var links = $('.category a');
var i = 0;
var clickNextLink = function() {
ajaxObserver.one("start", function($xhr) {
$xhr.done(function(data) {
if(i < links.length) {
fillProducts();
clickNextLink();
} else {
done();
}
});
})
$(links.get(i++)).click();//click current link then increment i
}
clickNextLink();
}
try this:
function start(){
$('.category a').each(function(i){
$(this).click();
fillProducts() ;
})
}
I get ya now. This is like say:
when facebook loads, I want to remove the adverts by targeting specific class, and then alter the view that i actually see.
https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/
Is a plugin for firefox, this will allow you to create a javascript file, will then allow you to target a specific element or elements within the html rendered content.
IN order to catch the ajax request traffic, you just need to catcher that within your console.
I can not give you a tutorial on greasemonkey, but you can get the greasemonkey script for facebook, and use that as a guide.
http://mashable.com/2008/12/25/facebook-greasemonkey-scripts/
hope this is it

Categories

Resources