I have an ajax call where I used jQuery.ajax() to make a request to an mvc action. This all worked fine. However due to some forms having a file control I changed it from using jQuery.ajax() to using the XMLHttpRequest to send it using the HTML5 File API.
Since making this change the MVC action method no longer see's it as an ajax request. Using Fiddler2 I have noticed that it no longer adds the "X-Requested-With: XMLHttpRequest" to the request and I assume this is the problem.
The form I am trying to send does not have a file input in it, only normal textboxes etc, but I was trying to keep the method generic to deal with both. The following is the code I am using to send the ajax request:
// get the edit tender form
var $Form = $Button.closest('form');
var Url = $Form.attr('action');
var AjaxRequestObject = new XMLHttpRequest();
var FormDataToSend = new FormData();
$Form.find(':input').each(function () {
if ($(this).is('input[type="file"]')) {
var files = $(this)[0].files;
if (files.length > 0) {
FormDataToSend.append(this.name, files[0]);
}
} else {
FormDataToSend.append(this.name, $(this).val());
}
});
AjaxRequestObject.open('POST', Url, true);
AjaxRequestObject.onreadystatechange = function () {
if (AjaxRequestObject.readyState == 4) {
// handle response.
if (AjaxRequestObject.status == 200) {
if (!AjaxErrorExists(AjaxRequestObject.responseText, )) {
alert("success");
console.log(AjaxRequestObject.responseText);
}
else {
alert('failure');
}
}
else {
alert('failure');
}
}
};
AjaxRequestObject.send(FormDataToSend);
This code was provided following a problem I had which Darin Dimitrov provided the solution to, so I could send the file inputs by ajax.
Any ideas why this request would not send the header for an ajax call?
X-Requested-With is automatically added by jQuery. You can just as easily add it yourself with AjaxRequestObject.setRequestHeader(). Docs
I was having troubles with detecting if my request was ajax. So, maybe this sample will save someone a minute or two:
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', URL, true); // `true` for async call, `false` for sync.
// The header must be after `.open()`, but before `.send()`
xmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xmlhttp.onreadystatechange = function() {
// 4th state is the last:
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { ... }
};
xmlhttp.send();
Tested with Flask.
You can override natively all XMLHttpRequest.open method calls and add in it X-Requested-With header like:
(function () {
// #author https://github.com/stopsopa jfdsa78y453cq5hjfd7s877834h4h3
if (window.XMLHttpRequest.prototype.onOpen) {
return console.log('XMLHttpRequest.onOpen is already defined');
}
function over(method, on, off) {
var old = window.XMLHttpRequest.prototype[method];
if (!old.old) {
var stack = [];
window.XMLHttpRequest.prototype[on] = function (fn) {
if (typeof fn === 'function') {
stack.push(fn);
}
}
window.XMLHttpRequest.prototype[off] = function (fn) {
for (var i = 0, l = stack.length ; i < l ; i += 1 ) {
if (stack[i] === fn) {
stack.splice(i, 1);
break;
}
}
}
window.XMLHttpRequest.prototype[method] = function () {
var args = Array.prototype.slice.call(arguments);
var ret = old.apply(this, args);
for (var i = 0, l = stack.length ; i < l ; i += 1 ) {
stack[i].apply(this, args);
}
return ret;
}
window.XMLHttpRequest.prototype[method].old = old;
}
}
over('open', 'onOpen', 'offOpen')
XMLHttpRequest.prototype.onOpen(function () {
this.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
});
}());
Related
I asked about 5 month ago about rewriting my ajax call in pure Javascript. Here the original post: https://stackoverflow.com/questions/35415812/need-help-to-rewrite-my-jquery-ajax-call-to-plain-javascript
I never thought about to rewrite the script completely because it works but now i need to rewrite the whole script to plain js. I already startet.
Here is the jQUery/JS mix:
var cc = document.getElementsByClassName("cart-count");
var wc = document.getElementsByClassName("wishlist-count");
var url = wp_ajax.ajax_url;
var data = {
action: 'get_counts'
};
// JQUERY JS mixed VERSION
$.ajax({
type: 'POST',
url: url,
data: data,
success: function (data) {
var counts = JSON.parse(data);
console.log(data);
for(var i = 0; i < cc.length; i++){
cc[i].innerText=counts["cartCount"];
}
for(var i = 0; i < wc.length; i++){
wc[i].innerText=counts["wlCount"];
}
}
});
console says:
{"cartCount":"(1)","wlCount":"(3)"}
That's right!
But now i tried to rewrite the rest. Here the latest:
var cc = document.getElementsByClassName("cart-count");
var wc = document.getElementsByClassName("wishlist-count");
var url = wp_ajax.ajax_url;
var data = {
action: 'get_counts'
};
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status == 200) {
//document.getElementById("myDiv").innerHTML = xmlhttp.responseText;
var counts = data
console.log(data);
for(var i = 0; i < cc.length; i++){
cc[i].innerText=counts["cartCount"];
}
for(var i = 0; i < wc.length; i++){
wc[i].innerText=counts["wlCount"];
}
console.log('done');
} else if (xmlhttp.status == 400) {
console.log('There was an error 400');
} else {
console.log('something else other than 200 was returned');
}
}
};
xmlhttp.open('POST', url, true);
xmlhttp.send(data);
It does't work. The console gives me not the value, just the var:
Object {action: "get_counts"}
My question/problem: How can i get the data action values without the jQuery ajax? Please no questions like "why not jQuery?".
Thanks for all help!!! Sorry for my english.
UPDATE:
I got it!
jQuery:
var data = {
action: 'get_counts'
};
JS:
url + '?action=get_counts'
add this
var data = JSON.parse(xmlhttp.responseText);//you have to parse result
before this
var counts = data
console.log(data);
You are not evaluating the AJAX response data, but the local variable data which is set above the AJAX call:
var data = {
action: 'get_counts'
};
You need to parse the AJAX response instead:
if (xmlhttp.status == 200) {
console.log( JSON.parse(xmlhttp.response) )
}
See: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/response
Its happening because Ajax is async request which the browser handers in a different thread than the one which is processing your code. Normally jquery and other similar frameworks have callback methods defined for that but in pure JS implementation you can use
xmlhttp.responseText
to fetch the output once the request is done
I am trying to make several server requests inside a for loop. I found this question and implemented the suggested solution. However it doesn't seem to work.
for (var i = 1; i <= 10; i++)
{
(function(i) {
if(<some conditions>)
{
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp[i]=new XMLHttpRequest();
} else { // code for IE6, IE5
xmlhttp[i]=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp[i].onreadystatechange=function() {
if (xmlhttp[i].readyState==4 && xmlhttp[i].status==200) {
document.getElementById("preselection").innerHTML=xmlhttp[i].responseText;
}
}
xmlhttp[i].open("GET","getBuoys.php?q="+i,true);
xmlhttp[i].send();
}
})(i);
}
If I remove the for loop and change all xmlhttp[i] to xmlhttp, everything works just fine for one element, but I can't make several requests. Thanks in advance for any suggestions.
Try the snippet below
// JavaScript
window.onload = function(){
var f = (function(){
var xhr = [], i;
for(i = 0; i < 3; i++){ //for loop
(function(i){
xhr[i] = new XMLHttpRequest();
url = "closure.php?data=" + i;
xhr[i].open("GET", url, true);
xhr[i].onreadystatechange = function(){
if (xhr[i].readyState === 4 && xhr[i].status === 200){
console.log('Response from request ' + i + ' [ ' + xhr[i].responseText + ']');
}
};
xhr[i].send();
})(i);
}
})();
};
// PHP [closure.php]
echo "Hello Kitty -> " . $_GET["data"];
Response
Response from request 0 [ Hello Kitty -> 0]
Response from request 1 [ Hello Kitty -> 1]
Response from request 2 [ Hello Kitty -> 2]
First thing first, that's awful formatting. A small request to keep it a bit more parseable in future please.
We can clean this up though.
var XMLHttpRequest
= XMLHttpRequest || require('xmlhttprequest').XMLHttpRequest;
// Makes a request for 4 buoy page responses.
requestAllBuoys(4, function(requests) {
console.log('Got results!');
// Take out the responses, they are collected in the order they were
// requested.
responses = requests.map(function(request) {
return request.responseText;
});
// Left to you to implement- I don't know what you're going to do with
// your page!
updateDom(responses);
});
// Makes request to all buoy url's, calling the given callback once
// all have completed with an array of xmlRequests.
function requestAllBuoys (n, cb) {
var latch = makeLatch(n, cb);
makeBuoyURLTo(n).map(function (url, i) {
startXMLRequest('GET', url, latch.bind(undefined, i));
});
}
// Generates a latch function, that will execute the given callback
// only once the function it returns has been called n times.
function makeLatch (n, cb) {
var remaining = n,
results = [],
countDown;
countDown = function (i, result) {
results[i] = result;
if (--remaining == 0 && typeof cb == 'function') {
cb(results);
}
}
return countDown;
}
// Generates an array of buoy URL's from 1 to n.
function makeBuoyURLTo (n) {
var i, buoyUrls = [];
for (i = 1; i <= n; i++) {
buoyUrls.push('getBuoys.php?q=' + i);
}
return buoyUrls;
}
// Create and initiate an XMLRequest, with the given method to the given url.
// The optional callback will be called on successful completion.
function startXMLRequest (method, url, cb) {
var xmlRequest = createXMLRequest();
xmlRequest.onreadystatechange = function () {
if (isXMLFinished(xmlRequest)) {
if (cb && typeof cb == 'function') {
cb(xmlRequest, method, url);
}
}
}
xmlRequest.open(method, url, true);
xmlRequest.send();
return xmlRequest;
}
// Initiates an XMLRequest from either HTML5 native, or MS ActiveX depending
// on what is available.
function createXMLRequest () {
var xmlRequest;
if (XMLHttpRequest) {
xmlRequest = new XMLHttpRequest();
} else {
xmlRequest = new ActiveXObject('Microsoft.XMLHTTP');
}
return xmlRequest;
}
// Verifies that XMLRequest has finished, with a status 200 (OK).
function isXMLFinished (xmlRequest) {
return (xmlRequest.readyState == 4) && (xmlRequest.status == 200);
}
This may seem longer, but it makes things infinitely clearer, and the time you spent making it so is time you don't spend debugging.
It also allows you to access the final result together, in the order that they came as a standard array. This is the main added bulk.
I would say you have a good idea of what you're actually doing here, as to me the only thing about your code that wouldn't work is the updating of the dom (surely you'll just be assigning them rapidly all into the same element? replacing each other each time...).
Have a look at this answer about handling async callbacks if you're still struggling. But please, for your own sake, keep your code cleaner.
I need to do a sequential XMLHttpRequest requests (FIFO) to not to call the server with many requests a same time, I wrote this function that do the XMLHttpRequest sequentially:
var queue = [];
var xmlHttpCurrentlyOccuped = false;
function loadUserDetails() {
var url = "https://someurl.com";
doWebRequest("GET", url, "", parseUserDetails);
}
function parseUserDetails(dataFromServer){
Console.log("data received from server: "+JSON.stringify(dataFromServer));
}
function doWebRequest(method, url, params, callback) {
var parametres = new Object();
parametres.myMethod = method;
parametres.myUrl = url;
parametres.myParams = params;
parametres.myCallback = callback;
queue.push(parametres);
while (queue.length>0 && !xmlHttpCurrentlyOccuped){
var doc = new XMLHttpRequest();
doc.onreadystatechange = function() {
var status;
if (doc.readyState == XMLHttpRequest.LOADING || doc.readyState == XMLHttpRequest.OPENED){
xmlHttpCurrentlyOccuped = true;
}
if (doc.readyState == XMLHttpRequest.DONE && doc.status == 200) {
xmlHttpCurrentlyOccuped = false;
var data;
var contentType = doc.getResponseHeader("Content-Type");
data = doc.responseText;
queue[0].myCallback(data);
queue.shift();
}
else if (doc.readyState == XMLHttpRequest.DONE) {
xmlHttpCurrentlyOccuped = false;
status = doc.status;
if(status!=200) {
parseTheError(url);
}
queue.shift();
}
}
doc.open(queue[0].myMethod, queue[0].myUrl);
doc.send();
}
}
My problem is, after the XMLHttpRequest is done well, the callback function is not working in this line of my code queue[0].myCallback(data);I have this error: "queue[0].callback(data): undefined".
Any idea to resolve this issue?
Update:
I resolved the issue, this is my working code maybe it can help someone:
var queue = [];
function doWebRequest(method, url, params, callback) {
var parametres = new Object();
parametres.myMethod = method;
parametres.myUrl = url;
parametres.myParams = params;
parametres.myCallback = callback;
if (queue.length>0) {if (queue[queue.length-1].url != parametres.url) queue.push(parametres);}
else queue.push(parametres);
var doc = new XMLHttpRequest();
function processNextInQueue() {
if (queue.length>0){
var current = queue.shift();
doc.onreadystatechange = function() {
if (doc.readyState == XMLHttpRequest.DONE){
if(doc.status == 200) {
if(typeof current.myCallback == 'function'){
current.myCallback(doc.responseText)
} else {
console.log('Passed callback is not a function');
}
processNextInQueue();
}
else if(doc.status!=200) {
parseTheErrors(current.myUrl);
}
}
}
doc.open(current.myMethod, current.myUrl);
doc.send();
}
}
processNextInQueue();
}
Thank you guys for your help ;)
You can't poll in javascript with a while loop like this and expect proper performance. Javascript is single threaded so when you poll like this, you don't allow any cycles for other things to happen. You need to learn how to write asynchronous code where you start the first ajax call and then return. When that first one completes, you then start the second one and so on.
Here's a way to do this:
queue.push(parametres);
function processNextInQueue() {
if (queue.length) {
var doc = new XMLHttpRequest();
doc.onreadystatechange = function() {
if (doc.readyState == XMLHttpRequest.DONE) {
if (doc.status == 200) {
queue[0].myCallback(doc.responseText);
} else {
fonctionPourAnalyserLErreur(url);
}
// done now so remove this one from the queue
// and start the next one
queue.shift();
processNextInQueue();
}
}
doc.open(queue[0].myMethod, queue[0].myUrl);
doc.send();
}
}
processNextInQueue();
The idea is that you fire off the first ajax call and then you just return. When the readystatechange shows it is done, you process the results and then fire off the next one. All the while the ajax call is in process, the javascript engine is free to service other events and do other things (that's the key to handling an asynchronous operation like an ajax call).
In this line: queue[0].myCallback(data), for debugging purposes (and to prevent errors from breaking your site) I would change to the following:
var current = queue.shift();
if(typeof current.myCallback == 'function'){
current.myCallback(data)
} else {
// for now, log it
console.log('Passed callback is not a function');
}
Also, have you tried just passing an anonymous function to make sure it's not a scope/function hoisting issue?
function loadUserDetails() {
var url = "https://someurl.com";
doWebRequest("GET", url, "", function(dataFromServer){
console.log("data received from server: "+JSON.stringify(dataFromServer));
});
}
hello i have problem to parse xml..
i have xml like this :
<tejemahan>
<kategori> komputer </kategori>
<hasil> aplikasi komputer </hasil>
</terjemahan>
Edited:
xml above I get in that way :
var url="http://localhost:8080/inlinetrans/api/translate/"+userSelection+"/"+hasilStemSel+"/"+hasilStem;
var client = new XMLHttpRequest();
client.open("GET", url, false);
client.setRequestHeader("Content-Type", "text/plain");
client.send(null);
if(client.status == 200)
alert("the request success"+client.responseText);
else
alert("the request isn't success"+client.status+""+client.statusText)
}
and this is my code to parse an xml file above :
this.loadXML = function (){
var url = http://localhost:8080/coba/api/artikan/"+sel+"/"+hasilStemSel+"/"+hasilStem
xmlDoc=document.implementation.createDocument("","",null);
xmlDoc.load("url");
xmlDoc.onload= this.readXML;
}
this.readXML = function() {
alert(xmlDoc.documentElement.tagName);
alert(xmlDoc.documentElement.childNodes[0].tagName);
alert(xmlDoc.documentElement.childNodes[1].tagName);
alert(xmlDoc.documentElement.childNodes[0].textContent);
alert(xmlDoc.documentElement.childNodes[1].textContent);
}
i can execute this code
xmlDoc=document.implementation.createDocument("","",null);
xmlDoc.load("url");
but why i can't execute this code
xmlDoc.load = this.readXML ???
Try putting the onload handler assignment before the load() call. If you call load() first, the onload event will happen before you have assigned a handler to handle it. Like this:
xmlDoc=document.implementation.createDocument("","",null);
xmlDoc.onload= this.readXML;
xmlDoc.load("url");
Firstly, I second David Dorward's suggestion: use XMLHttpRequest instead, which will work in all major browsers. Code is below.
Secondly, your readXML function is flawed, since most browsers will include whitespace text nodes within the childNodes collection, so xmlDoc.documentElement.childNodes[0] will actually be a text node and have no tagName property. I would suggest using getElementsByTagName() or checking the nodeType property of each node as you iterate over childNodes.
Thirdly, your XML is not valid: the <tejemahan> and </terjemahan> do not match, although this may be a typo in your question.
var url = "http://localhost:8080/coba/api/artikan/"+sel+"/"+hasilStemSel+"/"+hasilStem;
var readXML = function(xmlDoc) {
alert(xmlDoc.documentElement.tagName);
var kategori = xmlDoc.getElementsByTagName("kategori")[0];
alert(kategori.tagName);
};
var createXmlHttpRequest = (function() {
var factories = [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); },
function() { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); },
function() { return new ActiveXObject("Microsoft.XMLHTTP"); }
];
for (var i = 0, len = factories.length; i < len; ++i) {
try {
if ( factories[i]() ) {
return factories[i];
}
}
catch (e) {}
}
})();
var xmlHttp = createXmlHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
readXML(xmlHttp.responseXML);
}
};
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
I am having an error when using new XMLHttpRequest() for the second time in JavaScript code called from textbox event on page.
My JavaScript finds suggestions for text entry from the SQL to do that I use xmlhttprequest, it does fine when it is the first time but when I keep typing in the text box I receive:
"typeerror: xmlhttprequest not a costructor"
(this error happens only in Firefox)
This is my code:
function fnNull() { };
function changeofstate(){
if (XMLHttpRequest.readyState == 4)
{
whatever ;
}
XMLHttpRequest.onreadystatechange = fnNull();
}
function whentextchange(){
var WebURL = "the url here ";
XMLHttpRequest = CreateXmlHttpObject(changeOfState);
XMLHttpRequest.open("GET", WebURL, true);
XMLHttpRequest.send(null);
XMLHttpRequestt.abort();
}
}
function CreateXmlHttpObject(handler) {
var objXmlHttpReq = null;
var Req = null;
if (navigator.userAgent.indexOf("Opera")>=0)
{
return ;
}
if (navigator.userAgent.indexOf("MSIE")>=0)
{
var strName="Msxml2.XMLHTTP";
if (navigator.appVersion.indexOf("MSIE 5.5")>=0)
{
strName="Microsoft.XMLHTTP";
}
try
{
objXmlHttpReq=new ActiveXObject(strName);
objXmlHttpReq.onreadystatechange = handler;
return objXmlHttpReq;
}
catch(e)
{
return ;
}
}
if (navigator.userAgent.indexOf("Mozilla") >= 0) {
try
{
if (Req == null) {
Req = new XMLHttpRequest();
}
Req.onload = handler;
Req.onerror = handler;
return Req;
}
catch (e) {
alert(e);
alert(Req.responseText)
alert(e);
return;
}
}
}
You should name your request object something else than XMLHttpRequest. It might override the XMLHttpRequest object in the browser. Thus giving you the error.
XMLHttpRequest = CreateXmlHttpObject(changeOfState);
Assigning XMLHttpRequest variable like this is actually using global scope. You should use var and another variable name
var req = CreateXmlHttpObject(changeOfState);
Hope this clarifies.