问题描述:

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 = "[email protected]#$%^&*()-_+=><";

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: &lt;, &gt;, &amp;

    output.innerHTML += tp.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/&/g, '&amp;') + "<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 = "[email protected]#$%^&*()-_+=";

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));
}
相关阅读:
Top