Replace contents of <div> in a loop while waiting - javascript

I'm having difficulty figuring out my issue, or even finding solutions for it, which leads me to believe I may be going in the wrong direction.
On a cshtml page I have an ajax function tied to a button click. That routes back to my controller and a json string array is returned back to the client.
On the page itself I have <pre id="replaceMe">***</pre> defined. I am attempting to iterate through the json array doing $("#replaceMe").replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
Technically speaking this works, but only in the sense that the last update is visible. I might as well just go straight to the last element of the array.
I've tried using setTimeout to no avail, no changes and then suddenly the last element is displayed. I've found some sleep like functions that mimic the same basic behavior, all with similar results. I did see some recommendations for an async sleep, but none of my browsers seem to like the async and give me an error about a missing ;.
I then thought I could do something like
function updateSection(data) {
for (var i = 0; i < data.length; i++){
var section = $("#replaceMe");
section.fadeOut(500);
section.replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
section.fadeIn(500);
}
}
That however has the same end result. No apparent change and then suddenly it's the last element in the array.
I'm clearly going about this wrong, otherwise I'd find an example fairly readily I think, so what should I be doing instead?
To clarify and sum up, I want to replace the content of the <pre></pre> with text that's contained in an array. I want each iteration to be visible long enough for a human to see it and observe the changes (~1000ms) before going to the next iteration.
If, for example the array contains "Tom", "Dick", "Harry", then I would like for the page to have
<pre id="replaceMe">Tom</pre> for 1 second, then that element is replaced with
<pre id="replaceMe">Dick</pre> for 1 second, then that element is replaced with
<pre id="replaceMe">Harry</pre>
I am NOT looking for
<pre id="replaceMe">Tom</pre>
<pre id="replaceMe">Dick</pre>
<pre id="replaceMe">Harry</pre>

