JS: innerHTML inconsistency - javascript

I am using Google Chrome version 43.0.2357.130. I am trying to append output to an HTML element using innerHTML, which is being done inside of a loop. I get the expected result most of the time, but if I click on the "Generate" button over and over, eventually it will give me an unexpected result. For instance, one of the passwords will be chopped off at a random spot. I used the JS debugger in Chrome, but that didn't help much. Then I tried to debug it myself by using the alert() function alongside the innerHTML property so that I could compare the output. The output in the alert() popup was never truncated, unlike innerHTML.
I highlighted what I think is the problem code with '/* PROBLEM CODE */' in the JS file. The files should be placed in the same directory. Here is the HTML and JS:
<!DOCTYPE html>
<html>
<head>
<title>PassGen - Random Password Generator</title>
<!--link rel="stylesheet" src="//normalize-css.googlecode.com/svn/trunk/normalize.css"-->
<!--link href="css/main.css" rel="stylesheet" type="text/css"-->
<!--script src="../app/js/jquery-2.1.4.js"></script-->
</head>
<body>
<form>
<h2>Password amount</h2>
<input type="text" id="amount" name="amount" />
<h2>Letter amount</h2>
<input type="text" id="letters" name="letters" />
<h2>Number amount </h2>
<input type="text" id="numbers" />
<h2>Symbol amount</h2>
<input type="text" id="symbols" />
<input onclick="generatePassword(); return false;" type="submit" value="Generate" />
</form>
<p id="output"></p>
<script src="plain-app.js"></script>
</body>
</html>
// get the DOM element we will be using for the final output
var output = document.getElementById("output");
function generatePassword(amount) {
clearPasswords();
// get DOM form elements (user input)
var amount = document.getElementById("amount").value;
var letters = document.getElementById("letters").value;
var numbers = document.getElementById("numbers").value;
var symbols = document.getElementById("symbols").value;
// populate character sets
var letterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var numberSet = "0123456789";
var symbolSet = "~!##$%^&*()-_+=><";
var array = [];
// if there is no password amount specified, create one password
if(amount === undefined) {
amount = 1;
}
for(var j = 0; j < amount; j++) {
// random character sets to be concatenated later
var rl = "";
var rn = "";
var rs = "";
var tp = ""; // concatenated password before shuffling
// 3 random letters
for(var i = 0; i < letters; i++) {
var rnd = Math.floor((Math.random() * 52));
rl += letterSet[rnd];
}
// 3 random numbers
for(var i = 0; i < numbers; i++) {
var rnd = Math.floor((Math.random() * 10));
rn += numberSet[rnd];
}
// 3 random symbols
for(var i = 0; i < symbols; i++) {
var rnd = Math.floor((Math.random() * 17));
rs += symbolSet[rnd];
}
tp = rl + rn + rs; // string concatentation
tp = tp.split(''); // transform string into an array
// shuffling
for(var i = 0; i < tp.length; i++) {
var rnd = Math.floor(Math.random() * tp.length);
var temp = tp[i];
tp[i] = tp[rnd];
tp[rnd] = temp;
}
// transform the array into a string
tp = tp.join("");
array[j] = tp; // for logging and debugging purposes
// tp can be replaced with array[j], but still get the inconsistency
/* PROBLEM CODE */
output.innerHTML += (tp + "<br />");
/* PROBLEM CODE */
//alert(array[j]);
}
console.log(array);
return array; // not useful?
}
// clear all password output
function clearPasswords() {
while(output.hasChildNodes()) {
output.removeChild(output.firstChild);
}
}
Does innerHTML have side effects I don't know about or am I using it incorrectly? Should it not be used for appends? Should appendChild() be used instead?

The problem is that some characters have special meaning in HTML: <, >, &.
Therefore, you can
Remove those characters from the list
Escape them: <, >, &
output.innerHTML += tp.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&') + "<br />";
Parse the text as text instead of as HTML, e.g.
Use textContent and white-space
output.textContent += tp + "\n";
#output { white-space: pre-line; }
Use createTextNode and appendChild
output.appendChild(document.createTextNode(tp));
output.appendChild(document.crateElement('br'));

