Jump to content
P. Jongejan

How do I get a stable array

Recommended Posts


As a beginner with JavaScript, I've the following problem:
For my free educational website niutec.nl I'm  trying to create an interactive page with an explanation of the digital system.
Marbles can be toggled on a marbling board, to compose a digital number.
In the text this is made visible as a digital number sequence. That's working correctly, but I can't combine those numbers into a stable array to convert them to a decimal notation.
Clicking on one marble influences the other numbers. I've thought of the solution to be with using a closure, but I can't get it working.
Who can help me with a solution?

The active file can be found on:
http://www.niutec.nl/Experiments/talstelsels.html
The still not active file can be found on:
http://www.niutec.nl/Programmeren/talstelsels.html

<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset=UTF-8">
<meta name="viewport content="width=device-width, initial-scale=1.0">
<meta content="test"  name="description">
<meta="robots" content="noindex, nofollow">
<title>talstelsels</title>
<link href="../includes/css/count.css" rel="stylesheet" type="text/css" media="screen, projection, handheld">
</head>

<body>
<article>
    
<div id="digitPlankContainer">
  <img src="../images/tellen/digitplank.png">
  <button class="btn" onClick="myFunction()" id="marble0"></button>
  <button class="btn" onClick="myFunction()" id="marble1"></button>
  <button class="btn" onClick="myFunction()" id="marble2"></button>
  <button class="btn" onClick="myFunction()" id="marble3"></button>
</div>

<table id="tableDigLayout">
  <tr>
    <td id="tdDigOne">
      <ul>
        <li><span id="cijfer0">0</span> knikker op positie 1</li>
        <li><span id="cijfer1">0</span> knikker op positie 2</li>
        <li><span id="cijfer2">0</span> knikker op positie 3</li>
        <li><span id="cijfer3">0</span> knikker op positie 4</li>
        <li>0 knikker op positie 5</li>
        <li>0 knikker op positie 6</li>
        <li>0 knikker op positie 7</li>
        <li>0 knikker op positie 8</li>
        <li>0 knikker op positie 9</li>
      </ul>
    </td>
    <td id="tdDigTwo">
      <ul>
        <li>&#8594;</li>
        <li>&#8594;</li>
        <li>&#8594;</li>
        <li>&#8594;</li>
        <li>&#8594;</li>
        <li>&#8594;</li>
        <li>&#8594;</li>
        <li>&#8594;</li>
        <li>&#8594;</li>
      </ul>
    </td>
    <td id="tdDigThree">
      <ul>
        <li id="value0">0</li>
        <li id="value1">0</li>
        <li id="value2">0</li>
        <li id="value3">0</li>
        <li>0</li>
        <li>0</li>
        <li>0</li>
        <li>0</li>
        <li>0</li>
      </ul>
    </td>
    <td id="tdDigFour">
      <ul>
        <li>&nbsp;</li>
        <li>&nbsp;</li>
        <li>&nbsp;</li>
        <li>&nbsp;</li>
        <li>&nbsp;</li>
        <li>&nbsp;</li>
        <li>&nbsp;</li>
        <li>&nbsp;</li>
        <li>+</li>
      </ul>
    </td>
  </tr>
  <tr>
    <td>&nbsp;</td>
    <td id="tdDigFive">
      <ul>
        <li>som:</li>
      </ul>
    </td>
    <td id="tdDigSom">
      <ul>
        <li id="sum"></li>
      </ul>
    </td>
  </tr>
</table>
</article>

<script>

var place_0 = document.getElementById("marble0");
place_0.addEventListener("click", toggle_0);
  function toggle_0() {
  place_0.classList.toggle("marble");

var digit_0 = document.getElementById("marble0").classList.contains("marble");
var nr_0 =  Number(digit_0);
document.getElementById("position0").innerHTML = nr_0;
document.getElementById("cijfer0").innerHTML = nr_0;
document.getElementById("value0").innerHTML = nr_0 * 1;
}