setTimeout in a for loop runs after the for loop execution completed. so, you always see the last value. to solve this, you can use $.each method which provides a callback function or use an Immediately Invoked Function Expression.
more detailed info: https://codehandbook.org/understanding-settimeout-inside-for-loop-in-javascript/
var data=[];
for(var i=0; i<10; i++){
data.push(i+' lorem ipsum doloret');
}
$.each(data, function(i, el){
setTimeout(function(){
$("#replaceMe").replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
},500 + ( i * 1000 ));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id="replaceMe">***</pre>

You can do that using setInterval function:
var words = ['interating', 'and', 'replacing', 'text'];
var replace = document.querySelector('#replace');
var count = 0;
function replaceText() {
replace.innerHTML = words[count];
if(count === words.length - 1)
count = 0;
else
count++;
}
setInterval(replaceText, 1000);
Updating:
You don't need to replace all the element, you can replace only the content, using the atribute innerText.

//pass in the data to loop over, and the index to show
function updateSection(data, index) {
//get a reference to the section
var $section = $('#replaceMe');
//fade out the section over 500 milliseconds, then perform callback on finish
$section.fadeOut(500, () => {
//change the text
$section.text(data[index]);
//fade in the section over 500 milliseconds, and then advance the index
//use modulus to reset the index to 0 when it reaches beyond the array
$section.fadeIn(500, () => updateSection(data, ++index % data.length));
});
}
updateSection(['Tom', 'Dick', 'Harry'], 0);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="replaceMe">***</div>

Related

I am trying to stop my function from displaying the same object twice when clicking a button

I have for quite some time now been trying to figure out how I can stop my code to print the same quote twice.
Also, when every single object in the array has been printed out, I'd like for it to reset somehow. So that you can browse through the quotes once you've gone through all of them.
This is the essential parts of my code:
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
The printQuote function simply contains information that's accessing information from my array:
var randomObjectNumber = getRandomQuote();
var html = "<p class='quote'>"
+ quotes[randomObjectNumber].quote +
"</p>";
document.getElementById('quote-box').innerHTML = html;
One random object is displayed each time you click the eventListener:
function getRandomQuote () {
var randomObjectNumber = Math.floor(Math.random() * quotes.length );
return randomObjectNumber;
}
I have some ideas on how to do this and I have tried them but without success. I tried giving each object a boolean property but I can't really seem to assign each property a boolean value without messing the printQuote function up.
I also tried assigning the object displayed to a different array but the same problem occurred there.
I feel like there is some concepts around the eventListener that I don't fully understand, because every time I try to manipulate a displayed object I just end up changing every single object.
This is what a typical object in the array looks like by the way:
{quote : "Darkness is merely the absence of light"}
(I also have other properties assigned to the object but i feel like presenting them would be redundant)
If someone could explain, or give me a hint, on how to solve this problem I've been struggling with for some time.
Some hints would be greatly appreciated!
Have a nice day.
Sebastian.
EDIT: All code: https://jsfiddle.net/fusqb7hz/
Basically what you need:
Create a separate array that will store all quotes that you've already used.
Remove quote from initial array.
Check if you still have quotes in initial array, if not, get them back from backup array.
The problem is that you call addEventListener twice:
//Let's developers create multiple eventListeners without being redundant.
function onClicking (printFunction) {
document.getElementById('loadQuote').addEventListener("click", printFunction, false);
}
onClicking(printColor);
onClicking(printQuote);
by calling onClicking twice you make the click happen twice, so addEventListener is added twice, meaning one click counts as two.
Change the above code for this:
//Let's developers create multiple eventListeners without being redundant.
document.getElementById('loadQuote').addEventListener("click", function(){
printColor();
printQuote();
});
Here is the jsfiddle:
https://jsfiddle.net/fusqb7hz/3/
I think the easiest approach is to shuffle your quote array and then go through them one by one. This gives you the next "random" as yet unseen quote. The only part I'm not keen on is this shuffler (a derivation of Fisher Yates) modifies the original quote array. You might not care about that though.
// --------------------------------
// A bunch of quotes
// --------------------------------
var quotes = [];
quotes.push({quote : "Darkness is merely the absence of light"});
quotes.push({quote : "quote 2"});
quotes.push({quote : "quote 3"});
quotes.push({quote : "quote 4"});
quotes.push({quote : "quote 5"});
// --------------------------------
// --------------------------------
// Your favorite array shuffle utility
// --------------------------------
var shuffle = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
// --------------------------------
// --------------------------------
// construct a function to get a random unseen quote until
// all quotes have been seen. Then reset...
// --------------------------------
var getQuote = (function(quotes, shuffle){
var current = 0;
var get = function(){
if ( !quotes || !quotes.length ) { return ""; }
if ( current >= quotes.length ){ current = 0; }
if ( current === 0 ){
console.log("randomizing quotes...");
shuffle(quotes);
}
return quotes[current++].quote;
};
return get;
})(quotes, shuffle);
// --------------------------------
var printQuote = function(){
document.getElementById('quote').innerText = getQuote();
};
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
<div id="quote"></div>
<button id="loadQuote">get quote</button>

Using Jquery Splice

I am trying to remove an item from an Array using Splice method.
arrayFinalChartData =[{"id":"rootDiv","Project":"My Project","parentid":"origin"},{"1":"2","id":"e21c586d-654f-4308-8636-103e19c4d0bb","parentid":"rootDiv"},{"3":"4","id":"deca843f-9a72-46d8-aa85-f5c3c1a1cd02","parentid":"e21c586d-654f-4308-8636-103e19c4d0bb"},{"5":"6","id":"b8d2598a-2384-407a-e2c2-8ae56c3e47a2","parentid":"deca843f-9a72-46d8-aa85-f5c3c1a1cd02"}];
ajax_delete_id = "e21c586d-654f-4308-8636-103e19c4d0bb,deca843f-9a72-46d8-aa85-f5c3c1a1cd02,b8d2598a-2384-407a-e2c2-8ae56c3e47a2";
$.each(arrayFinalChartData, function (idx, obj) {
var myObj = obj.id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
var vararrayFinalChartDataOne = arrayFinalChartData.splice(idx, 1);
}
});
console.log(arrayFinalChartData);
Please check at : http://jsbin.com/deqix/3/edit
Note : It does not complete the "last leg " of the loop. That means if I have 4 items, then it successfully executes 3 items. Same goes for 6,7...items.
I need to "REMOVE" few items and "PRESERVE THE BALANCE" in an array.
You can use for loop instead of $.each function:
alert('length before delete ' + arrayFinalChartData.length);
for (var i = arrayFinalChartData.length - 1; i >= 0; i--) {
id = arrayFinalChartData[i].id;
if(ajax_delete_id.indexOf(id) > -1){
arrayFinalChartData.splice(i, 1);
}
};
alert('length after delete ' + arrayFinalChartData.length);
Demo.
Complete edit :
After researching a bit, and console.logging a lot, I finally found where the issue is coming from ! It's actually quite simple, but very sneaky !
Theoretical explanation :
You are calling the splice function with the variable "idx", but remember that the splice function remaps / reindexes your array ! So, each time you splice the array, its size decreases by one while you're still inside the $.each function. The splice messes up jQuery indexation of your array, because jQuery doesn't know that you're removing elements from it !
Iterative explanation :
$.each function starts, thinking your array has 4 elements, which is true, but only for a while. First loop, idx = 0, no splice. Second loop, idx = 1, splice, which means that your array has now 3 elements left in it, reindexed from 0 to 2. Third loop, idx = 2, splice, which means your array has now two elements left in it, but $.each continues ! Fourth loop, idx = 3, js crashes, because "arrayFinalChartData[3]" is undefined, since it was moved back each time the array got spliced.
To solve your problem, you need to use a for loop and to start analyzing the array from the end, not from the beginning, hence each time you splice it, your index will decrease as well. And if you want to preserve balance, just push the removed items into an array. Remember that you are analyzing the array from the end, so items pushed into the "removedItems" array will be in reverse order. Just like this :
var removedItems = new Array();
for (var i = arrayFinalChartData.length - 1; i >= 0; i--) {
var myObj = arrayFinalChartData[i].id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
removedItems.push(arrayFinalChartData.splice(i, 1)[0]);
}
}
console.log(arrayFinalChartData);
console.log(removedItems);
And a working demo (inspect the page, observe the console and click "Run") :
http://jsfiddle.net/3mL6C/3/
I will not give credit to myself for this answer, thanks to another similar thread for giving me a hint.
Your problem here is that when the $.each is set up, it's expecting a certain length of object, which you are then changing. You need to loop in a way that respects the dynamic length of the object.
var i = 0;
while (i < arrayFinalChartData.length) {
var myObj = arrayFinalChartData[i].id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
// current item is in the list, so remove it but KEEP THE SAME INDEX
arrayFinalChartData.splice(i, 1);
} else {
// item NOT in list, so MOVE TO NEXT INDEX
i++
}
}
console.log(arrayFinalChartData);
Demo

