Jquery POST sending multiple variables - javascript

I've been trying to figure out this issue with my website for a while now--I have a bunch of "stars" a user can click on.
clicking on a star loads a file into a div with information regarding that star. It also loads a button for the players to click and "Take over" the planet. That all is working well and fine, however--I've recently discovered an issue that I'm not quite sure how to handle.
IF a player clicks on multiple stars before reloading the page for whatever reason--when the click to attack/whatever the star--it'll send multiple requests across the server. I at first thought this was something in my coding that was sending all information regarding all the stars, however I've come to realize that it's only the stars that the player has clicked on.
Now--Here is the code:
$.ajaxSetup ({
// Disable caching of AJAX responses
cache: false
});
function loadStatus()
{
$.ajax(
{
url:'world1.php', error: function () { }, dataType:'json',
success: function(data)
{
denial = false;
$('#credits').html(data.credits);
$('#fuelleft').html(data.fuel);
$('#energyleft').html(data.energy);
}
});
}
function currentStarMapURL(URL)
{
$('#starmap').load(URL, {},
function()
{
$('#loader').hide();
fullStarInformation(URL);
starInformation();
setInterval(function() { $('.unknown').effect("highlight",{color:"#800000"}, 1500)});
return false;
}
);
}
/*
purhcase upgrades
*/
/*
Retriever Better Star Info
*/
function fullStarInformation()
{
$(".star").click(
function()
{
$('#planet-bar').empty();
val = this.id;
url = "planet.php?sid="+val;
$('#planet-bar').load(url, {'sid':val},
function()
{
colony(url);
}
);
}
);
}
function colony(url)
{
$('#planet-bar').on("click", "button",
function() {
event.preventDefault();
name = 0;
$(this).hide();
name = $(this).attr('sid');
$.post('purchase.php?mode=planet', {sid: name},
function ()
{
$('#planet-bar').load(url, {}, function () { currentStarMapURL(URL2); })
}
);
$(this).prop('disabled', true);
});
}
I figured at first that the issue was a caching issue, so I added the $.ajaxSetup to the first line, but that didn't seem to change anything.
Then I figured, maybe it's the way the code was being called--I originally had two seperate functions; one for attack, one for colonizing. both of which were being called in the fullStarInformation function, So I moved it all down to one function, i'm still getting the issue.
AFAIK, right now, i may have to rewrite this entire block of code so that the colony function and the starInformation function are separate and not acting upon one another. But I wanted to get a second, third maybe even fourth set of eyes on the code before I go about doing that.

If you are getting multiple ajax calls, chances are you are setting up multiple event handlers.
Just quickly glancing through the code, I would think you should change
function colony(url)
{
$('#planet-bar').on("click", "button", function() { ... } );
To
function colony(url)
{
$('#planet-bar').off("click", "button"); //unbind old event handlers
$('#planet-bar').on("click", "button", function() { ... } );

Related

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) { ... }
}

Knockout/JavaScript Ignore Multiclick

