random string generators overwriting each other

Viewed 113

I am very new to Javascript and am writing a little webapp that, among other things, generates random strings (used for visualisation, doesn't need to be perfectly random).

The site consists of three divs, which each should generate a random string. On pageload, the divs display 20 hyphens as visual placeholders. Within each div I then want to replace one hyphen after the other with a random character, waiting 100ms between each iteration.

I got this working just fine with one div. But as soon as I do this with multiple divs, each randomly generated character gets immediately overwritten by the almost simultaneously randomly generated character in the following div.

I also added a console.log, so you can see that initially, each div generates a random character, but then the third div overwrites the character in the two other divs.

I'm assuming this is something I'm just not getting yet, so my question:

How can I keep this from happening, so that each div keeps its own string (and why is this happening in the first place)?

Thanks for your help, let me know if you need anything else!

const placeholderLength = 20;
let valueForm = [],
  valueModus = [],
  valueInhalt = [],
  placeholder = [];
let valueJoined;

const outputForm = document.getElementById("outputForm");
const outputModus = document.getElementById("outputModus");
const outputInhalt = document.getElementById("outputInhalt");

const randomCharacter = () => {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ";

  return characters.charAt(Math.floor(Math.random() * characters.length));
};

const initialValues = (value, destination, identifier) => {
  let i = 0;
  const loop = () => {
    setTimeout(() => {
      value[i] = randomCharacter();
      valueJoined = value.join("");
      destination.innerHTML = valueJoined;
      i++;
      if (i < placeholderLength) {
        loop();
      }

      console.log(identifier, valueJoined);
    }, 100);
  };
  loop();
};

const main = () => {
  for (let i = 0; i < placeholderLength; i++) {
    placeholder.push("-");
  }
  valueForm = valueModus = valueInhalt = placeholder;

  initialValues(valueForm, outputForm, "value 1");

  initialValues(valueModus, outputModus, "value 2");

  initialValues(valueInhalt, outputInhalt, "value 3");
};

main();
.output {
  font-family: monospace;
  letter-spacing: 1px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="main.css" />
  <title>Document</title>
</head>

<body>
  <h1>Random Values</h1>
  <h2>Value 1</h2>
  <div id="outputForm" class="output"></div>
  <h2>Value 2</h2>
  <div id="outputModus" class="output"></div>
  <h2>Value 3</h2>
  <div id="outputInhalt" class="output"></div>
</body>
<script src="app.js"></script>

</html>

1 Answers

The problem is that you assign the same placeholder array to all three generators. Since arrays are objects and objects are passed (and assigned) by reference, after this line:

valueForm = valueModus = valueInhalt = placeholder

...they will point to the same array object. Mutating that object will change it for each generator.

To solve the issue, remove that line:

const placeholderLength = 20;
let valueForm = [],
  valueModus = [],
  valueInhalt = [],
  placeholder = [];
let valueJoined;

const outputForm = document.getElementById("outputForm");
const outputModus = document.getElementById("outputModus");
const outputInhalt = document.getElementById("outputInhalt");

const randomCharacter = () => {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ";

  return characters.charAt(Math.floor(Math.random() * characters.length));
};

const initialValues = (value, destination, identifier) => {
  let i = 0;
  const loop = () => {
    setTimeout(() => {
      value[i] = randomCharacter();
      valueJoined = value.join("");
      destination.innerHTML = valueJoined;
      i++;
      if (i < placeholderLength) {
        loop();
      }

      console.log(identifier, valueJoined);
    }, 100);
  };
  loop();
};

const main = () => {
  for (let i = 0; i < placeholderLength; i++) {
    placeholder.push("-");
  }
  //valueForm = valueModus = valueInhalt = placeholder;

  initialValues(valueForm, outputForm, "value 1");

  initialValues(valueModus, outputModus, "value 2");

  initialValues(valueInhalt, outputInhalt, "value 3");
};

main();
.output {
  font-family: monospace;
  letter-spacing: 1px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="main.css" />
  <title>Document</title>
</head>

<body>
  <h1>Random Values</h1>
  <h2>Value 1</h2>
  <div id="outputForm" class="output"></div>
  <h2>Value 2</h2>
  <div id="outputModus" class="output"></div>
  <h2>Value 3</h2>
  <div id="outputInhalt" class="output"></div>
</body>
<script src="app.js"></script>

</html>

To assign hyphens to the arrays, you can clone the array (e.g. by using Array#slice()):

const placeholderLength = 20;
let valueForm = [],
  valueModus = [],
  valueInhalt = [],
  placeholder = [];
let valueJoined;

const outputForm = document.getElementById("outputForm");
const outputModus = document.getElementById("outputModus");
const outputInhalt = document.getElementById("outputInhalt");

const randomCharacter = () => {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ";

  return characters.charAt(Math.floor(Math.random() * characters.length));
};

const initialValues = (value, destination, identifier) => {
  let i = 0;
  const loop = () => {
    setTimeout(() => {
      value[i] = randomCharacter();
      valueJoined = value.join("");
      destination.innerHTML = valueJoined;
      i++;
      if (i < placeholderLength) {
        loop();
      }

      console.log(identifier, valueJoined);
    }, 100);
  };
  loop();
};

const main = () => {
  for (let i = 0; i < placeholderLength; i++) {
    placeholder.push("-");
  }
  
  valueForm = placeholder.slice();
  valueModus = placeholder.slice();
  valueInhalt = placeholder.slice();

  initialValues(valueForm, outputForm, "value 1");

  initialValues(valueModus, outputModus, "value 2");

  initialValues(valueInhalt, outputInhalt, "value 3");
};

main();
.output {
  font-family: monospace;
  letter-spacing: 1px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="main.css" />
  <title>Document</title>
</head>

<body>
  <h1>Random Values</h1>
  <h2>Value 1</h2>
  <div id="outputForm" class="output"></div>
  <h2>Value 2</h2>
  <div id="outputModus" class="output"></div>
  <h2>Value 3</h2>
  <div id="outputInhalt" class="output"></div>
</body>
<script src="app.js"></script>

</html>

You can also move the slicing part to the initialvalues function, you don't have to repeat yourself:

const placeholderLength = 20;
let valueForm,
  valueModus,
  valueInhalt,
  placeholder = [];
let valueJoined;

const outputForm = document.getElementById("outputForm");
const outputModus = document.getElementById("outputModus");
const outputInhalt = document.getElementById("outputInhalt");

const randomCharacter = () => {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ";

  return characters.charAt(Math.floor(Math.random() * characters.length));
};

const initialValues = (originalValue, destination, identifier) => {
  let i = 0;
  const value = originalValue.slice()
  const loop = () => {
    setTimeout(() => {
      value[i] = randomCharacter();
      valueJoined = value.join("");
      destination.innerHTML = valueJoined;
      i++;
      if (i < placeholderLength) {
        loop();
      }

      console.log(identifier, valueJoined);
    }, 100);
  };
  loop();
};

const main = () => {
  for (let i = 0; i < placeholderLength; i++) {
    placeholder.push("-");
  }
  valueForm = valueModus = valueInhalt = placeholder;

  initialValues(valueForm, outputForm, "value 1");

  initialValues(valueModus, outputModus, "value 2");

  initialValues(valueInhalt, outputInhalt, "value 3");
};

main();
.output {
  font-family: monospace;
  letter-spacing: 1px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="main.css" />
  <title>Document</title>
</head>

<body>
  <h1>Random Values</h1>
  <h2>Value 1</h2>
  <div id="outputForm" class="output"></div>
  <h2>Value 2</h2>
  <div id="outputModus" class="output"></div>
  <h2>Value 3</h2>
  <div id="outputInhalt" class="output"></div>
</body>
<script src="app.js"></script>

</html>

Related