Unsetting javascript array in a loop?

I have the following code:
$.each(current, function(index, value) {
thisArray = JSON.parse(value);
if (thisArray.ttl + thisArray.now > now()) {
banned.push(thisArray.foodID);
}
else{
current.splice(index,1);
}
});
There's a problem with the following line:
current.splice(index,1);
What happens is that it (probably) unsets the first case which fits the else condition and then when it has to happen again, the keys don't match anymore and it cannot unset anything else. It works once, but not in the following iterations.
Is there a fix for this?
You can use a regular for loop, and loop backwards:
for(var i=current.length-1; i>= 0; i--) {
var thisArray = JSON.parse(current[i]);
if (thisArray.ttl + thisArray.now > now()) {
banned.push(thisArray.foodID);
} else {
current.splice(i, 1);
}
});
Also it seems thisArray is actually an object, not an array...
you can use regular for loop, but after splice, decrease index by 1
for(var i=0; i<current.length; i++){
current.splice(i, 1); i--;
}
You should pay attention if you mutate an array (by moving objects) while it's being traversed because some element may get skipped like in your case.
When you call current.splice(index, 1) element index will be removed and element index+1 will take its place. But then index will be incremented, thus skipping one element.
A better solution is IMO the read-write approach. You keep one index as the "read pointer" and you always increment it, and another index (the "write pointer") that is incremented only when you decide an element should be kept in the array:
var wp = 0; // The "write pointer"
for (var rp=0; rp<a.length; rp++) {
if (... i want to keep element a[rp] ...) {
a[wp++] = a[rp];
}
}
a.splice(wp); // Remove all elements after wp
This is a o(N) operation and will move each element at most once. Other approaches like starting from the end and using the same splice(i, 1) approach instead will keep moving all elements after i each time an element needs to be removed.