I'm having some problems with users clicking buttons multiple times and I want to suppress/ignore clicks while the first Ajax request does its thing. For example if a user wants add items to their shopping cart, they click the add button. If they click the add button multiple times, it throws a PK violation because its trying to insert duplicate items into a cart.
So there are some possible solutions mentioned here: Prevent a double click on a button with knockout.js
and here: How to prevent a double-click using jQuery?
However, I'm wondering if the approach below is another possible solution. Currently I use a transparent "Saving" div that covers the entire screen to try to prevent click throughs, but still some people manage to get a double click in. I'm assuming because they can click faster than the div can render. To combat this, I'm trying to put a lock on the Ajax call using a global variable.
The Button
<span style="SomeStyles">Add</span>
Knockout executes this script on button click
vmProductsIndex.AddItemToCart = function (item) {
if (!app.ajaxService.inCriticalSection()) {
app.ajaxService.criticalSection(true);
app.ajaxService.ajaxPostJson("#Url.Action("AddItemToCart", "Products")",
ko.mapping.toJSON(item),
function (result) {
ko.mapping.fromJS(result, vmProductsIndex.CartSummary);
item.InCart(true);
item.QuantityOriginal(item.Quantity());
},
function (result) {
$("#error-modal").modal();
},
vmProductsIndex.ModalErrors);
app.ajaxService.criticalSection(false);
}
}
That calls this script
(function (app) {
"use strict";
var criticalSectionInd = false;
app.ajaxService = (function () {
var ajaxPostJson = function (method, jsonIn, callback, errorCallback, errorArray) {
//Add the item to the cart
}
};
var inCriticalSection = function () {
if (criticalSectionInd)
return true;
else
return false;
};
var criticalSection = function (flag) {
criticalSectionInd = flag;
};
// returns the app.ajaxService object with these functions defined
return {
ajaxPostJson: ajaxPostJson,
ajaxGetJson: ajaxGetJson,
setAntiForgeryTokenData: setAntiForgeryTokenData,
inCriticalSection: inCriticalSection,
criticalSection: criticalSection
};
})();
}(app));
The problem is still I can spam click the button and get the primary key violation. I don't know if this approach is just flawed and Knockout isn't quick enough to update the button's visible binding before the first Ajax call finishes or if every time they click the button a new instance of the criticalSectionInd is created and not truely acting as a global variable.
If I'm going about it wrong I'll use the approaches mentioned in the other posts, its just this approach seems simpler to implement without having to refactor all of my buttons to use the jQuery One() feature.
You should set app.ajaxService.criticalSection(false); in the callback methods.
right now you are executing this line of code at the end of your if clause and not inside of the success or error callback, so it gets executed before your ajax call is finished.
vmProductsIndex.AddItemToCart = function (item) {
if (!app.ajaxService.inCriticalSection()) {
app.ajaxService.criticalSection(true);
app.ajaxService.ajaxPostJson("#Url.Action("AddItemToCart", "Products")",
ko.mapping.toJSON(item),
function (result) {
ko.mapping.fromJS(result, vmProductsIndex.CartSummary);
item.InCart(true);
item.QuantityOriginal(item.Quantity());
app.ajaxService.criticalSection(false);
},
function (result) {
$("#error-modal").modal();
app.ajaxService.criticalSection(false);
},
vmProductsIndex.ModalErrors);
}
}
you could use the "disable" binding from knockout to prevent the click binding of the anchor tag to be fired.
here is a little snippet for that. just set a flag to true when your action starts and set it to false again when execution is finished. in the meantime, the disable binding prevents the user from executing the click function.
function viewModel(){
var self = this;
self.disableAnchor = ko.observable(false);
self.randomList = ko.observableArray();
self.loading = ko.observable(false);
self.doWork = function(){
if(self.loading()) return;
self.loading(true);
setTimeout(function(){
self.randomList.push("Item " + (self.randomList().length + 1));
self.loading(false);
}, 1000);
}
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
Click me
<br />
<div data-bind="visible: loading">...Loading...</div>
<br />
<div data-bind="foreach: randomList">
<div data-bind="text: $data"></div>
</div>

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.

jquery Select2 prevent selecting in ajax response

I want to prevent from adding a category to the Select2 element if it fails creating the row first in my db. The action is not prevented when i call ev.preventDefault(); Nothing happens.. what is wrong?
$('#sel2').select2({
placeholder: 'Enter categories',
minimumInputLength: 3,
multiple: true,
ajax: {
url: 'async/get_categories.php',
dataType: 'json',
quietMillis: 250,
data: function (term, page) {
return {
q: term,
};
},
results: function (data, page) {
return {
results: data.items
};
},
cache: true
},
formatResult: format,
formatSelection: format
}).on('select2-selecting', function(e) {
console.log(e);
if (e.val == 4) {
// if category id equals 4
// do not add this category to select 2
// e.preventDefault();
// the above works just fine and its just for testing
}
// Is something wrong here?
var ev = e;
$.ajax({
type: 'POST',
url: 'async/create_profile_category.php',
data: {
profile_id: '1',
category_id: ev.val
},
success: function(response) {
console.log(response);
if (response.error === false) {
// category assigned successfully
} else {
// failed to assign category
// so i want now to prevent from adding to select2
console.log('should not add this category');
ev.preventDefault();
// the above is not working
}
},
error: function() {
alert('Failed to assign category!');
}
});
});
The AJAX request is made asynchronusly, so by the time it has finished the element has already been added. Even though you are calling ev.preventDefault(), it is too late for it to make a difference. So this leaves you with two options:
Make the request synchronusly, which will allow preventDefault to make the difference.
Make the request asynchronusly, and manually remove the element if it fails.
Both options have their pros and cons, and it's up to you to decide which option you go with.
Making the request synchronusly
Pros
The value will never be added if the request fails.
Works well in cases where the element cannot be added quite often.
Cons
Blocks the UI - So the user is potentially left with an unresponsive page while the request is made.
Making the request asynchronusly
Pros
Does not block the UI.
Works well in cases where elements typically can be added.
Cons
The value will always show up for the user, even if it fails later.
You must manually unset the new option.
What's important to consider here is the user experience of both options. When making synchronus requests, it's not uncommon for the browser to stop relaying events - which gives the illusion that the UI has locked up and the page has gone unresponsive. This has the benefit of ensuring that the value never shows up if it isn't allowed. But if users typically can add the elements, it also has the downside of complicating the most common use case.
If users can usually add elements, then it is a better experience to add the element while the request is being made, and then notifying the user later (while removing the element) if there was an issue. This is very common is web applications, and you can see it being used in many places, such as the Twitter and Facebook like buttons (where requests usually work), as well as places on Stack Overflow.
There is a way to get around this with version4 of the select2 library.
on select2:selecting we cancel the preTrigger event. Which will stop the select2:select event. We do our ajax call. On success we then get out Select2 instance then call the trigger of the Observer that way it by passes overwritten trigger method on your select2 instance.
The call method needs your select2 instance as the context so that the existing listeners are available to call.
var sel = $('#sel');
sel.select2(config);
sel.on('select2:selecting', onSelecting);
function onSelecting(event)
{
$.ajax({
type: 'POST',
url: 'async/create_profile_category.php',
data: {
profile_id: '1',
category_id: event.params.args.data.id
},
success: function(event, response) {
console.log(response);
if (response.error === false) {
// category assigned successfully
// get select2 instance
var Select2 = $users.data('select2');
// remove prevented flag
delete event.params.args.prevented;
// Call trigger on the observer with select2 instance as context
Select2.constructor.__super__.trigger.call(Select2, 'select', event.params.args);
} else {
// failed to assign category
// so i want now to prevent from adding to select2
console.log('should not add this category');
}
}.bind(null, event),
error: function() {
alert('Failed to assign category!');
}
});
event.preventDefault();
return false;
}
here how I did it for yii2 Select2 integrated into Gridview:
'pluginEvents' => [
'select2:selecting' => "
function(event)
{
var select2 = $('#types-" . $model->id . "');
select2.select2('close');
$.post('update',{id: " . $model->id . ", type_id: event.params.args.data.id})
.done (function(response)
{
select2.val(event.params.args.data.id);
select2.trigger('change');
})
.fail(function(response)
{
krajeeDialog.alert('Error on update:'+response.responseText);
});
event.preventDefault();
return false;
}",
],
it allows to asynchoronous update data in the grid using select2 and ajax and return it to previous value if there was an error on updating.

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