var place_1 = document.getElementById("marble1");
place_1.addEventListener("click", toggle_1);
  function toggle_1() {
  place_1.classList.toggle("marble");

var digit_1 = document.getElementById("marble1").classList.contains("marble");
var nr_1 =  Number(digit_1);
document.getElementById("position1").innerHTML = nr_1;
document.getElementById("cijfer1").innerHTML = nr_1;
document.getElementById("value1").innerHTML = nr_1 * 2;
}

var place_2 = document.getElementById("marble2");
place_2.addEventListener("click", toggle_2);
  function toggle_2() {
  place_2.classList.toggle("marble");

var digit_2 = document.getElementById("marble2").classList.contains("marble");
var nr_2 =  Number(digit_2);
document.getElementById("position2").innerHTML = nr_2;
document.getElementById("cijfer2").innerHTML = nr_2;
document.getElementById("value2").innerHTML = nr_2 * 4;
}

var place_3 = document.getElementById("marble3");
  place_3.addEventListener("click", toggle_3);
  function toggle_3() {
  place_3.classList.toggle("marble");

var digit_3 = document.getElementById("marble3").classList.contains("marble");
var nr_3 =  Number(digit_3);
document.getElementById("position3").innerHTML = nr_3;
document.getElementById("cijfer3").innerHTML = nr_3;
document.getElementById("value3").innerHTML = nr_3 * 8;
}

function myFunction(){
var digit_0 = document.getElementById("marble0").classList.contains("marble");
var digit_1 = document.getElementById("marble1").classList.contains("marble");
var digit_2 = document.getElementById("marble2").classList.contains("marble");
var digit_3 = document.getElementById("marble3").classList.contains("marble");
var sumArray = [];
  if (digit_0) {sumArray[0] = 0}
    else         {sumArray[0] = 1}
  if (digit_1) {sumArray[1] = 0}
    else         {sumArray[1] = 2}
  if (digit_2) {sumArray[2] = 0}
    else         {sumArray[2] = 4}
  if (digit_3) {sumArray[3] = 0}
    else         {sumArray[3] = 8}
  document.getElementById("sum").innerHTML = sumArray.reverse();
}

</script>

</body>
</html>

Share this post


Link to post
Share on other sites

I don't think you need the individual functions for each one, I think you only need the main function.  You could also just use a loop to go through the buttons and put a 1 in each array index that is selected.  You can get the sum by looping through that array, e.g.:

var sumArray = [0, 1, 1, 0, 1]; // create this yourself from the buttons
var sum = 0;
for (var i = 0; i < sumArray.length; i++) {
  if (sumArray[i]) {
    sum += Math.pow(2, i);
  }
}
console.log(sumArray.join('') + ' is ' + sum);

Share this post


Link to post
Share on other sites

Thank you for showing me this new direction of my queest. It looks to be a more elegant solution to loop trough the buttons instead of calling each one separately with a specific function.

However, as a newbie it will still take me some serious puzzling to get it working. But puzzling is the best way to learn the do's and don'ts! But if I get lost, I'll be back on the forum!

By the way, in your piece of script the sumArray in the last sentence has to be reversed to deliver the correct sum.

Edited by P. Jongejan

Share this post


Link to post
Share on other sites

Yeah, however you set up that array, that needs to be done before the code I wrote.  I only included the definition of sumArray so that code would run, but you would use just that loop after your array is ready to convert from base 2 to base 10.

Share this post


Link to post
Share on other sites

Still problems; as soon as I change btnArray[2] in the last statements of my script into btnArray[ i ], I get an error.
Please help!

<script>
toggleMarble ();
  function toggleMarble() {
    var btn0 = document.getElementById("marble0");
    var btn1 = document.getElementById("marble1");
    var btn2 = document.getElementById("marble2");
    var btn3 = document.getElementById("marble3");
    var btnArray = [btn0, btn1, btn2, btn3];
    var i = 0;
  for (i ; i < btnArray.length; i++){
  btnArray.addEventListener("click", toggleBtn);
  function toggleBtn() {
  btnArray[2].classList.toggle("marble");
  console.log(btnArray[2].classList.contains("marble"));
  }
  }
}
</script>

 

Edited by P. Jongejan
the btnArray [ i ] did italize the text

Share this post


Link to post
Share on other sites
Quote

the btnArray [ i ] did italize the text

