I am using the foreach loop to call a php page that sends back html of one gallery image.
The forEach Loop is triggered by the function galleryplayer. Gallery size can be anything from 1 to 99 photos max.
I want to use the function stopGalPlay to halt the foreach loop on the iteration it is currently on. However after a week of trying to solve this I cannot think how.
Reading about it, the consensus says that I should not be using a forEach loop. If so how would I rewrite the galleryPlayer function to use a different kind of loop and/or incorporate some mechanism to break the loop and execute the code in the stopGalPlay function.
I know the question of breaking a foreach loop has been answered a million times before but I cannot figure out how to incorporate a stop play into my image gallery.
Any help would be greatly appreciated.
Note that I want to use pure javascript without libraries like jquery or others.
var detailPageTabContentContainer = document.getElementById("detailPageTabContentContainer");
// Open the Modal
function openGalleryModal(galId, count, rowTotal) {
var galId = arguments[0];
var count = arguments[1];
var rowTotal = arguments[2];
const api_url = "includes/ajax/GalleryViewerXhr.php?galId=" + galId + "&&count=" + count + "&&rowTotal=" + rowTotal;
fetch(api_url, { method: 'get', credentials: 'same-origin' })
.then(response => response.text())
.then(html => {
detailPageTabContentContainer.innerHTML = html;
})
.catch((err) => console.log("Can’t access " + api_url + err));
} // End of function openGalleryModal
// Close the Modal
function closeGalleryModal() {
document.getElementById("galleryModal").style.display = "none";
document.getElementById("active").click();
} // End of function closeGalleryModal
function plusGallerySlides(galId, count, rowTotal, n) {
var galId = parseInt(arguments[0]);
var count = parseInt(arguments[1]);
var rowTotal = parseInt(arguments[2]);
var n = (arguments[3]);
var GalIdFragment = (Math.floor(galId / 100));
var GalIdFragmentString = GalIdFragment.toString();
var countString;
if (count + n > rowTotal) {newCount = 1}
if (count + n < 1) {newCount = rowTotal}
if (count + n == rowTotal) {newCount = rowTotal}
if ((count + n < rowTotal)&&(count + n != 0)) {newCount = count + n}
if (count.toString().length == 1) {countString = "0" + count}
else {countString = count}
if (newCount.toString().length == 1) {countString = "0" + newCount } else {countString = newCount};
countString = countString.toString();
newGalId = GalIdFragmentString + countString;
const api_url = "includes/ajax/GalleryViewerXhr.php?galId=" + newGalId + "&&count=" + newCount + "&&rowTotal=" + rowTotal;
fetch(api_url, { method: 'get', credentials: 'same-origin' })
.then(response => response.text())
.then(html => {
detailPageTabContentContainer.innerHTML = html;
})
.catch((err) => console.log("Can’t access " + api_url + err));
} // End of function plusGallerySlides
function stopGalPlay(galId, count, rowTotal) {
var galId = parseInt(arguments[0]);
var count = parseInt(arguments[1]);
var rowTotal = parseInt(arguments[2]);
console.log("gallery Id is " + galId + ". Count is " + count + ". Row total is " + rowTotal + ".");
const api_url = "includes/ajax/GalleryViewerXhr.php?galId=" + galId + "&&count=" + count + "&&rowTotal=" + rowTotal;
fetch(api_url, { method: 'get', credentials: 'same-origin' })
.then(response => response.text())
.then(html => {
detailPageTabContentContainer.innerHTML = html;
})
.catch((err) => console.log("Can’t access " + api_url + err));
}
function galleryplayer(galId, count, rowTotal) {
var galId = parseInt(arguments[0]);
var count = parseInt(arguments[1]);
var rowTotal = parseInt(arguments[2]);
var GalIdFragment = (Math.floor(galId / 100));
var GalIdFragmentString = GalIdFragment.toString();
var galIdArr = [];
for ( i = 1; i <= rowTotal ; i++ ) {
galIdArr.push(i < 10 ? (GalIdFragmentString + "0" + i.toString()) : GalIdFragmentString + i.toString())
};
var interval = 4950;
var promise = Promise.resolve();
galIdArr.forEach(function (ArrayGalId, i) {
promise = promise.then(function() {
const api_url = "includes/ajax/GalleryViewerXhr.php?galId=" + ArrayGalId + "&&count=" + (i+1) + "&&rowTotal=" + rowTotal;
fetch(api_url, { method: 'get', credentials: 'same-origin' })
.then(response => response.text())
.then(html => {
detailPageTabContentContainer.innerHTML = html;
document.getElementById("galNext").style.display = "none";
document.getElementById("galPrev").style.display = "none";
document.getElementById("galPlay").style.display = "none";
document.getElementById("galClose").style.display = "none";
document.getElementById("galPhoto").classList.add("Gallery-Player-Fade-in-Out");
})
return new Promise(function(resolve) {
setTimeout(resolve, interval);
if((i+1) === (galIdArr.length)) {
var lastGalId = (galIdArr[galIdArr.length -1]);
var galLength = galIdArr.length;
const api_url = "includes/ajax/GalleryViewerXhr.php?galId=" + lastGalId + "&&count=" + galLength + "&&rowTotal=" + rowTotal;
fetch(api_url, { method: 'get', credentials: 'same-origin' })
.then(response => response.text())
.then(html => {
detailPageTabContentContainer.innerHTML = html;
});
};
});
});
});
}// End of function galleryplayer
You could use a while loop. while this condition is false do this. Or you could put the forEach loop inside of a function and return when it is supposed to break.
Use [].some instead of [].forEach
wherever you want to break it simply return true; it will stop the execution for further iteration.
eg
[].forEach(v => {
...
break; // does not work;
....
})
[].some(v => { // some work same as foreach if you don't return true;
....
return true; // will stop the iteration.
....
});
Related
I am a javascript beginner, I have a multiple choice exam project where I want to get response data for each selected answer. I can do it by typing the code manually but I want to make the code efficient because the data can be more than 50 questions.
heres my best code .
var i;
for (i = 1; i <= <?= session()->get('participant')['jml_soal'] ?>; i++) {
window['radio' + i] = document.querySelectorAll("input[name='optionTrue" + i + "']");
window['rubahtombol' + i] = document.getElementById("buton" + i);
}
let findSe = () => {
let selected = document.querySelector("input[name='optionTrue1']:checked").value;
var soalId = document.getElementById("idSoal1").value;
var opsiPilih = document.getElementById("jawaban" + selected).textContent
document.getElementById("pilihan1").textContent = ". " + opsiPilih;
rubahtombol1.classList.remove("btn-secondary");
rubahtombol1.classList.add("btn-primary")
}
let findSe1 = () => {
let selected = document.querySelector("input[name='optionTrue2']:checked").value;
var soalId = document.getElementById("idSoal2").value;
var opsiPilih = document.getElementById("jawaban" + selected).textContent
document.getElementById("pilihan2").textContent = ". " + opsiPilih;
rubahtombol2.classList.remove("btn-secondary");
rubahtombol2.classList.add("btn-primary")
}
radio1.forEach(radioBtn => {
radioBtn.addEventListener("change", findSe1);
});
radio2.forEach(radioBtn1 => {
radioBtn1.addEventListener("change", findSe2);
});
findSe1();
findSe2();
i'm trying to do this but not working
var i;
for (i = 1; i <= <?= session()->get('participant')['jml_soal'] ?>; i++) {
window['radio' + i] = document.querySelectorAll("input[name='optionTrue" + i + "']");
window['rubahtombol' + i] = document.getElementById("buton" + i);
window['findSe' + i] = () => {
let selected = document.querySelector("input[name='optionTrue1']:checked").value;
var soalId = document.getElementById("idSoal1").value;
console.log(selected);
var opsiPilih = document.getElementById("jawaban" + selected).textContent
console.log("aku pilih:" + opsiPilih);
console.log("id saol:" + soalId);
document.getElementById("pilihan1").textContent = ". " + opsiPilih;
window['rubahtombol'+i.classList.remove("btn-secondary")];
window['rubahtombol'+i.classList.add("btn-primary")];
}
}
radio1.forEach(radioBtn => {
radioBtn.addEventListener("change", findSe1);
});
radio2.forEach(radioBtn1 => {
radioBtn1.addEventListener("change", findSe2);
});
findSe1();
findSe2();
what i imagine is, i want do that in one looping
Your second approach looks pretty close, but you need to make i local to the loop body. See JavaScript closure inside loops – simple practical example
But you can make it a little cleaner with OOP.
class Button {
contructor(i) {
this.index = i;
this.radio = document.querySelectorAll(`input[name='optionTrue${i}']`);
this.rumbahtombol = document.getElementById("buton" + i);
this.radio.addEventListener("change", this.findSe.bind(this));
}
findSe() {
let selected = document.querySelector(`input[name='optionTrue${this.index}']:checked`).value;
let soalId = document.getElementById(`idSoal${this.index}`).value;
let opsiPilih = document.getElementById("jawaban" + selected).textContent;
document.getElementById(`pilihan${this.index}`).textContent = ". " + opsiPilih;
this.rubahtombol.classList.remove("btn-secondary");
this.rubahtombol.classList.add("btn-primary")
}
}
for (let i = 1; i <= <?= session()->get('participant')['jml_soal'] ?>; i++) {
new Button(i);
}
i did a little change from the code made by #Barmar and it worked
class Button {
contructor(i) {
let radio = [];
this.index = i;
radio[i] = document.querySelectorAll(`input[name='optionTrue` + i + `']`);
radio[i].forEach(radioBtn => {
radioBtn.addEventListener("change", this.findSe.bind(this));
});
}
findSe() {
let rubahtombol = []
let selected = document.querySelector(`input[name='optionTrue${this.index}']:checked`).value;
let soalId = document.getElementById(`idSoal${this.index}`).value;
let opsiPilih = document.getElementById("jawaban" + selected).textContent;
document.getElementById(`pilihan${this.index}`).textContent = ". " + opsiPilih;
rubahtombol = document.getElementById(`buton${this.index}`);
rubahtombol.classList.remove("btn-secondary");
rubahtombol.classList.add("btn-primary")
}
}
for (let i = 1; i <= <?= session()->get('participant')['jml_soal'] ?>; i++) {
new Button(i).contructor(i);
}
I have a function that connect to a web service in SOAP. Unfortunately the web service only support a very limited connections. I have an array of items to search in the web service, if i do a for or a foreach loop, the 70% of cases complete with no error, but in the 30% the web service response a error. This occurs when the max connections is overflow. This happens because the loop is no waiting the response of the webservice and the loop cotinues creating a lot of connections.
Here's my code:
var promiseArray = [];
for (var i = 0; i < result.length; i++) {
let m = result[i].id
let xml = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">' +
'<soapenv:Header/>' +
'<soapenv:Body>' +
'<tem:EjecutarConsultaXML>' +
'<!--Optional:-->' +
'<tem:pvstrxmlParametros>' +
'<![CDATA[' +
'<Consulta><NombreConexion>USERNAME</NombreConexion>' +
'<IdConsulta>QUERY</IdConsulta>' +
'<Parametros>' +
'<doc>' + m + '</doc>' +
'</Parametros>' +
'</Consulta>' +
']]>' +
'</tem:pvstrxmlParametros>' +
'</tem:EjecutarConsultaXML>' +
'</soapenv:Body>' +
'</soapenv:Envelope>';
const options = {
explicitArray: true
};
promiseArray.push(new Promise(async(resolve, reject) => {
await axios.post(url, xml, {
headers: {
'Content-Type': 'text/xml;charset=UTF-8'
}
})
.then((data) => {
xml2js.parseString(data.data, options, (err, result) => {
var temp = (result['soap:Envelope']['soap:Body'][0]['EjecutarConsultaXMLResponse'][0]['EjecutarConsultaXMLResult'][0]['diffgr:diffgram'][0]['NewDataSet'][0]['Resultado'])
resolve({
doc: m,
state: temp[0].f430_ind_estado[0]
})
});
})
.catch((err) => {
console.log(err)
});
}))
}
res.send(await Promise.all(promiseArray))
There are several issues with your code within the call to promiseArray.push().
There is no need to create a new Promise() since axios already provides one
This is actually and antipattern
There is no need for async/await in that call for the same reason.
Mixing Promises and functions that use callbacks usually doesn't turn out too well
You have no error checking in your code if the XML parser fails
The option object is not required as explicitArray: true is the default
Changes:
Removed all the extra/uneeded Promise code
Replaced xml2js.parseString with xml2js.parseStringPromise
Changed resolve to return
Since you're simply console.log() the error, removed unecessary boilerplate
Everything else is OK as written. Please let me know if I've missed something.
promiseArray.push(
axios.post(url, xml, {
headers: {
'Content-Type': 'text/xml;charset=UTF-8'
}
})
.then(data=>data.data)
.then(xml2js.parseStringPromise)
.then(result => {
var temp = result['soap:Envelope']['soap:Body'][0]['EjecutarConsultaXMLResponse'][0]['EjecutarConsultaXMLResult'][0]['diffgr:diffgram'][0]['NewDataSet'][0]['Resultado'];
return {
doc: m,
state: temp[0].f430_ind_estado[0]
};
});
.catch(console.log)
);
Just do it one by one, using async/await to do that, this means you have to use parseStringPromise instead.
var response = [];
for (var i = 0; i < result.length; i++) {
let m = result[i].id
let xml = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">' +
'<soapenv:Header/>' +
'<soapenv:Body>' +
'<tem:EjecutarConsultaXML>' +
'<!--Optional:-->' +
'<tem:pvstrxmlParametros>' +
'<![CDATA[' +
'<Consulta><NombreConexion>USERNAME</NombreConexion>' +
'<IdConsulta>QUERY</IdConsulta>' +
'<Parametros>' +
'<doc>' + m + '</doc>' +
'</Parametros>' +
'</Consulta>' +
']]>' +
'</tem:pvstrxmlParametros>' +
'</tem:EjecutarConsultaXML>' +
'</soapenv:Body>' +
'</soapenv:Envelope>';
const options = {
explicitArray: true
};
try {
var { data } = await axios.post(url, xml, { // extract data from data.data
headers: {
'Content-Type': 'text/xml;charset=UTF-8'
}
})
var xmlObject = await xml2js.parseStringPromise(data)
var temp = (xmlObject['soap:Envelope']['soap:Body'][0]['EjecutarConsultaXMLResponse'][0]['EjecutarConsultaXMLResult'][0]['diffgr:diffgram'][0]['NewDataSet'][0]['Resultado'])
response.push({
doc: m,
state: temp[0].f430_ind_estado[0]
}) // push item to result array
} catch (error) {
console.log(error);
}
}
res.send(result) // send the result to client
How to change my code, when I push the button again the information is adding to previous, I need that when I push the button the information updates.
document.querySelector(".city-select").onchange = () => {
let strUser = document.querySelector(".city-select").value;
updateInfo(strUser);
//getWeather()// при селекті
}
function updateInfo(strUser) {
fetch(`http://api.openweathermap.org/data/2.5/weather?q=${strUser}&appid=f3ab273b1163fcf008d6d3ce02f9e86e`)
.then(function (resp) { return resp.json() })
.then(function (data) {
console.log(data);
document.querySelector('.package-name').textContent = data.name;
document.querySelector('.price').innerHTML = Math.round(data.main.temp - 273) + '°';
document.querySelector('.disclaimer').textContent = data.weather[0]['description'];
//https://openweathermap.org/img/wn/02d#2x.png
document.querySelector('.features li').innerHTML = `<img src="https://openweathermap.org/img/wn/${data.weather[0]['icon']}#2x.png">`;
document.querySelector('.button-primary').onclick = () => {
let div = document.createElement('div');
div.innerHTML = 'Wind speed: ' + data.wind.speed + ' km/h' + '<br>'+'Humidity: '+data.main.humidity + '%' + '<br>'+ 'Pressure: ' + data.main.pressure + 'Pa';
document.querySelector('.out').appendChild(div);
}
})
.catch(function () {
// catch any errors
});
}
This is because you used appendChild (https://www.w3schools.com/jsref/met_node_appendchild.asp)
Maybe you should try this instead (if there is already an element) : How to replace DOM element in place using Javascript?
This is my code:
let info = document.getElementById('info');
let week = document.getElementById('week');
/*Initializer Function*/
window.addEventListener('load', () => {
let long;
let lat;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(position => {
long = position.coords.longitude;
lat = position.coords.latitude;
const proxy = 'https://cors-anywhere.herokuapp.com/';
const api = `${proxy}https://api.darksky.net/forecast/d571a1e2483b31605b94edaae84c647e/${lat},${long}`;
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
let obj = JSON.parse(this.responseText);
console.log(obj);
document.getElementById('temperature-degree').innerHTML = obj.currently.apparentTemperature;
document.getElementById('location').innerHTML = obj.timezone;
document.getElementById('temperature-description').innerHTML = obj.currently.summary;
setIcons(obj.currently.icon, document.getElementById('currentDayIcon'));
for (let i = 0; i < obj.hourly.data.length; i++) {
info.innerHTML += `<div id='hour${i + 1}' class='hourly'>` + `<canvas id='hourIcon${i + 1}'></canvas>` + `<h3 id='hourTemp${i + 1}'></h3>` + `<p id='hourSummary${i + 1}'></p>` + '</div>';
setIcons(obj.hourly.data[i].icon, document.getElementById(`hourIcon${i + 1}`));
document.getElementById(`hourTemp${i + 1}`).innerHTML = obj.hourly.data[i].temperature;
document.getElementById(`hourSummary${i + 1}`).innerHTML = obj.hourly.data[i].summary;
}
for (let i = 0; i < 5; i++) {
week.innerHTML += `<div id='day${i + 1}' class='daily'>` + `<canvas id='dayIcon${i + 1}'></canvas>` + `<h2 id='dayTemp${i + 1}'></h2>` + `<p id='daySummary${i + 1}'></p>` + '</div>';
setIcons(obj.daily.data[i].icon, document.getElementById(`dayIcon${i + 1}`));
document.getElementById(`dayTemp${i + 1}`).innerHTML = obj.daily.data[i].temperatureMax;
document.getElementById(`daySummary${i + 1}`).innerHTML = obj.daily.data[i].summary;
}
}
};
xhr.open("GET", api, true);
xhr.send();
});
}
function setIcons(icon, iconId) {
const skycons = new Skycons({color: 'white'});
const currentIcon = icon.replace(/-/g, '_').toUpperCase();
skycons.play();
return skycons.set(iconId, Skycons[currentIcon]);
}
;
});
...and the problem is that when it comes to the icons being loaded and displayed it's only the last one that actually displays. I don't know what the problem is because if it's successful for the last one, then it should be successful for the previous 47 since the last is the same iteration done for the 48th time. Any ideas?
I have been having issues getting the data I am requesting to display correctly it was being displayed as undefined.
fetch(url)
.then(res => res.json())
.then((manager) => {
/* console.log(manager); */
for (i in manager.data){
var row = $('<div id="Data">' + '<br>' + getLogo(manager, i) + '<br>'
+ '<br>' + '<p1>' + getName(manager, i) + '</p1>'
+ getAdd1(manager, i) + getAdd2(manager, i) + getAdd3(manager, i)
+ getCity(manager, i) + getPcode(manager, i)
+ '<br>' + getCountry(manager, i) + '<br>'
My issue is with the call to getCountry
+ '<br>' + getWeb(manager, i) + '<br>'
+ '<br>' + getPhases(manager, i)
+ '<br>' + getPspon(manager, i)
+ '<br>' + getOspon(manager, i) + '<br>'
+ '<br>' + getDesc(manager, i) + '<br>'
+ '<br>' + '</div>' + '<br>');
$('#Results').append(row);
}
})
};
After some research I think the problem was that the getCountry method is so long it carries on with the rest of the code and displays it as undefined.
I then came across Promises and tried to add this to the function but now the html page just shows [object Promise].
The getCountry function is shown below and was the same before the addition of the Promise code.
This is what I am trying to achieve
Checks that there is an address and then checks for a country code. Assigns the 3 digit numerical country code to country. Then loads a JSON containing ISO 3166 data process this into a searchable object. Searches the objects for a match to the value stored in country. Then assigns the name field from the matched object to result and then returns it to be displayed at the end of the address.
function getCountry(manager, i){
return new Promise(function(resolve, reject) {
if(manager.data[i].attributes.addresses[0] != null && manager.data[i].attributes.addresses[0].country != null){
var country = manager.data[i].attributes.addresses[0].country;
var c = country.toString();
let url = 'http://data.okfn.org/data/core/country-codes/r/country-codes.json';
fetch(url)
.then(res => res.json())
.then((data) => {
console.log(data);
console.log(manager);
for(var i=0, length=data.length; i<length; i++){
if(data[i].M49 === c){
var result = data[i].name;
console.log(result);
Promise.resolve(result);
}
}
})
}
else {
var reason = " ";
Promise.reject(reason);
}
}
);
}
Where am I going wrong?
Updated code using #Zohaib Ijaz suggestion
fetch(url)
.then(res => res.json())
.then((manager) => {
/* console.log(manager); */
for (i in manager.data){
/* use a Promise in order to receive the result for the below function */
getCountry(manager, i).then((cm)=> {
var row = $('<div id="Data">' + '<br>' + getLogo(manager, i) + '<br>'
+ '<br>' + '<p1>' + getName(manager, i) + '</p1>'
+ getAdd1(manager, i) + getAdd2(manager, i) + getAdd3(manager, i)
+ getCity(manager, i) + getPcode(manager, i)
+ '<br>' + cm + '<br>'
+ '<br>' + getWeb(manager, i) + '<br>'
+ '<br>' + getPhases(manager, i)
+ '<br>' + getPspon(manager, i)
+ '<br>' + getOspon(manager, i) + '<br>'
+ '<br>' + getDesc(manager, i) + '<br>'
+ '<br>' + '</div>' + '<br>');
$('#Results').append(row);
});
}
});
}
The getCountry function
function getCountry(manager, i){
return new Promise(function(resolve, reject) {
if(manager.data[i].attributes.addresses[0] != null && manager.data[i].attributes.addresses[0].country != null){
var country = manager.data[i].attributes.addresses[0].country;
var c = country.toString();
let url = 'http://data.okfn.org/data/core/country-codes/r/country-codes.json';
fetch(url)
.then(res => res.json())
.then((data) => {
for(var i=0, length=data.length; i<length; i++){
if(data[i].M49 === c){
var result = data[i].name;
/* console.log(result); */
resolve(result);
}
else {
var reason = "";
reject(reason);
}
}
})
}
else {
var reason = "";
reject(reason);
}
}
);
}
This is what I see in the Chrome console (24 times)
Uncaught (in promise) test.html:1
What I noticed is that you're calling Promise.resolve(result); when you instantiated the Promise at the start of the function, you passed it a function with two arguments, resolve and reject. Those are what you should be using to 'end' your Promise, so changing them to just resolve(result) and reject(reason) should let your Promise resolve properly.
That said, the point of a Promise is to say "do this, and when that's done, .then do this other thing". So you'd need something like
getCountry(manager, i).then(function(result) {
// result is whatever you passed into resolve() in getCountry
// everything in here is done after getCountry finishes running
// and returns a value
}, function(rejection) {
// rejection is whatever you passed into reject() in getCountry
// this only happens if things didn't work
}
I'm not sure that Promises would work since you're calling the function in the middle of a concatenation. If your other functions make asyncronous calls, an alternative you might consider is to rewrite all of your asynchronous functions as Promises, then use Promise.all() to wait for all of them to resolve before proceeding to concatenate them into HTML. Something like this
var p1 = getLogo(manager, i);
var p2 = getName(manager, i);
var p3 = getAdd1(manager, i);
...
Promise.all([p1, p2, p3]).then(function(results) {
results.join('<br>')
// Whatever else you want to happen once all the functions are finished.
})
getCountry is not a synchronous function call. You need to wait for response.
fetch(url)
.then(res => res.json())
.then((manager) => {
/* console.log(manager); */
for (i in manager.data){
getCountry(manager, i).then((cm)=> {
// Add other values as well.
var row = $('<br>' + cm + '<br>');
$('#Results').append(row);
});
}
});