The problem occurred in the symbolSet. It contained the characters < and >. When they were selected at random and added to the password, the browser tried to render them as elements when I was outputting the password to the screen. To fix this, I just removed the two characters from symbolSet, and changed the random symbol generation loop to reflect the shortened length of symbolSet.
I changed the two problematic sections of code to
var symbolSet = "~!##$%^&*()-_+=";
and
for(var i = 0; i < symbols; i++) {
var rnd = Math.floor((Math.random() * 15)); // this end of this line
rs += symbolSet[rnd];
}
In addition, I changed the useless if statement to (this can be applied to all of the fields)
if(!isNumber(amount) || amount === 0) {
amount = 1;
}
and added an isNumber function to check for valid input on all of the fields.
function isNumber(obj) {
return !isNaN(parseFloat(obj));
}

Related

How do I use a for-loop to add values to a JS variable? [duplicate]

This question already has answers here:
How to add two strings as if they were numbers? [duplicate]
(20 answers)
Closed 4 years ago.
I have made a piece of code that generates a random code of 12 characters. I am using Math.random and for-loops to do this. On the page you can write in an input how many codes you want.
What I want to do is save the generated codes in an array, however I can't do this because the for-loop and Math.random creates the code number by number and places them after each other. How can I add the whole 12 digit code to my array (so I can use it later)?
I've tried array.push with no luck. What works is outputting the numbers to DOM object in HTML, like this:
for (i = 0; i < 12; i++) {
var mathRandom = Math.floor(Math.random() * 9);
var result = document.querySelector("#result");
result.innerHTML += mathRandom;
}
But that doesn't put the 12 digit code into a variable. I've also tried this:
var codeNumber = "";
codeNumber += mathRandom;
But that ends up in the variable value having only 1 digit.
<input type="number" id="numberOfCodes">
<button onclick="codeGen()">Generate</button>
<div id="result"></div>
<script>
var numberOfCodes = document.querySelector("#numberOfCodes");
var arr = [];
function codeGen() {
x = numberOfCodes.value;
for (a = 0; a < x; a++) {
generate();
console.log("Generated code");
}
}
function generate() {
for (i = 0; i < 12; i++) {
var mathRandom = Math.floor(Math.random() * 9);
var result = document.querySelector("#result");
result.innerHTML += mathRandom;
}
}
</script>
</div>
</body>
</html>
I expect the codes created (after some changes) to be added to the array, so that I can later use the codes on the page. Each individual 12-digit code needs to have its own place in the array.
This should work:
var result = [], stringResult;
for (i = 0; i < 12; i++) {
var mathRandom = Math.floor(Math.random() * 9);
result.push(mathRandom);
}
stringResult = result.join(''); // concatenates all the elements
console.log(stringResult);
The problem with your code is that + sign attempts to determine types of the operands and to choose the right operation, concatenation or addition. When adding stuff to innerHtml it treats the number as string. That is why it worked.
You'll want to refactor things so generating a single code is encapsulated in a single function (generate() here), then use that function's output, like this. (I hope the comments are enlightening enough.)
var numberOfCodes = document.querySelector("#numberOfCodes");
var resultDiv = document.querySelector("#result");
function codeGen() {
var nToGenerate = parseInt(numberOfCodes.value);
for (var a = 0; a < nToGenerate; a++) {
var code = generate(); // generate a code
// you could put the code in an array here!
// for the time being, let's just put it in a new <div>
var el = document.createElement("div");
el.innerHTML = code;
resultDiv.appendChild(el);
}
}
function generate() {
var code = ""; // define a local variable to hold the code
for (i = 0; i < 12; i++) { // loop 12 times...
code += Math.floor(Math.random() * 9); // append the digit...
}
return code; // and return the value of the local variable
}
<input type="number" id="numberOfCodes" value=8>
<button onclick="codeGen()">Generate</button>
<div id="result"></div>
As this answer shows, this should work for you:
function makeRandCode() {
var code = "";
var ints = "1234567890";
for (var i = 0; i < 12; i++) {
code += ints.charAt(Math.floor(Math.random() * ints.length));
}
return code;
}
console.log(makeRandCode());
The problem is that you are adding numbers and what you really want is to concatenate them, the solution is to transform those numbers into String, then save them in the variable where you want to store them. An example:
2 + 2 = 4 and '2'+'2'='22'
Just use .toString() before save it in to the variable.

