This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 6 years ago.
I try to write script to loop through object and return those which type is equal to custom data type set in HTML. Yet im unable to pass variable with array of objects to my for loop. Can you please tell me what am I doing wrong in this code? I receive:
Cannot read property 'length' of undefined
PS. It has to be done in raw JavaScript, no jQuery
var btn = document.getElementById("btn");
var przepisy;
function findData(data) {
var kuchnia = data.dataset.type;
var myRequest = new XMLHttpRequest();
myRequest.onreadystatechange = function() {
if (this.readyState === 4 && this.status == 200) {
przepisy = JSON.parse(myRequest.responseText);
}
};
myRequest.open('GET', 'js/przepisy.json');
myRequest.send();
for (i = 0; i < przepisy.length; i++) {
var results = "";
var obj = przepisy[i];
var type = przepisy.type;
if (type === kuchnia) {
results += obj.name;
document.write(results);
}
}
}
The issue is that you are making the call to your for loop before you get the data back, which is why the length is 0. You should just move the for loop into your response:
function findData(data) {
var kuchnia = data.dataset.type;
var myRequest = new XMLHttpRequest();
myRequest.onreadystatechange = function() {
if (this.readyState === 4 && this.status == 200) {
przepisy = JSON.parse(myRequest.responseText);
for(i = 0; i < Object.keys(przepisy).length; i++) {
var results = "";
var obj = przepisy[i];
var type = przepisy.type;
if(type === kuchnia) {
results += obj.name;
document.write(results);
}
}
}
};
myRequest.open('GET','js/przepisy.json');
myRequest.send();
}
Related
I was successfully able to make multiple XMLHttpRequest and this is my code:
var index = ["status", "net"]
for (var i = 0; i < index.length; i++) {
var url = "http://localhost:3000/api/" + index[i];
let http = new XMLHttpRequest();
http.open("GET", url);
http.onreadystatechange = function () {
if (http.readyState === XMLHttpRequest.DONE && http.status == 200) {
console.log(http.responseText);
var respond = JSON.parse(http.responseText);
console.log("respond is" + respond.name);
console.log("respond is" + respond.dhcp_on);
console.log("respond is" + respond.sn);
document.getElementById('device-name').value = respond.name;
}
}
http.send();
}
I am getting the proper value in the console but as I am trying to display the value in my input element with id (device-name) I get undefined.
FYI this is how my console log is looking like
{"name":"DEV 1","sn":"123456789","uptime":123}
respond isDEV 1
respond isundefined
respond is123456789
{"dhcp_on":true,"ip":"1.2.3.4","mask":"1.2.3.4","gw":"1.2.3.4","dns":"1.2.3.4"}
respond isundefined
respond istrue
respond isundefined
Could anyone explain why?
Thanks.
The problem is that you're writing to the same element repeatedly in a loop. The response to your first request has name:
{"name":"DEV 1","sn":"123456789","uptime":123}
but the response to your second response does not:
{"dhcp_on":true,"ip":"1.2.3.4","mask":"1.2.3.4","gw":"1.2.3.4","dns":"1.2.3.4"}
Since the loop does
document.getElementById('device-name').value = respond.name;
both times, you're overwriting the result of the first (which has the name) with the result of the second (which doesn't), exactly like this:
const results = [
{"name":"DEV 1","sn":"123456789","uptime":123},
{"dhcp_on":true,"ip":"1.2.3.4","mask":"1.2.3.4","gw":"1.2.3.4","dns":"1.2.3.4"}
];
// (Using your loop structure and variable names)
for (var i = 0; i < results.length; ++i) {
var respond = results[i];
document.getElementById("device-name").value = respond.name;
}
<input type="text" id="device-name">
You only want to set device-name when processing the first response, not the second. You can do that by checking i, or by checking whether name is in the response:
const results = [
{"name":"DEV 1","sn":"123456789","uptime":123},
{"dhcp_on":true,"ip":"1.2.3.4","mask":"1.2.3.4","gw":"1.2.3.4","dns":"1.2.3.4"}
];
// (Using your loop structure and variable names)
for (var i = 0; i < results.length; ++i) {
var respond = results[i];
if ("name" in respond) {
document.getElementById("device-name").value = respond.name;
}
}
<input type="text" id="device-name">
In this case, if ("name" in respond) { could be if (respond.hasOwnProperty("name")) { or if (respond.name !== undefined) {, whatever you prefer. in checks the whole prototype chain, but the chain is quite short when the object came from JSON.parse.
so by simply adding an if condition to check if the id i want to display is in my returned object or not as #T.J. Crowder explained it worked perfectly
var index = ["status", "net"];
for (var i = 0; i < index.length; i++) {
var url = "http://localhost:3000/api/" + index[i];
let http = new XMLHttpRequest();
http.open("GET", url);
http.onreadystatechange = function () {
if (http.readyState === XMLHttpRequest.DONE && http.status == 200) {
console.log(http.responseText);
var respond = JSON.parse(http.responseText);
if ("name" in respond) {
document.getElementById('device-name').value = respond.name;
}}
}
http.send();
}
When the button is clicked I would like my text to change every 3 secs. I am using setInterval I can see the numbers changing in console.log but the text isn't changing and i don't know why.
What I've tried:
setInterval(function() {
var quote = jokesArray[jokeNum];
console.log(jokeTxt.innerHTML = quote);
}
setInterval(function() {
if (jokeTxt.innerHTML !== "") {
jokeTxt.innerHTML = "";
jokeNum++;
jokeTxt.innerHTML = jokesArray[jokeNum];
}
setInterval(function() {
var quote = jokesArray[Math.floor(Math.random() % 586) + 1];
console.log(jokeTxt.innerHTML = quote);
Full Script:
function main() {
var myApi = "https://api.icndb.com/jokes/random";
var xhttp = new XMLHttpRequest();
var jokesArray = [];
var randomIndex = Math.random();
var randomizer = (randomIndex % 586) + 1;
var jokeNum = Math.floor(randomizer);
var jokeTxt = document.getElementById("jokeTxt");
xhttp.onreadystatechange = function () {
if (xhttp.readyState == XMLHttpRequest.DONE) {
var jokesJSON = JSON.parse(xhttp.responseText);
var jokes = jokesJSON.value.joke;
for (var i = 0; i < jokes.length; i++) {
jokesArray.push(jokes);
}
//allJokes = jokesArray[jokeNum];
setInterval(function() {
var quote = jokesArray[Math.floor(Math.random() % 586) + 1];
console.log(jokeTxt.innerHTML = quote);
},3000);
}
}
xhttp.open("GET", myApi, true);
xhttp.send();
}
var myBtn = document.getElementById("btn");
myBtn.onclick = main;
The API you're using only returns a single joke at a time, rather than an array of multiple jokes. In your code, you're treating var jokes as though it's an array of many jokes, but this is actually just a string - i.e., one joke. Iterating over this variable means you're looping over the string and performing an action for each character in the joke.
The API also returns a random joke from their database, so instead of building a local array and selecting a random element, you could just send multiple requests to the API and get a new (random) joke each time. Here's a modification of your code that does this:
var jokeTxt = document.getElementById("jokeTxt");
function getJoke() {
var myApi = "https://api.icndb.com/jokes/random";
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == XMLHttpRequest.DONE) {
var jokesJSON = JSON.parse(xhttp.responseText);
var joke = jokesJSON.value.joke;
jokeTxt.innerHTML = joke;
}
}
xhttp.open("GET", myApi, true);
xhttp.send();
}
function startJokes() {
// get the first joke right away...
getJoke();
// ...then get a new joke every 3 seconds
setInterval(function() {
getJoke();
}, 3000);
}
var myBtn = document.getElementById("btn");
myBtn.onclick = startJokes;
<button id="btn">Start the Jokes!</button>
<div id="jokeTxt"><div>
I am trying to create a dynamic AJAX search bar which communicates with my database. Here is my code.
function getXmlHttpRequestObject(){
if(window.XMLHttpRequest){
return new XMLHttpRequest();
}
else if (window.ActiveXObject){
return new ActiveXObject("Microsoft.XMLHTTP");
}
else{
alert("Your browser does not support our dynamic search");
}
}
var search = getXmlHttpRequestObject();
function ajaxSearch(){
if (search.readyState == 4 || search.readyState == 0){
var str = escape(document.getElementById('searchBox').value);
search.open("GET", 'searchSuggest.php?search=' + str, true);
search.onreadystatechange.handleSearchSuggest;
search.send(null);
}
}
function handleSearchSuggest(){
if(search.readyState == 4){
var ss = document.getElementById('ajaxSearch');
ss.innerHTML = '';
var str = search.responseText.split("\n");
for(i=0; i<str.length-1; i++){
var suggestion = '<div onmouseover="javascript:suggestOver(this);"';
suggestion += 'onmouseout="javascript.suggestOut(this);"';
suggestion += 'onclick="javascript:setSearch(this.innerHTML);"';
suggestion += 'class="suggestLink">' + str[i] + '<div>';
ss.innerHTML += suggestion;
}
}
}
function suggestOver(divValue){
divValue.className = "suggestLink";
}
function suggestOut(divValue){
divValue.className = "suggestLink";
}
function setSearch(x){
document.getElementById('searchBox').value = x;
document.getElementById('ajaxSearch').innerHTML = '';
}
The problem is that the readyState is changing from 0 to 1, but then it will not change to any other state. I need it to change to 4 to enter the function handleSearchSuggest().
I also get this error within the console:
TypeError: search.onreadystatechange is null
You need to set the callback function correctly.
search.onreadystatechange = handleSearchSuggest;
Note that a ready state of 1 means OPENED and 4 means DONE. You can test this via properties of the XMLHttpRequest class:
XMLHttpRequest.UNSENT == 0
XMLHttpRequest.OPENED == 1
XMLHttpRequest.HEADERS_RECEIVED == 2
XMLHttpRequest.LOADING == 3
XMLHttpRequest.DONE == 4
Try:
search.onreadystatechange = handleSearchSuggest;
I want to create a array containing objects, and I'm using Parse to query all the data.
However, the for loop which loops over the results doesn't does that in the correct order but randomly loops over the data. If I log i each iteration, the logs show different results every time.
Here is my code:
for (var i = 0; i < results.length; i++)
{
Parse.Cloud.useMasterKey();
// retrieve params
var objectid = results[i];
var self = request.params.userid;
// start query
var Payment = Parse.Object.extend("Payments");
var query = new Parse.Query(Payment);
query.get(objectid, {
success: function (payment) {
// get all the correct variables
var from_user_id = payment.get("from_user_id");
var to_user_id = payment.get("to_user_id");
var amount = payment.get("amount");
var createdAt = payment.updatedAt;
var note = payment.get("note");
var img = payment.get("photo");
var location = payment.get("location");
var status = payment.get("status");
var fromquery = new Parse.Query(Parse.User);
fromquery.get(from_user_id, {
success: function(userObject) {
var fromusername = userObject.get("name");
var currency = userObject.get("currency");
var toquery = new Parse.Query(Parse.User);
toquery.get(to_user_id, {
success: function(touser)
{
var tousername = touser.get("name");
if(tousername !== null || tousername !== "")
{
sendArray(tousername);
}
},
error: function(touser, error)
{
var tousername = to_user_id;
if(tousername !== null || tousername !== "")
{
sendArray(tousername);
}
}
});
function sendArray(tousername) {
var array = new Array();
// create the time and date
var day = createdAt.getDate();
var year = createdAt.getFullYear();
var month = createdAt.getMonth();
var hour = createdAt.getHours();
var minutes = createdAt.getMinutes();
// create the timestamp
var time = "" + hour + ":" + minutes;
var date = "" + day + " " + month + " " + year;
var associativeArray = {};
if(self == from_user_id)
{
fromusername = "self";
}
if(self == to_user_id)
{
tousername = "self";
}
associativeArray["from"] = fromusername;
associativeArray["to"] = tousername;
associativeArray["amount"] = amount;
associativeArray["currency"] = currency;
associativeArray["date"] = date;
associativeArray["time"] = time;
associativeArray["status"] = status;
if(note == "" || note == null)
{
associativeArray["note"] = null;
}
else
{
associativeArray["note"] = note;
}
if(img == "" || img == null)
{
associativeArray["img"] = null;
}
else
{
associativeArray["img"] = img;
}
if(location == "" || location == null)
{
associativeArray["location"] = null;
}
else
{
associativeArray["location"] = location;
}
array[i] = associativeArray;
if((i + 1) == results.length)
{
response.success(array);
}
},
error: function(userObject, error)
{
response.error(106);
}
});
},
error: function(payment, error) {
response.error(125);
}
});
}
But the i var is always set to seven, so the associative arrays are appended at array[7] instead of the correct i (like 1,2,3,4,5)
The reason that this is so important is because I want to order the payment chronologically (which I have done in the query providing the results).
What can I do to solve this issue?
Success is a callback that happens at a later point in time. So what happens is, the for loop runs 7 times and calls parse 7 times. Then after it has run each of parse success calls will be executed, they look at i which is now at 7.
A simple way to fix this is to wrap the whole thing in an immediate function and create a new closure for i. Something like this
for(var i = 0; i < results.length; i++){
function(iClosure) {
//rest of code goes here, replace i's with iClosure
}(i);
}
Now what will happen is that each success function will have access to it's own iClosure variable and they will be set to the value of i at the point they were created in the loop.
Hi I am using an ajax call to a function called build_Array. This function should break up myString which is "Call 1-877-968-7762 to initiate your leave.,1,0,through;You are eligible to receive 50% pay.,1,365,through;Your leave will be unpaid.,1,0,After;"
into sections divided by the commas into a 2d array. But it is not working. It says all of the values for the array are undefined. Here is where I call the function inside the ajax... (It works in the jsfiddle http://jsfiddle.net/ChaZz/3/)
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
var myString = request.responseText;
myString = build_Array(myString);
document.getElementById('ajax').innerHTML = myString;
}
}
And here is the function build_Array...
function build_Array (myString) {
var mySplitResult = myString.split(';');
var myArray = new Array(mySplitResult.length);
//may need to get rid of -1
for(var i = 0; i < mySplitResult.length -1; i++){
myArray[i] = new Array(4);
var mySplitResult2 = mySplitResult[i].split(',');
for(var z = 0; z < mySplitResult2.length; z++) {
myArray[i][z] = mySplitResult2[z];
}
}
var final_message = myArray[1][1];
return final_message;
}
http://jsfiddle.net/ChaZz/5/
var myString = "Call 1-877-968-7762 to initiate your leave.,-30,0,through;You are eligible to receive 50% pay.,0,365,through;Your leave will be unpaid.,365,0,After;";
function build_Array (myString) {
var mySplitResult = myString.split(';');
var myArray = [];
for(var i = 0; i < mySplitResult.length; i++){
myArray[i] = [];
var mySplitResult2 = mySplitResult[i].split(',');
for(var z = 0; z < mySplitResult2.length; z++) {
myArray[i][z] = mySplitResult2[z];
}
}
var final_message = myArray[1][1];
return final_message;
}
console.log(build_Array(myString)); // 0
There's no need for the loop to copy from mySplitArray2 to myArray, just assign the array returned by split directly to that element of the new array. And array.push can be used to build up an array incrementally.
function build_Array (myString) {
var myArray = [];
for (substring in myString.split(';')){
myArray.push(substring.split(','));
}
var final_message = myArray[1][1];
return final_message;
}