Change innertext of element dynamically inside for loop of a function - javascript

Issue : Upon entering a higher number like 10000, innertext of the new paragraph element is updated only after the for loop ends. Please help to make the innertext get updated for each number.
The increment function is called when an onchange event happens after sending a number as a input to the input element.
JAVASCRIPT :
function increment() {
var count = document.getElementById('ac_count').value; //ac_count is the id of the input element
var stat = document.createElement("p");
stat.id = "current_status";
stat.innerText = "";
document.body.appendChild(stat);
stat.style.display = "block";
for (g = 1; g < count; g++) {
stat.innerText = Number(g + 1) + " out of " + count + " records have been processed";
}
}

The DOM doesn't redraw until the executing thread is free. You need to introduce async delays to your code to see a progressive update.
function increment() {
var count = document.getElementById('ac_count').value;
var stat = document.createElement("p");
stat.id = "current_status";
document.body.appendChild(stat);
var g = 1
var itvl = setInterval(function() {
update(g);
g += Math.floor(Math.random() * 20) + 1
if (g >= count) {
clearInterval(itvl);
update(count);
}
}, 10)
function update(g) {
stat.textContent = g + " out of " + count + " records have been processed";
}
}
<input type=number value=10000 id=ac_count>
<button onclick="increment()">CLICK ME</button>

I'm not suggesting that this is a good solution, but this should achieve the effect you're looking for
function increment() {
var count = document.getElementById('ac_count').value; //ac_count is the id of the input element
var stat = document.createElement("p");
stat.id = "current_status";
stat.innerText = "";
document.body.appendChild(stat);
stat.style.display = "block";
updateInnerText(0, count);
}
function updateInnerText(number, max){
if(number < max){
stat.innerText = Number(number + 1) + " out of " + count + " records have been processed";
setTimeout(function(){
updateInnerText(number+1, max);
}, 500);
}
}

Related

Unable to target JS generated button within an array with a function, getting "Cannot read property 'setAttribute' of null" error