Problems averaging

I have the following function in javascript to calculate the average:
function calculaMediaFinal () {
var soma = 0;
for(var i = 1; i>5; i++) {
soma += parseInt(document.getElementById('resultado' + i).value, 10);
}
var media = soma / 5;
var inputCuboMedia = document.getElementById('ConcretizaObj');
inputCuboMedia.value = parseInt(media, 10);
}
function ContarObjetivos() {
let contador = 0;
if(document.getElementById('resultado' + i).value) {
contador++;
}
}
But I have a problem, it's that I put in that at most there are 5 which is not true, because the user is who chooses how many results he wants. That is, the 5 can not be filled if the user only wants 4. How do I average without the number 5 but with the number of results that the user wants?
You can do it like this.
create input element and let user pass each number into it separated by space
create button that will trigger the code that calculates the average
create element that will store the result
To perform the actual computation
get value of input field, split it at space ' ', remove white spaces around each separate number using trim
sum the array created in the previous step using reduce
divide the sum by the amount of provided numbers
const input = document.querySelector('input');
const btn = document.querySelector('button');
const res = document.querySelector('p > span');
function getAverage() {
const values = input.value.split(' ').map(v => v.trim());
const sum = values.reduce((acc, v) => acc + Number(v), 0);
res.textContent = (sum / values.length);
}
// 1 2 3 4 5 6 7 8 9 10
btn.addEventListener('click', getAverage);
<input type='text' />
<button>get average</button>
<p>result: <span></span></p>
Where you pass numbers into input field one by one separated by space (try passing 1 2 3 4 5 6 7 8 9 10) and then click button to perform the computation which then will be shown in span element.
Your loop wasn't executed:
In a for, the second parameter is the condition for which the iteration is going to be executed (true = execute). Changing > to <= made it work.
I also merged your two functions so that a not filled input doesn't count.
Here is a working snippet where I used all your code:
// Merged both function:
function calculaMediaFinal() {
let soma = 0;
let contador = 0;
for (var i = 1; i <= 5; i++) { // Changed > to <= here
if (document.getElementById('resultado' + i).value) {
soma += parseInt(document.getElementById('resultado' + i).value, 10);
contador++;
}
}
var media = soma / contador;
var inputCuboMedia = document.getElementById('ConcretizaObj');
inputCuboMedia.value = parseInt(media, 10);
}
<input id="resultado1"><br>
<input id="resultado2"><br>
<input id="resultado3"><br>
<input id="resultado4"><br>
<input id="resultado5"><br>
<button onclick="calculaMediaFinal();">calcula</button>
<br> Media:
<input id="ConcretizaObj">
⋅
⋅
⋅
If you don't need to specify the "base" in the parseInt function, I also suggest you to use the unary + operator:
// Merged both function:
function calculaMediaFinal() {
let soma = 0;
let contador = 0;
for (var i = 1; i <= 5; i++) { // Changed > to <= here
if (document.getElementById('resultado' + i).value) {
soma += +document.getElementById('resultado' + i).value;
contador++;
}
}
var media = soma / contador;
document.getElementById('ConcretizaObj').value = media;
}
<input id="resultado1"><br>
<input id="resultado2"><br>
<input id="resultado3"><br>
<input id="resultado4"><br>
<input id="resultado5"><br>
<button onclick="calculaMediaFinal();">calcula</button>
<br> Media:
<input id="ConcretizaObj">
Hope it helps.
If I understand your question correct, you want something like this. Create a new numeric input field that stores the amount that the user wants. Put the value of the input field in a variable and use it in your code? Also it probably needs to be '<=' in the for loop to be executed. For example
HTML add next line in your code:
<input type="number" id="userAmount" />
Javascript:
function calculaMediaFinal () {
var soma = 0;
var amount = parseInt(document.getElementById("userAmount").value);
for(var i = 1; i<=amount; i++) {
soma += parseInt(document.getElementById('resultado' + i).value, 10);
}
var media = soma / amount;
var inputCuboMedia = document.getElementById('ConcretizaObj');
inputCuboMedia.value = parseInt(media, 10);
}
function ContarObjetivos(){
let contador = 0;
if(document.getElementById('resultado' + i).value) {
contador++;
}
}