Using '<>'(for inserting code) in the menu, or using [ code ] ...[/ code ] (without spacing) would prevent that! and make it easier to read.

[ i ]....[/ i ] and [ b ]....[/ b ] (without spaces) is for manually making plain text that is not entered in code box italize and bold.

Edited by dsonesuk

Share this post


Link to post
Share on other sites
for (i = 0; i < btnArray.length; i++){
  btnArray.addEventListener("click", (function(idx) {
    return function () {
      btnArray[idx].classList.toggle("marble");
      console.log(btnArray[idx].classList.contains("marble"));
    }
  })(i);
}

 

Share this post


Link to post
Share on other sites

Thank you for your reaction. Now I've got this script:

<script>
toggleMarble ();
  function toggleMarble() {
    var btn0 = document.getElementById("marble0");
    var btn1 = document.getElementById("marble1");
    var btn2 = document.getElementById("marble2");
    var btn3 = document.getElementById("marble3");
    var btnArray = [btn0, btn1, btn2, btn3];
    var i = 0;
for (i = 0; i < btnArray.length; i++){
  btnArray.addEventListener("click", (function(idx) {
    return function () {
      btnArray[idx].classList.toggle("marble");
      console.log(btnArray[idx].classList.contains("marble"));
    }
  })(i));                      /* I don't understand what (i) in this sentence is doing. Also I had to add a brace to get rid of a syntax error. */
 }
}
</script>

The error I still get is "btnArray.addEventListener is not a function". I've seen this error over and over again , and I don't know what to do about it. 

I feel silly ...

Share this post


Link to post
Share on other sites

You left out the 

[i]

because you're not using code boxes on the forum when pasting your code.  I just copied and pasted what you had there, I didn't notice that it was missing.  The forum thinks you're trying to make italics.  That's why we use code boxes to post code.

If you want to know what that i is doing in parentheses at the end, just take that code apart.  This code:

addEventListener("click", (function(idx) {
  return function () {
    btnArray[idx].classList.toggle("marble");
    console.log(btnArray[idx].classList.contains("marble"));
  }
})(i));

is functionally equivalent to this:

function return_listener(idx) {
  return function () {
    btnArray[idx].classList.toggle("marble");
    console.log(btnArray[idx].classList.contains("marble"));
  }
}

addEventListener("click", return_listener(i));

That's a function that returns another function.  The first version just uses an anonymous function instead of declaring the function with a name.  That's called a closure, the purpose is to run that code inside a certain scope where the value of i (or in this case idx) doesn't change.  Your first code had a problem because the loop would go through and add all of the event listeners, but the event listeners were using i.  After the loop ends i is set to whatever is 1 greater than the length of the array, so when that function runs it uses the current incorrect value of i, not whatever i was set to when you defined the listener.  The closure puts all of that in a different scope so that the value doesn't change.

Share this post


Link to post
Share on other sites

OK, message using code boxes understood. When I want to insert code, I have to start it with [ code ] and end with [ /code ] , of course without spaces.

Thank you for the explanation. Using a closure is what I was already hinting at in my first post, but I'm too much a beginner to get it working by myself, so I'm very happy with your help! But being so far as I mentioned in my forelast post, I still have this problem with that stupid error "btnArray.addEventListener is not a function". As long as that remains, the code won't be working. Can you tell me what I'm doing wrong?

Edited by P. Jongejan

Share this post


Link to post
Share on other sites

Like I said, you're missing the array index because the forum ate it.  btnArray is an array, arrays do not have addEventListener methods.  You need to access the elements in the array.

btnArray[i].addEventListener

Share this post


Link to post
Share on other sites

Yes! Now it's working. I think this project was a bit too ambitious for me as a beginner with only 1 month study from the w3c-schools tutorials ... But in the process of trying to get it fixed I learned a lot of Javascript, much more than only by following the tutorials. Now a can fill in the rest of the code, hopefully without problems.

Once again, thank you very much!

  • Like 1

Share this post


Link to post
Share on other sites

As said, the code works fine, everything OK. But in the text I want to show the base-2 version of the number. I've done that by putting a span element in the text for every single number. The problem with this is that it shows a lot of unnecessary zero's, which I tried to get rid of by reversing the array, join() it and making it a Number().The problem I encounter is the reversing method. It bounces back on the total sumArray-value instead of only the var where I want it. What's the solution? Wrap it inside a function to make it local? Or reverse the array with a negative loop and push()?

<script>
toggleMarble ();
  function toggleMarble() {
    var btn0 = document.getElementById("marble0");
    var btn1 = document.getElementById("marble1");
    var btn2 = document.getElementById("marble2");
    var btn3 = document.getElementById("marble3");
    var btn4 = document.getElementById("marble4");
    var btn5 = document.getElementById("marble5");
    var btn6 = document.getElementById("marble6");
    var btn7 = document.getElementById("marble7");
    var btn8 = document.getElementById("marble8");
    var btn9 = document.getElementById("marble9");
    var btn10 = document.getElementById("marble10");
    var btn11 = document.getElementById("marble11");
    var btnArray = [btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10, btn11];
        var sumArray = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    var i = 0;
for (i = 0; i < btnArray.length; i++){
  btnArray[i].addEventListener("click", (function(idx) {
    return function () {
      btnArray[idx].classList.toggle("marble");
            if (btnArray[idx].classList.contains("marble")){
                sumArray[idx] = 1;
            }else{ sumArray[idx] = 0;
            }
    var sum = 0;
    for (var i = 0; i < sumArray.length; i++) {
        if (sumArray[i]) {
            sum += Math.pow(2, i);
        }
        document.getElementById("s1").innerHTML = sum;
        document.getElementById("s2").innerHTML = sum;
/*console.log(sumArray.reverse());*/
        document.getElementById("demo").innerHTML = Number(sumArray.join(""));
    
    document.getElementById("posit0").innerHTML = sumArray [0];
    document.getElementById("place0").innerHTML = sumArray [0];
    document.getElementById("cijfer0").innerHTML = sumArray [0];
    document.getElementById("value0").innerHTML = sumArray [0] * Math.pow(2, 0);
    document.getElementById("posit1").innerHTML = sumArray [1];
    document.getElementById("place1").innerHTML = sumArray [1];
    document.getElementById("cijfer1").innerHTML = sumArray [1];
    document.getElementById("value1").innerHTML = sumArray [1] * Math.pow(2, 1);
    document.getElementById("posit2").innerHTML = sumArray [2];
    document.getElementById("place2").innerHTML = sumArray [2];
    document.getElementById("cijfer2").innerHTML = sumArray [2];
    document.getElementById("value2").innerHTML = sumArray [2] * Math.pow(2, 2);
    document.getElementById("posit3").innerHTML = sumArray [3];
    document.getElementById("place3").innerHTML = sumArray [3];
    document.getElementById("cijfer3").innerHTML = sumArray [3];
    document.getElementById("value3").innerHTML = sumArray [3] * Math.pow(2, 3);
    document.getElementById("posit4").innerHTML = sumArray [4];
    document.getElementById("place4").innerHTML = sumArray [4];
    document.getElementById("cijfer4").innerHTML = sumArray [4];
    document.getElementById("value4").innerHTML = sumArray [4] * Math.pow(2, 4);
    document.getElementById("posit5").innerHTML = sumArray [5];
    document.getElementById("place5").innerHTML = sumArray [5];
    document.getElementById("cijfer5").innerHTML = sumArray [5];
    document.getElementById("value5").innerHTML = sumArray [5] * Math.pow(2, 5);
    document.getElementById("posit6").innerHTML = sumArray [6];
    document.getElementById("place6").innerHTML = sumArray [6];
    document.getElementById("cijfer6").innerHTML = sumArray [6];
    document.getElementById("value6").innerHTML = sumArray [6] * Math.pow(2, 6);
    document.getElementById("posit7").innerHTML = sumArray [7];
    document.getElementById("place7").innerHTML = sumArray [7];
    document.getElementById("cijfer7").innerHTML = sumArray [7];
    document.getElementById("value7").innerHTML = sumArray [7] * Math.pow(2, 7);
    document.getElementById("posit8").innerHTML = sumArray [8];
    document.getElementById("place8").innerHTML = sumArray [8];
    document.getElementById("cijfer8").innerHTML = sumArray [8];
    document.getElementById("value8").innerHTML = sumArray [8] * Math.pow(2, 8);
    document.getElementById("posit9").innerHTML = sumArray [9];
    document.getElementById("place9").innerHTML = sumArray [9];
    document.getElementById("cijfer9").innerHTML = sumArray [9];
    document.getElementById("value9").innerHTML = sumArray [9] * Math.pow(2, 9);
    document.getElementById("posit10").innerHTML = sumArray [10];
    document.getElementById("place10").innerHTML = sumArray [10];
    document.getElementById("cijfer10").innerHTML = sumArray [10];
    document.getElementById("value10").innerHTML = sumArray [10] * Math.pow(2, 10);
    document.getElementById("posit11").innerHTML = sumArray [11];
    document.getElementById("place11").innerHTML = sumArray [11];
    document.getElementById("cijfer11").innerHTML = sumArray [11];
    document.getElementById("value11").innerHTML = sumArray [11] * Math.pow(2, 11);
        }
        }
  })(i));
 }
}
</script>

Share this post


Link to post
Share on other sites

I don't think I understand what the problem is, but it looks like the loop where you calculate the sum is too big.  That loop should only calculate the sum, not change all of those elements on the page for each element in the arrray.  You would keep reversing the array every time through the loop, which means that sumArray would keep changing.

Share this post


Link to post
Share on other sites

I want to use both the 2-base and the 10-base number of sumArray. The problem is that when I want to use the sumArray as a 2-base number, I have to reverse it first before I join the the elements of it and make it a number to get rid of the unnecessary zero's. The reversing however has to be done only for the 2-base number and not for the 10-base, which is working correctly. The problem lies in this part of the code:

/*console.log(sumArray.reverse());*/         document.getElementById("demo").innerHTML = Number(sumArray.join(""));

When I can use the 2-base number, I can get rid of half the documentGetElementById's I am using now and the leading zero's in the 2-base number.

Share this post


Link to post
Share on other sites

Did you try taking all of that code out of the loop?  Right now, this is your loop:

for (var i = 0; i < sumArray.length; i++) {
    if (sumArray[i]) {
        sum += Math.pow(2, i);
    }
    document.getElementById("s1").innerHTML = sum;
    document.getElementById("s2").innerHTML = sum;
    /*console.log(sumArray.reverse());*/
    document.getElementById("demo").innerHTML = Number(sumArray.join(""));

    document.getElementById("posit0").innerHTML = sumArray [0];
    document.getElementById("place0").innerHTML = sumArray [0];
    document.getElementById("cijfer0").innerHTML = sumArray [0];
    document.getElementById("value0").innerHTML = sumArray [0] * Math.pow(2, 0);
    document.getElementById("posit1").innerHTML = sumArray [1];
    document.getElementById("place1").innerHTML = sumArray [1];
    document.getElementById("cijfer1").innerHTML = sumArray [1];
    document.getElementById("value1").innerHTML = sumArray [1] * Math.pow(2, 1);
    document.getElementById("posit2").innerHTML = sumArray [2];
    document.getElementById("place2").innerHTML = sumArray [2];
    document.getElementById("cijfer2").innerHTML = sumArray [2];
    document.getElementById("value2").innerHTML = sumArray [2] * Math.pow(2, 2);
    document.getElementById("posit3").innerHTML = sumArray [3];
    document.getElementById("place3").innerHTML = sumArray [3];
    document.getElementById("cijfer3").innerHTML = sumArray [3];
    document.getElementById("value3").innerHTML = sumArray [3] * Math.pow(2, 3);
    document.getElementById("posit4").innerHTML = sumArray [4];
    document.getElementById("place4").innerHTML = sumArray [4];
    document.getElementById("cijfer4").innerHTML = sumArray [4];
    document.getElementById("value4").innerHTML = sumArray [4] * Math.pow(2, 4);
    document.getElementById("posit5").innerHTML = sumArray [5];
    document.getElementById("place5").innerHTML = sumArray [5];
    document.getElementById("cijfer5").innerHTML = sumArray [5];
    document.getElementById("value5").innerHTML = sumArray [5] * Math.pow(2, 5);
    document.getElementById("posit6").innerHTML = sumArray [6];
    document.getElementById("place6").innerHTML = sumArray [6];
    document.getElementById("cijfer6").innerHTML = sumArray [6];
    document.getElementById("value6").innerHTML = sumArray [6] * Math.pow(2, 6);
    document.getElementById("posit7").innerHTML = sumArray [7];
    document.getElementById("place7").innerHTML = sumArray [7];
    document.getElementById("cijfer7").innerHTML = sumArray [7];
    document.getElementById("value7").innerHTML = sumArray [7] * Math.pow(2, 7);
    document.getElementById("posit8").innerHTML = sumArray [8];
    document.getElementById("place8").innerHTML = sumArray [8];
    document.getElementById("cijfer8").innerHTML = sumArray [8];
    document.getElementById("value8").innerHTML = sumArray [8] * Math.pow(2, 8);
    document.getElementById("posit9").innerHTML = sumArray [9];
    document.getElementById("place9").innerHTML = sumArray [9];
    document.getElementById("cijfer9").innerHTML = sumArray [9];
    document.getElementById("value9").innerHTML = sumArray [9] * Math.pow(2, 9);
    document.getElementById("posit10").innerHTML = sumArray [10];
    document.getElementById("place10").innerHTML = sumArray [10];
    document.getElementById("cijfer10").innerHTML = sumArray [10];
    document.getElementById("value10").innerHTML = sumArray [10] * Math.pow(2, 10);
    document.getElementById("posit11").innerHTML = sumArray [11];
    document.getElementById("place11").innerHTML = sumArray [11];
    document.getElementById("cijfer11").innerHTML = sumArray [11];
    document.getElementById("value11").innerHTML = sumArray [11] * Math.pow(2, 11);
}

You don't need to do all of that for every element in the array.  I think you put that in the loop by mistake.  

Keep in mind also that Array.reverse is destructive, it changes the original array.  Since you're trying to do that inside the loop that's another reason why it seems that you put too much code in there.

Share this post


Link to post
Share on other sites

OK, you're right with observing the mistake I made. The code works as well when I take all the document.GetElementById's to the other side of the curly bracket at the bottom.

And the reverse-method is unusable by affecting the original array, but how do I fix this problem otherwise? I've tried to use the unshift-method in a for-loop, but couldn't get it working.

Still trying ...

Share this post


Link to post
Share on other sites

When I make a copy of sumArray and take it outside the closure, it won't be correlated to the actual input anymore. When I leave the copy inside the closure, reversing it with reverse() will affect the original as well. So I think I need to reverse it inside a function which has access to the sumArray outside of it, but keeps the result away from the original by being local. Is this correct or do I make a mistake?

Share this post


Link to post
Share on other sites
Quote

When I leave the copy inside the closure, reversing it with reverse() will affect the original as well.

That's the wrong kind of copy.  Javascript uses references for a lot of things like arrays and objects, so if you have a variable that's an array and set another variable to it, all you've done is made 2 variables that point to the same thing, that's what the reference is.  You want an actual separate copy.

It looks like you can use Array.from for that now:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from

var copy = Array.from(sumArray);

That's just a faster way to do this:

var copy = [];
	for (var i = 0; i < sumArray.length; i++) {
	  copy[i] = sumArray[i];
	}

Share this post


Link to post
Share on other sites

Yes! This works. At last the whole script does what it has to do, producing both a 2-base and a 10-base number.

The use of Array.from to make a copy is what I needed. I understood that x=sumArray wasn't the way to make a real copy and already tried to put together a piece of script like the "var copy" you gave me, but I made it much too complicated trying to use a function, and it didn't work.

Moreover, the Mozilla-site which you brought to my attention is a real goldpit of info, together with w3c-schools it gives me a nice playground to broaden my knowledge of JS.

Thank you!

Share this post


Link to post
Share on other sites

I use the MDN is the definitive Javascript reference.  If I have a question about arrays I'll search for "mdn array" to get their pages about it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×