I'm simply trying to cause a specific button background to change when I click it.
I generated 100 buttons (in the hopes of making a simple game, later on) as an array through a for loop, while assigning an id and a distinct function call (based on the incrementing 'i' value of the loop), and am unable to actually cause the background to change upon clicking, getting the followed error instead
"Uncaught TypeError: Cannot read property 'setAttribute' of null
at showOptions (brain.js:31)
at HTMLButtonElement.onclick (index.html:15)"
My code goes as follows
var btn = [];
function generateBoard() {
for (i = 0; i < 100; i++) {
var modulo = i % 10;
var up, forLeft;
btn[i] = document.createElement("BUTTON");
var element = document.getElementById("body");
//btn[i].innerText = "CLICK ME";
element.appendChild(btn[i]);
btn[i].style.backgroundColor = "white";
btn[i].id = i;
btn[i].style.width = "50px";
btn[i].style.height = "40px";
btn[i].style.position = "absolute";
btn[i].style.top = modulo * 100;
btn[i].style.left = Math.floor(i / 10) * 100;
btn[i].x = (i + 10) % 10;
btn[i].y = Math.floor(i / 10);
document
.getElementById(btn[i].id)
.setAttribute("onclick", "showOptions(i)");
btn[i].innerText = btn[i].id;
console.log(
btn[i].id +
" " +
btn[i].style.left +
" " +
btn[i].style.top +
" " +
btn[i].x +
" " +
btn[i].y
);
}
}
generateBoard();
function showOptions(i) {
document.getElementById(i).setAttribute("style", "background-color: red;"); //this is line 31
}
in console.log I actually get the correct digit as btn[i].id, oddly enough.
the (index.html:15) line of the error is simply
</html>
There are multiple issues with your code.
You should get body element with getElementsByTagName, which returns array. So pick first element.
Setting onclick attribute should use value of i not text i.
var btn = [];
function generateBoard() {
for (i = 0; i < 100; i++) {
var modulo = i % 10;
var up, forLeft;
btn[i] = document.createElement("BUTTON");
var element = document.getElementsByTagName("body")[0];
//btn[i].innerText = "CLICK ME";
element.appendChild(btn[i]);
btn[i].style.backgroundColor = "white";
btn[i].id = i;
btn[i].style.width = "50px";
btn[i].style.height = "40px";
btn[i].style.position = "absolute";
btn[i].style.top = modulo * 100;
btn[i].style.left = Math.floor(i / 10) * 100;
btn[i].x = (i + 10) % 10;
btn[i].y = Math.floor(i / 10);
document.getElementById(btn[i].id).setAttribute('onclick', 'showOptions(' + i + ')');
btn[i].innerText = btn[i].id;
console.log(btn[i].id + " " + btn[i].style.left + " " + btn[i].style.top + " " + btn[i].x + " " + btn[i].y);
}
}
generateBoard();
function showOptions(i) {
document.getElementById(i).setAttribute("style", "background-color: red;"); //this is line 31
}
I've reworked your code, fixing a few issues. I assume you have an element with id body, which is distinct from the body element, which can be accessed through document.body.
var buttons = [];
function generateBoard(){
for(i = 0; i < 100; i++) {
var modulo = i % 10;
buttons[i] = document.createElement("BUTTON");
document.getElementById("body").appendChild(buttons[i]);
//buttons[i].innerText = "CLICK ME";
buttons[i].style.backgroundColor = "white";
buttons[i].id = i;
buttons[i].style.width = "50px";
buttons[i].style.height = "40px";
buttons[i].style.position = "absolute";
buttons[i].style.top = modulo * 100;
buttons[i].style.left = Math.floor(i / 10) * 100;
buttons[i].x = (i + 10) % 10;
buttons[i].y = Math.floor(i / 10);
buttons[i].addEventListener('click', function(event) {
// This code is run when the button is clicked
// Note I am passing the element, rather than an id
showOptions(this);
});
buttons[i].innerText = i;
console.log(buttons[i].id + " " + buttons[i].style.left + " " + buttons[i].style.top + " " + buttons[i].x + " " + buttons[i].y);
}
}
generateBoard();
function showOptions(button){
button.style.backgroundColor = "red";
}
Rather than assigning and attempting to use an ID attribute you can target the clicked element by referring to a property of the event itself. If you were to analyse the event ( use console ) you would notice several properties - the target allows access to the element itself which helps simplify the showOptions function. In this instance you could also simply use this to refer to the button itself from within showOptions - which would make it even simpler - such as this.style.backgroundColor='red';
let bttns=[];
const generateBoard=function( s=100 ){
const showOptions=function(e){
e.target.style.backgroundColor='red';
};
for( let i=0; i < s; i++ ){
/* create a new button and add properties */
let bttn=document.createElement('button');
bttn.setAttribute('id',i);
bttn.setAttribute('data-x',((i+10)%10));
bttn.setAttribute('data-y',Math.floor(i/10));
bttn.style.left=( Math.floor( i / 10 ) * 100 )+'px';
bttn.style.top=( ( i % 10 ) * 100 )+'px';
bttn.style.width = '50px';
bttn.style.height = '40px';
bttn.style.position = 'absolute';
bttn.innerText=i;
/* bind event listener */
bttn.addEventListener('click', showOptions );
/* add to the DOM */
document.body.appendChild( bttn );
/* if it is important to have in an array... */
bttns.push( bttn );
}
}
document.addEventListener('DOMContentLoaded',(e)=>{
generateBoard( 100 );
})
Adding arbitrary attributes to elements is not best practise - rather than assigning x and y you should make use of dataset attributes instead - so data-x and data-y would be correct.

2D Battleship game demo. Suggestions and logic corrections welcome