How to use variables in for loop statements in javascript?

So I'm trying to get this to roll an amount of dice equal to the specified input. To achieve this, I'm using a for loop, however it's not working.
As I just started coding recently, I have no idea why. Could someone please help?
<!DOCTYPE html>
<html>
<body>
<input type="number" id="diceamount" value="1">
<button onclick="FirstFunction()">Roll!</button>
<p id="display"></p>
<script>
var random = [];
document.getElementById("display").innerHTML = random;
a = document.getElementById("diceamount").innerHTML;
function FirstFunction() {
for (i=0; i<"a"; i++) {
x = Math.floor(Math.random() * 4) + 1;
random.push(x);
document.getElementById("display").innerHTML = random;
}
}
</script>
</body>
</html>
Here is how you could do it. There are several issues highlighted in comments:
function FirstFunction() {
// Reset array within this function
var random = [];
// Get value property, and convert to number (with +)
// And use var!
var a = +document.getElementById("diceamount").value;
// No quotes around a, and use var!
for (var i=0; i<a; i++) {
// Use var!
var x = Math.floor(Math.random() * 4) + 1;
random.push(x);
}
// Only show after the loop, and use textContent
document.getElementById("display").textContent = random;
}
<input type="number" id="diceamount" value="1">
<button onclick="FirstFunction()">Roll!</button>
<p id="display"></p>
Note that the array gets implicitly formatted as a comma separated value string when you display it.
This is not how you define "a".
This is how you do it:
for (i=0; i<a; i++) {
This is how you get the value from the text field:
var b = document.getElementById('textbox_id').value;
Then get the integer value:
var a = parseInt(b);
And then the for loop:
for (i=0; i<a; i++) {
It seems you are doing it wrong ,...
Suppose you want to take the value of input which has id = "diceamount"
so for that you have to store the value of the input in variable.
var a = document.getElementById('diceamount').value;
for(i = ; i<= a; i++){
your code
}
make this question down ; and look for some tutorials for javascript
var random = [];
// not useful? but in your code
document.getElementById("display").innerHTML = random;
function FirstFunction(event) {
// get local value into a and use it the + avoids parseInt
let a = Math.abs(+document.getElementById("diceamount").value);
for (i=0; i<a; i++) {
// x was not declared and should be
let x = Math.floor(Math.random() * 4) + 1;
random.push(x);
}
// could be textContent instead...outside loop
document.getElementById("display").innerHTML = random;
}
<input type="number" id="diceamount" value="1">
<button onclick="FirstFunction()">Roll!</button>
<p id="display">x</p>

Checking random number variable against Javascript array values for match, not working

For the sake of learning (I am very much a beginner), I am trying to write a function with that will generate random numbers according to three user inputed variables. The user can input how many random numbers to generate
var count = 10;
which numbers to avoid generating (seperated by commas)
var doNotInclude = (document.getElementById("doNotIncludeInput").value).split(",");
and what the highest number possible should be
var highestNumber = 10;
In theory, if the user inputed "1,2" for the doNotInclude variable, the function should create an array containing those two numbers and then each time it generates a random number, check it against the array to see if the number it generated is one of the numbers it isn't supposed to generate.
Unfortunately, it doesn't work. It creates the array as it should which I verified via console.log(), but it will still generate the numbers it isn't supposed to. Here's the full script:
document.getElementById("button").onclick = function() {
var total = 0,
average = 0,
random = 0,
count = parseInt(document.getElementById("countInput").value),
doNotInclude = document.getElementById("doNotIncludeInput").value).split(","),
highestNumber = parseInt(document.getElementById("highestNumberInput").value);
document.getElementById("text1").innerHTML = "";
for (var i = 0; i < count; i++) {
var proceed = false;
for (random = Math.floor(((Math.random()) * (highestNumber + 1))); proceed == false;) {
if (doNotInclude.indexOf(random)===-1) {
proceed = true;
}
}
document.getElementById("text1").innerHTML = document.getElementById("text1").innerHTML + "<br />" + (i + 1) + ". " + random;
total = total + random;
if (i == (count - 1)) {
total / count;
document.getElementById("text").innerHTML = "Mean average = " + (Math.round(total / count));
}
}
}
The part that isn't working
if (doNotInclude.indexOf(random)===-1) {
proceed = true;
}
the indexOf function, is something I read about on here, but I guess I don't fully understand it. By my understanding, it should check to see if any of the array values are the same as the random variable, and if not, then return "-1" and execute my code. It doesn't seem to be doing that though. I am super confused...would love some help. Is it possible the array is storing the numbers as strings instead of integers, and that is the problem?
Your if statement doesn't work because of coercion.
===-1
The array doNotInclude contains strings, but random is an integer value, === always compares value and type both.
You should either use ==. Or have the both types same.
Try this Fiddle
HTML
Count: <input id="countInput" value="10" /><br />
Do not include: <input id="doNotIncludeInput" value="0,1,2,3,4,5" /><br />
Highest Number: <input id="highestNumberInput" value="10" /><br />
<br />
<button type="button" id="button">Click to Run!</button><br />
<br />
Results:<br />
<div id="text1"></div>
Js
document.getElementById("button").onclick = function() {
var currentCount = 0;
var randomNumbers = [];
var count = parseInt(document.getElementById("countInput").value);
var doNotIncludeInput = document.getElementById("doNotIncludeInput").value.split(",");
var highestNumberInput = parseInt(document.getElementById("highestNumberInput").value);
var resultsElement = document.getElementById("text1");
resultsElement.innerHTML = "";
while(currentCount < count) {
var random = -1;
while(random === -1){
random = Math.floor((Math.random()) * (highestNumberInput + 1));
for(var i in doNotIncludeInput) {
if(parseInt(doNotIncludeInput[i]) === random){
random = -1;
}
}
if(random !== -1){
randomNumbers.push(random);
}
}
currentCount += 1;
}
resultsElement.innerHTML = randomNumbers.join(', ');
}

Specifying number of characters contained per line in a textarea element

I need to specify number of characters contained per line in a textarea element (Link to fiddle).
This is the horror I'm witnessing:
So cols="50" actually gives me following row lengths:
| Without scroll | With scroll
Chrome 50 47
FF 52 50
What I actually meant with cols="50" is that I want the textarea to hold exactly 50 characters per line. How I can achieve this so that it works in all major browsers?
EDIT: I would also like to point out that in the real page the total number of characters in a textarea is not limited so y-scrolling must be possible. Whether or not line breaks are automatically included by a textarea (or equivalent element) doesn't matter since the text is later split into rows containing maximum of 50 characters before sending them to the server with ajax post.
It seems this issue is only in Chrome, so you could do something like this:
<script>
window.onload = function(){
textareas = document.getElementsByTagName("textarea");
var isChrome = navigator.userAgent.indexOf("Chrome") != -1;
for(var i = 0; i < textareas.length; ++i){
if(isChrome){
textareas[i].style.width = textareas[i].offsetWidth + textareas[i].cols/3 + "px";
}
}
}
</script>
Check it out here: JSFiddle
Heres how you can do it in javascript:
<script>
var textareas = document.getElementsByTagName("textarea");
for(var i = 0; i < textareas.length; ++i){
textareas[i].addEventListener("input", handleInput);
textareas[i].cols += 3;
handleInput(null, textareas[i]);
}
function handleInput(event, area){
if(area === undefined) area = this;
area.value = fixText( area.value, area.value.length, area.cols-3);
}
function fixText(text, length, limit){
var counter = 0;
for(var i = 0; i < length; ++i){
var currChar = text[i];
counter++;
if(currChar === "\n"){
counter = 0;
} else if(counter > limit){
text = text.substring(0,i) + "\n" + text.substring(i, text.length);
counter = 0;
}
}
return text;
}
Here i use the col attribute of the textarea and add a linebreak every VALUE_OF_COL characters that doesn't contain a linebreak.
Check it out here: JSFiddle

Categories