Async ajax call causing browser to freeze - javascript

I have two different sets of a and p elements in my html page which are made as display:none by default.
At the end of the of the page I'm calling a function by sending their ID's and some values to enable any one of them based on some conditions
1st set
<a style="display:none;" id="ClickMe1">Click Me</a>
<p class="button" id="Sorry1" style="display:none;">Sorry!</p>
2nd set
<a style="display:none;" id="ClickMe2">Click Me</a>
<p class="button" id="Sorry2" style="display:none;">Sorry!</p>
Function call
<script>
window.onload = function () {
Initialize("ClickMe1", "Sorry1", "23,35");
Initialize("ClickMe2", "Sorry2", "76,121");
};
</script>
Initialize function consists of a ID, p ID and set of values(it can contain n values) to check which element to enable
Javascript Function
function Initialize(ClickMeID, SorryID,Values) {
var valList = Values.split(',');
for (i = 0; i < valList.length; i++) {
var paramts = "{'val':'" + valList[i] + "'}";
jQuery.ajax({
type: "POST",
url: "/services/MyService.asmx/GetData",
data: paramts,
contentType: "application/json; charset=utf-8",
dataType: "json",
async: false,
success: function (response) {
Status = response.d.toString();
},
error: function (response) {
}
});
if (Status == "0") {
$('#' + SorryID).show();
return;
}
}
$('#' + (ClickMeID).show();
}
In my function I'm splitting the comma seperated Values and looping through each value and making an ajax call to my service with async:false.
The response of success call is either 1 or 0. If any of Values is 0 of a function call I want to display p element else a element of the sent ID's.
This function is working fine but when the function call is raised this is making the browser freeze until the execution of the function.
If I make async: true I'm not able to find out which set of buttons to enable and disable
How can I make prevent the browser from freezing.

You should set
async: true
If it's not async, then it'll be blocking.
Also, if you're looping through many items, you should wrap each iteration in a setTimeout and make it async too.
Code samples
function click(e){
var button = e.target;
$.ajax({
type: "POST",
url: "http://localhost/accounts/save",
data : {
accountID: 123,
name:"hello world"
},
beforeSend: function(){
//disable the button.
}
}).always(function(){
//enable the button
})
}
here's an example of of setTimeout
setTimeout(function(){
//do something
}, 3000); //3seconds
I would highly recommend, that you read up on jquery.Deferred and event loops.

I'm not able to find out which set of buttons to enable and disable
Then that is your real issue. You solved your problem with other code to cause a new problem.
I highly suggest reading Decoupling Your HTML, CSS, and JavaScript.
Here is what I would do (since you tagged jquery might as well.. actually fully use it).
<style>
.is-hidden{ display: none; }
</style>
<div class="js-init-content" data-params="[{'val':23},{'val':35}]">
<a class="is-hidden js-clickme">Click Me</a>
<p class="button is-hidden js-sorry">Sorry!</p>
</div>
<div class="js-init-content" data-params="[{'val':76},{'val':121}]">
<a class="is-hidden js-clickme">Click Me</a>
<p class="button is-hidden js-sorry">Sorry!</p>
</div>
<script>
// when the document is ready...
$(document).ready(function(){
// loop through each init-content item
$(".js-init-content").each(function(){
var $this = $(this);
// get the data from the html element
// jquery will return an array containing objects
// because it's smart and cool like that
var params = $this.data('params');
var isAvailable = true;
// loop through each param
$.each(params, function(index, param){
// stop loop and ajax calls if any previous ajax call failed
if (!isAvailable) return false;
// make an ajax call, param will be the object from the array
$.ajax({
type: "POST",
url: "/services/MyService.asmx/GetData",
data: param,
contentType: "application/json; charset=utf-8",
// dataType: "json", -- jquery is smart it will figure it out
// async: false, -- Almost no reason to ever do this
).done(function(response){
isAvailable = response.d.toString() != "0";
}); // End Ajax-Done
}); // End js-init-content.each
var selector = isAvailable
? ".js-clickme"
: ".js-sorry";
$this.find(selector).removeClass("is-hidden");
}); // End doc-ready
</script>
I encapsulated the data in the html, instead of hardcoding it in the javascript. Fully used jQuery for loading and updating.

Related

Binding table in MVC 4 after Ajax call

I have an HTML able, which I bind by using the following Action in MVC controller:
public ActionResult BindTable(int ? page)
{
int pageSize = 4;
int pageNumber = 0;
List<Users> _users = query.ToList();
return View(_users.ToPagedList(pageNumber, pageSize));
}
Below the table I have the following HTML:
<textarea class="form-control" style="resize:none;" rows="9" placeholder="Enter value here..." id="txtValue"></textarea>
<br />
<button style="float:right; width:100px;" type="button" onclick="CallFunction()" class="btn btn-primary">Update specific record</button>
The Javascript function responsible for calling the action is as following:
function CallFunction() {
if ($('#txtValue').val() !== '') {
$.ajax({
url: '/User/UpdateUser',
type: 'POST',
data: { txt: $('#txtValue').val() },
success: function (data) {
$('#txtValue').val('');
alert('User updated!');
},
error: function (error) {
alert('Error: ' + error);
}
});
}
And here is the Action responsible for updating the user:
public ActionResult UpdateUser(string txtValue)
{
var obj = db.Odsutnost.Find(Convert.ToInt32(1));
if(obj!=null)
{
obj.Text= txtValue;
obj.Changed = true;
db.SaveChanges();
return RedirectToAction("BindTable");
}
return RedirectToAction("BindTable");
}
Everything works fine. But the table doesn't updates once the changes have been made ( it doesn't binds ?? )...
Can someone help me with this ???
P.S. It binds if I refresh the website.. But I want it to bind without refreshing the website...
I created a BIND function with Javascript, but it still doesn't binds:
function Bind() {
$(document).ready(function () {
var serviceURL = '/User/BindTable';
$.ajax({
type: "GET",
url: serviceURL,
contentType: "application/json; charset=utf-8",
});
});
}
You're not actually updating the page after receiving the AJAX response. This is your success function:
function (data) {
$('#txtValue').val('');
alert('User updated!');
}
So you empty an input and show an alert, but nowhere do you modify the table in any way.
Given that the ActionResult being returned is a redirect, JavaScript is likely to quietly ignore that. If you return data, you can write JavaScript to update the HTML with the new data. Or if you return a partial view (or even a page from which you can select specific content) then you can replace the table with the updated content from the server.
But basically you have to do something to update the content on the page.
In response to your edit:
You create a function:
function Bind() {
//...
}
But you don't call it anywhere. Maybe you mean to call it in the success callback?:
function (data) {
$('#txtValue').val('');
Bind();
alert('User updated!');
}
Additionally, however, that function doesn't actually do anything. For starters, all it does is set a document ready handler:
$(document).ready(function () {
//...
});
But the document is already loaded. That ready event isn't going to fire again. So perhaps you meant to just run the code immediately instead of at that event?:
function Bind() {
var serviceURL = '/User/BindTable';
$.ajax({
type: "GET",
url: serviceURL,
contentType: "application/json; charset=utf-8",
});
}
But even then, you're still back to the original problem... You don't do anything with the response. This AJAX call doesn't even have a success callback, so nothing happens when it finishes. I guess you meant to add one?:
function Bind() {
var serviceURL = '/User/BindTable';
$.ajax({
type: "GET",
url: serviceURL,
contentType: "application/json; charset=utf-8",
success: function (data) {
// do something with the response here
}
});
}
What you do with the response is up to you. For example, if the response is a completely new HTML table then you can replace the existing one with the new one:
$('#someParentElement').html(data);
Though since you're not passing any data or doing anything more than a simple GET request, you might as well simplify the whole thing to just a call to .load(). Something like this:
$('#someParentElement').load('/User/BindTable');
(Basically just use this inside of your first success callback, so you don't need that whole Bind() function at all.)
That encapsulates the entire GET request of the second AJAX call you're making, as well as replaces the target element with the response from that request. (With the added benefit that if the request contains more markup than you want to use in that element, you can add jQuery selectors directly to the call to .load() to filter down to just what you want.)

.map find elements once loaded via AJAX

I have the following function getItemID, it looks for all ID's under a certain parent ID (#search-output). If the ID (#test) was loaded when the page loads, getItemID finds the ID with no issue.
However I am actually building these ID's (#test) in realtime via AJAX, so my function getItemID needs to be able to find these ID's (#test) that have loaded via AJAX.
I think the issue here is .map(function(){}) inside getItemID cannot find elements added to the DOM after the page is loaded. I need a way for this .map to find all elements regardless of when they were loaded.
This works: HTML:
<div id="search-output">
<div id="test"></div>
</div>
This does not work: HTML:
<div id="search-output">
</div>
JavaScript getItemID function:
function getItemID(){
var ID = $('#search-output [id]').map(function(){
return this.id;
}).get();
if(ID.length == 0){
return null;
}else{
return ID;
}
}
JavaScript AJAX that returns the HTML:
$.ajax({
url:'lib/search-server.php',
type:'POST',
data: {
search: "*"
},
dataType:'json',
success: function(data){
/* data[0] contains "<div id="test"></div>" */
$('#search-output').append(data[0]);
}
});
One possible problem here is, you are calling getItemID before the ajax request is completed, now search-output don't have any children so the method won't find any element ids to return.
So the solution is to call the getItemID method after the ajax call is completed and the dom is updated.
$.ajax({
url: 'lib/search-server.php',
type: 'POST',
data: {
search: "*"
},
dataType: 'json',
success: function (data) {
/* data[0] contains "<div id="test"></div>" */
$('#search-output').append(data[0]);
//now call the method
var ID = getItemID();
//do other stuff
}
});
Try utilizing deferred.done( doneCallbacks [, doneCallbacks ] )
A function, or array of functions, that are called when the Deferred
is resolved.
$.ajax({
url:'lib/search-server.php',
type:'POST',
data: {
search: "*"
},
dataType:'json'
})
.done([function(data){
/* data[0] contains <div id="test"></div> */
$('#search-output').append(data[0]);
}, getItemID]
});

Replace javascript onmouseover with jQuery only

I have a page that displays a dynamic amount of "orders" and I have a button to "view" and another button to "print". To display the specific OrderNumber I'm using a javascript function triggered by onmouseover and a jQuery ajax function to change the button text, make a database entry, and then view or print another page. The problem is the order is viewed or printed MULTIPLE times from onmouseover. How can use only jQuery and call the specfic OrderNumber? Here is the code I'm using now:
This code is repeated for each order:
<div class="console_orders_details">
<input type="button" value="View"
id="vieworder'.$row[orderid].'" onmouseover="vieworder('.$row[orderid].');">
</div>
Here is the function to view the order:
function vieworder(id){
$(function(){
$('#vieworder' + id).click(function(){
var orderid = id;
var dataString = 'orderid='+ orderid; //string passed to url
$.ajax
({
url: "includes/ajax/console-view.php", //url of php script
dataType: 'html', //json is return type from php script
data: dataString, //dataString is the string passed to the url
success: function(result)
{
window.open("print.php?view=1&orderid="+id+"");
$('#vieworder' + orderid + ':input[type="button"]').attr("value", "Viewed!").fadeIn(400);
}
});
})
});
}
I'm assuming I need to eliminate the "vieworder" function and use a pure jQuery function. However, I don't know how to send over the order "id", which is why I used javascript.
You can target all elements with an ID that starts with vieworder, and then store the row ID as a data attribute :
<div class="console_orders_details">
<input type="button" value="View" id="vieworder'.$row[orderid].'" data-id="'.$row[orderid].'">
</div>
JS
$(function(){
$('[id^="vieworder"]').on('click', function(){
var orderid = $(this).data('id'),
btn = $('input[type="button"]', this);
$.ajax({
url: "includes/ajax/console-view.php",
dataType: 'html',
data: {orderid : orderid}
}).done(function(result) {
window.open("print.php?view=1&orderid="+orderid+"");
btn.val("Viewed!").fadeIn(400);
});
});
});
Your onmouseover event is probably being fired many times, resulting in your problem. This might help to stop unwanted extra calls, by ignoring them unless the previous one has completed.
var activeRequests = {}; // global
function vieworder(id){
if (activeRequests[id]) { return; }
activeRequests[id] = true;
$(function(){
$('#vieworder' + id).click(function(){
var orderid = id;
var dataString = 'orderid='+ orderid; //string passed to url
$.ajax
({
url: "includes/ajax/console-view.php", //url of php script
dataType: 'html', //json is return type from php script
data: dataString, //dataString is the string passed to the url
success: function(result) {
delete activeRequests[id];
window.open("print.php?view=1&orderid="+id+"");
$('#vieworder' + orderid + ':input[type="button"]').attr("value", "Viewed!").fadeIn(400);
}
});
})
});
}
First, don't have a dynamic id that you have to parse, and don't have an event handler in your html:
<div class="console_orders_details">
<input type="button" value="View" class="vieworder" data-id="$row[orderid]">
</div>
Next, create an event handler for just what you want to do. .one() will set an event handler to fire only once:
$(document).ready(function (){
$(".console_orders_details").one("mouseover", ".vieworder" function(){
var dataString = "orderid=" + $(this).data("id");
$.ajax({
url: "includes/ajax/console-view.php", //url of php script
dataType: 'html', //json is return type from php script
data: dataString, //dataString is the string passed to the url
success: function(result) {
window.open("print.php?view=1&" + dataString);
$(this).val("Viewed!");
}
});
});
});
If you want this to work onclick, then just change the mouseover to click. Also, fadeIn doesn't work on values. Here is a fiddle that has the basics: http://jsfiddle.net/iGanja/EnK2M/1/

How to bring ajax search onkeyup with jquery

My Script to call ajax
<script language="javascript">
function search_func(value)
{
$.ajax({
type: "GET",
url: "sample.php",
data: {'search_keyword' : value},
dataType: "text",
success: function(msg){
//Receiving the result of search here
}
});
}
</script>
HTML
<input type="text" name="sample_search" id="sample_search" onkeyup="search_func(this.value);">
Question: while onkeyup I am using ajax to fetch the result. Once ajax result delay increases problem occurs for me.
For Example
While typing t keyword I receive ajax result and while typing te I receive ajax result
when ajax time delay between two keyup sometime makes a serious issue.
When I type te fastly. ajax search for t keyword come late, when compare to te. I don't know how to handle this type of cases.
Result
While typing te keyword fastly due to ajax delays. result for t keyword comes.
I believe I had explained up to reader knowledge.
You should check if the value has changed over time:
var searchRequest = null;
$(function () {
var minlength = 3;
$("#sample_search").keyup(function () {
var that = this,
value = $(this).val();
if (value.length >= minlength ) {
if (searchRequest != null)
searchRequest.abort();
searchRequest = $.ajax({
type: "GET",
url: "sample.php",
data: {
'search_keyword' : value
},
dataType: "text",
success: function(msg){
//we need to check if the value is the same
if (value==$(that).val()) {
//Receiving the result of search here
}
}
});
}
});
});
EDIT:
The searchRequest variable was added to prevent multiple unnecessary requests to the server.
Keep hold of the XMLHttpRequest object that $.ajax() returns and then on the next keyup, call .abort(). That should kill the previous ajax request and let you do the new one.
var req = null;
function search_func(value)
{
if (req != null) req.abort();
req = $.ajax({
type: "GET",
url: "sample.php",
data: {'search_keyword' : value},
dataType: "text",
success: function(msg){
//Receiving the result of search here
}
});
}
Try using the jQuery UI autocomplete. Saves you from many low-level coding.
First i will suggest that making a ajax call on every keyup is not good (and this why u run in this problem) .
Second if you want to use keyup then show a loading image after input box to show user its still loading (use loading image like you get on adding comment)
Couple of pointers. Firstly, language is a deprecated attribute of javascript. In HTML(5) you can leave the attribute off, or use type="text/javascript". Secondly, you are using jQuery so why do you have an inline function call when you can do that with jQuery too?
$(function(){
// Document is ready
$("#sample_search").keyup(function()
{
$.ajax({
type: "GET",
url: "sample.php",
data: {'search_keyword' : value},
dataType: "text",
success: function(msg)
{
//Receiving the result of search here
}
});
});
});
I would suggest leaving a little delay between the keyup event and calling an ajax function. What you could do is use setTimeout to check that the user has finished typing before then calling your ajax function.

jQuery script supposed to run async but works only sync? why?

I have this small jquery script that does not work if I remove the 'async:false' part... And I don't understand why (the alert() part is there just to check if it works or not). My guess was it would work asynchronously but it just doesn't. Can somebody explain to me why? And what should I change to make it async?
$(document).ready(function(){
var artistName = new Array();
var artistPlaycount = new Array();
$('#inputForm').submit(function(){
var userName = $('#username').attr('value');
var amount = $('#amount').attr('value');
userName = "someUsername";
$.ajax({
type: "POST",
url: "prepXML.php",
data: "method=getartists&user="+userName+"&amount="+amount,
dataType: "xml",
async:false,
success: function(xml){
var i = 0;
$("artist",xml).each(function(){
artistName[i] = $(this).find("name").text();
artistPlaycount[i] = $(this).find("playcount").text();
i++;
});
}
});
});
alert(artistName[2]); //or any other iteration number
});
thank you
To do this asynchronously you need to move the alert into the callback and remove the async option, like this:
$.ajax({
type: "POST",
url: "prepXML.php",
data: "method=getartists&user="+userName+"&amount="+amount,
dataType: "xml",
success: function(xml){
$("artist",xml).each(function(i){
artistName[i] = $(this).find("name").text();
artistPlaycount[i] = $(this).find("playcount").text();
});
alert(artistName[2]);
}
});
Otherwise that success function populating the array happens after the alert does...so what you want isn't quite there yet. Not until the request comes back from the server does the success handler execute.
Also, the first parameter to the .each() callback is the index, you can use it, no need to keep your own incrementing variable :)
It doesn't work because the callback is fired after the alert. Put the alert in the callback.
you need to move the alert into your success handler.
alert(artistName[2]); //or any other iteration number
should go right after you loop through the xml.
so you should have:
success: function(xml){
var i = 0;
$("artist",xml).each(function(){
artistName[i] = $(this).find("name").text();
artistPlaycount[i] = $(this).find("playcount").text();
i++;
});
alert(artistName[2]); //or any other iteration number
}

Categories