I am a novice programmer. I used a Random number generator to place ships. Placing ships needs few tweaks. Will use loops to make use of all the available space and also incorporate vertical placement.
Please review my code below and suggests any tweaks or logic corrections.
Thank you.
var rn = 0;
var bx = by = cx = cy = 0;
var guess;
var guesses
var fsunk;
var bhit;
var c1hit;
var userchoices = [];
var battleship = []; //ship position stored in array
var cruiser1 = [];
var sunk = []; //ship status array 0 = floating, 1 = sunk
//var cruiser2 = [];
//var destroyer1 = [];
//var destroyer2 = [];
//var destroyer3 = [];
//var submarine1 = [];
//var submarine2 = [];
//var submarine3 = [];
//var submarine4 = [];
function rng() //Generates Random Numbers
{
rn = Math.floor(Math.random() * 5);
console.log("rn = " + rn);
return rn;
}
function spawn() //uses rng() to get x and y coordinates
{
bx = rng();
by = rng();
cx = rng();
cy = rng();
position();
}
function position() // ship positon generated
{
battleship[0] = ("" + bx + by);
battleship[1] = ("" + bx + (by + 1));
battleship[2] = ("" + bx + (by + 2));
battleship[3] = ("" + bx + (by + 3));
cruiser1[0] = ("" + cx + cy);
cruiser1[1] = ("" + cx + (cy + 1));
cruiser1[2] = ("" + cx + (cy +2));
}
function play() // onclick function
{
console.log("game start");
sunk[0] = 0; //battleship status flag
sunk[1] = 0; //cruiser1 status flag
bhit = 0; //battleship hit counter set to zero
c1hit = 0; //cruiser hit counter set to zero
console.log("sunk array = " + sunk[0] + " " + sunk[1]);
fsunk = 0; //fleet status
spawn();
position();
console.log("battleship coordinates" + battleship);
console.log("cruiser coordinates" + cruiser1);
game();
}
function game()
{
while(fsunk == 0)
{
if(sunk[0] && sunk[1] == 1) //check if fleet is sunk
{
alert("enemy fleet sunk");
fsunk = 1;
}
else
{
userinput(); //calls function to get users input
if(battleship.includes(guess)) //checks if users guess is in battleship array
{
console.log("game.if") //just to see which clause is executing
bhit++; //no of hits on battleship incremented
if(bhit == 4)
{
sunk[0] = 1; //battleship status set to sunk
console.log("battleship sunk");
}
}
else if (cruiser1.includes(guess))
{
console.log("game.elseif")
c1hit++; //no of hits in cruiser1 incemented
if(c1hit == 3)
{
sunk[1] = 1; //cruiser1 status set to sunk
console.log("cruiser1 sunk");
}
}
}
}
}
function userinput()
{
guess = prompt("Enter the grid coordinates. Do not use space. X-coordinates
0-6, Y-coordinates 0-6.");
console.log("users input = " + guess);
while(guess < 0 || guess > 66 || userchoices.includes(guess)) //checks user input for repeated strikes or out of range. Suggest better way if possible. this is just bad code
{
alert("You have entered an invalid coordinate.");
guess = prompt("Try Again!");
}
guesses++; //increments no of guessess
userchoices.push(guess); //add users guess to array
return guess;
}
I have added comments to better explain each line of code. But feel free to ask for any explanations and do correct my logic wherever required.
Thank you
Edit: I am currently working on incorporating more ships and vertical positioning. I am aware that requires major changes to spawn() and position(). I am also extending the board to the standard 10 X 10 grid and that also makes validating users input way easier.

Javascript - Why can't I update the textContent of an h3 with my conditional statement?

I have a conditional that checks to see if an item has been added to an array. If that item has been added to the array, I set a previously declared variable "isDouble" from 'false' to 'true'. I then later use another conditional to check if 'isDouble' is true.
If the 'isDouble' is 'false', I want to create a brand new h3 and populate the text with the quantity of that item - if not, I simply want to update the quantity of that item WITHOUT creating a new h3.
The function that is handling all this is called 'addCartItem()' - nearer the end of the code
Can someone please help?
Thank you
JAVASCRIPT
(function () {
let body = document.querySelector('body');
let totalBasket = document.querySelector('.totalBasket');
let cartCount = document.querySelector('.cartCount');
let cartItemsDiv = document.querySelector('.cartItems');
function DrinkBluePrint(name, price) {
this.name = name;
this.price = price;
this.quantity = 0;
}
let latte = new DrinkBluePrint('Latte', 5.00);
let flatW = new DrinkBluePrint('Flat White', 3.60);
let cap = new DrinkBluePrint('Cap', 2.75);
let moc = new DrinkBluePrint('Moc', 3.15);
let cortado = new DrinkBluePrint('Cortado', 3.15);
let array = [
latte,
flatW,
cap,
moc,
cortado
];
let cart = [];
let p;
let button;
let isDouble = false;
for (let i = 0; i < array.length; i++) {
p = document.createElement('p');
button = document.createElement('button');
button.textContent = 'Add';
let text = document.createTextNode(array[i].name);
p.appendChild(text);
body.appendChild(p);
body.appendChild(button);
button.addEventListener('click', (e) => {
if (cart.indexOf(array[i]) !== -1) {
isDouble = true;
}
cart.push(array[i]);
displayTotal();
addCartItem();
});
function displayTotal() {
let total = 0;
for (let i = 0; i < cart.length; i++) {
total += cart[i].price;
}
totalBasket.textContent = '£ ' + total.toFixed(2);
cartCount.textContent = `You have ${cart.length} items in your cart`;
if (total >= 10) {
let discountedPrice = (total - 3);
totalBasket.textContent = '£ ' + discountedPrice.toFixed(2);
}
}
// code that needs fixed below
function addCartItem() {
// add one to quantity
addOne();
// create h3 and append text node
let h3 = document.createElement('h3');
let h3Text = document.createTextNode(array[i].name + " " + array[i].price.toFixed(2) + " x " + array[i].quantity);
h3.appendChild(h3Text);
// check to see if item has already been added to cart
if (!isDouble) {
// if item hasn't been added before, append the h3 to the div
cartItemsDiv.appendChild(h3);
} else {
// if item has already been added, then update the text of the existing h3
h3.textContent = array[i].name + " " + array[i].price.toFixed(2) + " x " + array[i].quantity + " blah blah blah";
}
console.log(h3.textContent);
}
function addOne() {
let itemQuantity = array[i].quantity += 1;
}
};
})();
In your code, you perform:
// create h3 and append text node
let h3 = document.createElement('h3');
let h3Text = document.createTextNode(array[i].name + " " + array[i].price.toFixed(2) + " x " + array[i].quantity);
h3.appendChild(h3Text);
if (!isDouble) {
// if item hasn't been added before, append the h3 to the div
cartItemsDiv.appendChild(h3);
} else {
// if item has already been added, then update the text of the existing h3
h3.textContent = array[i].name + " " + array[i].price.toFixed(2) + " x " + array[i].quantity + " blah blah blah";
}
In the consequent block of your if clause, where isDouble is false (!isDouble == true), you appear to do what you want. However, in the alternative block, you do not modify the existing h3 element, you modify the h3 element you just created in the addCartItem function. You need to select the existing h3 element in the alternative block.
You could give the h3 element an id, based upon array[i].name, and then when adjusting an h3 already on the page, query for the element with that id, and then modify it.
let h3 = document.createElement('h3');
h3.id = array[i].name;
...
else {
h3 = document.getElementById(array[i].name);
... // modify the h3 created earlier
}
You can not change data using textcontent.
let h3 = document.createElement('h3');
if (!isDouble) {
let h3Text = document.createTextNode(array[i].name + " " + array[i].price.toFixed(2) + " x " + array[i].quantity);
h3.appendChild(h3Text);
cartItemsDiv.appendChild(h3);
} else {
h3.textContent = array[i].name + " " + array[i].price.toFixed(2) + " x " + array[i].quantity + " blah blah blah";
h3.appendChild(h3Text);
}

I guess i need dynamic variables in javascript to do this?

I am trying to use this stopwatch here: Stopwatch Script but for many counters on the page, that are dynamically created via php loops.
For example:
<button type="button" class="btn" id="bn1" data-bid="n1">Start</button>
<br><br>
<div id=n1></div>
<br><br>
<button type="button" class="btn" id="bn2" data-bid="n2">Start</button>
<br><br>
<div id=n2></div>
Using jquery i am trying to create 2 simple functions. One to start each timer clicked and one to stop it
<script src="http://code.jquery.com/jquery-2.2.0.min.js"></script>
<script language=javascript>
var vars = {};
var bid;
var h = 0;
var m = 0;
var s = 0;
$('[class^=btn]').on('click', function() {
var pause = $(this).text() === 'Stop';
$(this).text(pause ? 'Start' : 'Stop');
bid = $(this).attr('data-bid');
return (this.timvar ^= 1) ? startimer(bid) : stop(bid);
});
function stop(bid) {
clearTimeout(['tm' + bid]);
}
function startimer(bid) {
//vars['tm' + bid] = setInterval('disp('+bid+')',1000);
vars['tm' + bid] = setInterval(function() {
disp(bid)
}, 1000);
}
function disp(bid) {
//alert(bid);
// Format the output by adding 0 if it is single digit //
if (s < 10) {
var s1 = '0' + s;
} else {
var s1 = s;
}
if (m < 10) {
var m1 = '0' + m;
} else {
var m1 = m;
}
if (h < 10) {
var h1 = '0' + h;
} else {
var h1 = h;
}
// Display the output //
str = h1 + ':' + m1 + ':' + s1;
document.getElementById(bid).innerHTML = str;
// Calculate the stop watch //
if (s < 59) {
s = s + 1;
} else {
s = 0;
m = m + 1;
if (m == 60) {
m = 0;
h = h + 1;
} // end if m ==60
} // end if else s < 59
// end of calculation for next display
}
</script>
Somehow i have to be able to execute these 2 functions multiple times for each button, that will use a dynamic 'tm' variable that updates the text inside the n1 and n2..(etc) divs with the timer. And then when the user presses stop to be able to stop that specific tm var.
But...i am kinda lost when it comes to dynamic variables and how to properly pass them around between javascript functions...I am not even sure if there is a simpler way of doing this...I dont know what's the proper term to search for it in google. Any advice ? What am i doing wrong ?
-Thanks

Such a low success rate with this script

The script itself works fine and all; it is errorless. However, out of running 7,500 times, only 35 were successful. How would I improve the success rate of Regex? Because right now, it is not doing the job.
var IDs = [136758649, 116770724, 136171998]//A lot more IDS than this
var PriceWanting = 60
var scanneditems = 0
var itemerror = 0
document.write('<p id = "title">Total number bought: 0 items for a total of 0</p>')
document.write('<p id = "scanned">Items scanned: 0</p>')
document.write('<p id = "itemerrors">Items scanned: 0</p>')
var buys = 0
var totalrobuxspent = 0
console.log("Bot started")
var loop = setInterval(function()
{
for (var i = 0;i<IDs.length;i++) {
$.get(" http://m.roblox.com/items/" + IDs[i] + "/privatesales",function(data) {
var Regex = /\<span class="currency-robux">([\d,]+)\<\/span\>/;
var PriceSelling = data.match(Regex);
scanneditems = scanneditems + 1
document.getElementById("scanned").innerHTML = "Scanned items: " + scanneditems
PriceSelling = PriceSelling ? PriceSelling[1] : '';
if (PriceSelling.length < 1) {
itemerrors = itemerrors + 1
document.getElementById(''itemserror'').innerHTML = ''Total errors: '' + itemerrors
return
}
PriceSelling = Number(PriceSelling.replace(",",""))
PriceSelling = PriceSelling * 1
totalrobuxspent = totalrobuxspent + PriceSelling
var remaining = PriceWanting - PriceSelling
if (remaining >= -0.1)
{
buys = buys + 1
document.getElementById("title").innerHTML = "Total number of items bought: " + buys + " for a total of " + totalrobuxspent + " "
var Regex2 = /<a href="\/Catalog\/VerifyTransfer\DuserAssetOptionId=([\d,]+)\Damp;expectedPrice=([\d,]+)">/
var HatBuyId = data.match(Regex2)[1]
var HatBuyLink = "http://m.roblox.com/Catalog/VerifyPurchase?assetid=" + HatBuyId + " &type=robux&expectedPrice=" + PriceSelling
var Explorer = document.createElement('iframe');
function Buy(){
Explorer.contentDocument.forms[0].submit();
console.log("Item purchase complete, scanning again.")
var inf = document.createElement('div');
inf.style.fontSize = "18px";
inf.style.background = "rgba(0,0,5,0)";
inf.style.position = "absolute";
inf.style.width = "100%";
inf.style.height = "18pt";
inf.innerText = "Bot currently running. Purchases: "+answer;
document.body.appendChild(inf);
};
Explorer.onload = Buy;
Explorer.width = "100%";
Explorer.height = "85%";
Explorer.src = HatBuyLink;
document.body.innerHTML = "";
document.body.appendChild(Explorer);
}
})
}
},500)
.get is asynchronous. This means that the callback function is not executed until the response is returned. By that point, you are no longer in a loop and in fact the loop has already completed.
Though it's about a slightly different problem, this Question is a very good read to understand how asynchronous calls work.
In addition, continue here would still be illegal, even without the asynchronous behavior, because the function has its own context and is not in the same context as the loop. This simple example would also be illegal and may be easier to understand the context issue:
for(var i=0; i<10; i++){
someFunc();
}
function someFunc(){
continue; // illegal
}
Though in your case the function is an anonymous function, the same concept applies.
Please, use semicolons in your code.
continue;
is applicable in loops, not in functions. Use the
return;
keyword to get out of the context of a function.

Categories

Resources