Get array values and put them into different divs with a for loop?

I'm trying hard to learn javascrip+jquery on my own, and also trying to learn it right. Thus trying to enforce the DRY rule.
I find myself stuck with a problem where I have an array,
var animals = [4];
a function,
var legs = function(amount){
this.amount = amount;
this.body = Math.floor(Math.random()*amount)+1;
}
and an evil for loop. I also have 5 div's called printAnimal1, printAnimal2 and so on.. In which I wish to print out each value in the array into.
for(i = 0; i < animals.length; i++){
animals[i] = new legs(6);
$(".printAnimal"+i).append("animals[i]");
}
I feel as if I'm close to the right thing, but I cant seem to figure it out. I also tried something like this:
for(i = 0; i < animals.length; i++){
animals[i] = new legs(6);
$this = $(".printAnimal");
$(this+i).append("animals[i]");
}
But one of the problems seem to be the "+i" and I cant make heads or tails out of it.
I also know that I can simply do:
$(".printAnimal1").append("animals[i]");
$(".printAnimal2").append("animals[i]");
$(".printAnimal3").append("animals[i]");
...
But that would break the DRY rule. Is it all wrong trying to do this with a for loop, or can it be done? Or is there simply a better way to do it! Could anyone clarify?
Your first attempt should be fine, as long as you take "animals[i]" out of quotes in your append() call ($(".printAnimal"+i).append(animals[i]))
Also, I assume you declared var i; outside your for loop? If not, you'll want to declare it in your for loop (for(var i=0....)
EDIT: problems with your fiddle
you never call startGame()
you didn't include jQuery
you can't (as far as I know) append anything that isn't html-- in your case, you're trying to append a js object. What do you want the end result to look like?
http://jsfiddle.net/SjHgh/1/ is a working fiddle showing that append() works as you think it should.
edit: forgot to update the fiddle. Correct link now.
EDIT: reread your response to the other answer about what you want. http://jsfiddle.net/SjHgh/3/ is a working fiddle with what you want. More notes:
You didn't declare new when you called DICE
you have to reference the field you want, (hence dices[i].roll), not just the object
Just a few comments:
This is declaring an array with only one item and that item is the number 4
var animals = [4];
In case you still need that array, you should be doing something like:
var animals = []; // A shiny new and empty array
and then add items to it inside a for loop like this:
animals.push(new legs(6)); //This will add a new legs object to the end of the array
Also, what is the content that you are expecting to appear after adding it to the div?
If you want the number of legs, you should append that to the element (and not the legs object directly).
for(i = 0; i < animals.length; i++){
animals.push(new legs(6));
$(".printAnimal"+i).append(animals[i].body);
}
Adding another answer as per your comment
var i, dicesThrown = [];
function throwDice(){
return Math.ceil(Math.random() * 6);
}
//Throw 5 dices
for (i=0 ; i<5 ; i++){
dicesThrown.push( throwDice() );
}
//Show the results
for (i=0 ; i<5 ; i++){
$("body").append("<div>Dice " + (i+1) + ": " + dicesThrown[i] +"</div>");
}

JavaScript array.sort() Issue

drafting up a quick listing tool to list local kids baseball teams. Takes a couple of inputs and writes to a text field. There's some validation and whatnot too, but that's out of scope and doesn't seem to be impacting things.
Problem is, I'm having trouble figuring out how to "capture" the existing text, add the new inputs and sort the whole lot, before writing the new result to the paragraph element (effectively replacing it).
So far I have:
var LeagueTeams = [];
var IndividualTeam = '';
LeagueTeams.push(document.forms[0].TeamName.value);
LeagueTeams.push(document.getElementById('TeamList')
LeagueTeams = LeagueTeams.sort();
for (j = 0; j < LeagueTeams.length; j++) {
IndividualTeam = LeagueTeams.pop();
IndividualTeam = IndividualTeam + '' + \n;
document.forms[0].TeamName.value += IndividualTeam;
}
What I end up getting is my input, and then an array of my input PLUS the previous contents, with a couple of line breaks. Setting the operator to = instead of =+ stops it from printing to the array at all.
i.e.
Enter: a
Text area: a
Then enter: b
Text area: a ab
(etc)
OK, now that we have a better idea of what you're trying to do, here's some code that will do that:
HTML:
<label>Enter Team Name: <input id="newTeam" type="text"></label>
<button id="add">Add</button><br>
All Teams:<br>
<textarea id="allTeams" rows="40" cols="40"></textarea>
Javascript (plain javascript, no framework, called after page is loaded):
var teamList = ["Dodgers", "Mets", "Giants"];
document.getElementById("add").onclick = function() {
var input = document.getElementById("newTeam");
if (input.value) {
teamList.push(input.value);
}
updateTeamList();
input.value = "";
}
function updateTeamList() {
teamList.sort();
var o = document.getElementById("allTeams");
o.value = teamList.join("\n");
}
updateTeamList();
And, you can see it working here: http://jsfiddle.net/jfriend00/HkhsL/
Comments on your existing code:
I'm not sure I understand what you're trying to do overall, but do you realize that this loop is going to have problems:
for (j = 0; j < LeagueTeams.length; j++) {
IndividualTeam = LeagueTeams.pop();
IndividualTeam = IndividualTeam + '' + \n;
document.forms[0].TeamName.value += IndividualTeam;
}
Each time you do LeagueTeams.pop() you are reducing the length of the array and you're continually comparing to LeagueTeams.length in the for loop. This will only get half way through the array because each time through the loop, you increment j and decrement LeagueTeams.length which means you'll only get half way through the array.
If you intend to iterate all the way through the array in your for loop, you should use this version that gets the length once initially and simplifies the code in the loop:
for (j = 0, len = LeagueTeams.length; j < len; j++) {
document.forms[0].TeamName.value += LeagueTeams.pop() + '\n';
}
or perhaps even better, this version that doesn't even use j:
while (LeagueTeams.length > 0) {
document.forms[0].TeamName.value += LeagueTeams.pop() + '\n';
}
Then further, I see that you're trying to use LeagueTeams.sort() on an array that has both strings in it and DOM object references. What are you trying to do with that sort because the built-in sort function does a lexigraphical sort (e.g. alpha) which will do something odd with a DOM reference (probably sort by whatever toString() returns which may be object type)?
If you want to sort the input by team name, then you would need to put both team name and the DOM reference into an object, insert that object into the array as one unit and then use a custom sort function that would sort by the name in the object. As your code is written above, you see to be using document.getElementById('TeamList') which is the same for all teams so I'm not sure why you're putting it into the array at all.
If you can show your HTML and a more complete version of your code, we could help further. What you have above is just a non-working piece of code and we don't know what your HTML looks like that it's trying to operate on.
FYI, there are several syntax errors in the code you posted, so this can't be running code:
Missing paren at the end of this: LeagueTeams.push(document.getElementById('TeamList'))
Missing quotes around \n: IndividualTeam = IndividualTeam + '' + '\n';
If you are just trying to make a list of the teams, try something like:
<script type="text/javascript">
function addTeam(form) {
var para = document.getElementById('teamList');
var teams = para.innerHTML.split(/<br\s*[\\]?>/);
teams.push(form.teamName.value);
para.innerHTML = teams.sort().join('<br>');
}
</script>
<form action="">
<input type="text" name="teamName">
<input type="button" value="Add team" onclick="addTeam(this.form)">
</form>
<p id="teamList"></p>
You may be using different elements or layout, but the strategy should be about the same. If you are making a set of options for a select, things are a little easier.

Categories