0

I am trying to make a game where there is a grid of n*n and all the grids contain an input. How do I bind it in a way that only one character can stay per box?

<script>
  let boxes = [
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""]
  ];

  function isValid(keyCode) {
    if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 97 && keyCode <= 122)) {
      return true;
    }
    return false;
  }
</script>
<main>
  <div class="root">
    {#each boxes as row, i}
    <div class="row">
      {#each row as column, j}
      <span class="column">
        <input
          class="col-input"
          type="text"
          bind:value="{boxes[i][j]}"
        />
      </span>
      {/each}
    </div>
    {/each}
  </div>
</main>

1 Answer 1

1

To make sure only a single character can be inside each input at a time you can use a keydown event on the input and preventDefault on the handler so that it doesn't double enter into the field. Then pass on some information about the key event to your function and set the value of the input to be the value of the current keyCode passed through by the event inside the function.

I see you're looking to limit it to a specific set of keycode's so you can take care of that in this function as well.

<script>
  let boxes = [
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""]
  ];

    function isValid(val, i, j) {
      if ((val.keyCode >= 65 && val.keyCode <= 90) || (val.keyCode >= 97 && val.keyCode <= 122)) {
        boxes[i][j] = val.key;
        return true;
      }
      return false;
    }

</script>

<main>
  <div class="root">
    {#each boxes as row, i}
    <div class="row">
      {#each row as column, j}
      <span class="column">
        <input
          class="col-input"
          type="text"
          bind:value="{boxes[i][j]}"
          on:keydown|preventDefault={e => isValid(e, i, j)}
        />
      </span>
      {/each}
    </div>
    {/each}
  </div>
</main>

Here's an example in the REPL

EDIT: If tab functionality needs to be preserved (assuming this is not a point and click game, then some extra functionality will need to be added since preventDefault kills the tab key in this instance.

This can be done by changing bind:value to bind:this and all references that would change the boxes' value will need to be changed to boxes[i][j].value.

I added some additional functionality to handle the arrow keys and preserve tab key function while keeping focus inside the grid (usually tab would want to jump out of window)

<script>
  let boxes = [
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""],
    ["", "", "", "", "", "", "", ""]
  ];

    function isValid(val, i, j) {
        const width = boxes[i].length, height = boxes.length, prevCol = j - 1, nextCol = j + 1, prevRow = i - 1, nextRow = i + 1, left = 37, up = 38, right = 39, down = 40, tab = 9;

        // Preserve tab functionality, 
        // loop to first input of next row 
        // when end of row is reached or to 
        // first row and column when end of 
        // matrix is reached
        if (val.keyCode == tab) {
            nextCol != width? boxes[i][nextCol].focus() : i != height - 1? boxes[nextRow][0].focus() : boxes[0][0].focus();
            return;
        }

        // Loop around single row with right and left arrows
        if (val.keyCode == right) {
            nextCol != width? boxes[i][nextCol].focus() : boxes[i][0].focus();
            return;
        }
        if (val.keyCode == left) {
            j != 0? boxes[i][prevCol].focus() : boxes[i][width - 1].focus();
            return;
        }

        // loop around single column with up and down arrows
        if (val.keyCode == up) {
            i != 0?  boxes[prevRow][j].focus() : boxes[height - 1][j].focus();
            return;
        }

        if (val.keyCode == down) {
            i != height - 1?  boxes[nextRow][j].focus() : boxes[0][j].focus();
            return;
        }


        if ((val.keyCode >= 65 && val.keyCode <= 90) || (val.keyCode >= 97 && val.keyCode <= 122)) {
          boxes[i][j].value = val.key;
          return true;
        }
        return false;
    }

</script>

<main>
  <div class="root">
    {#each boxes as row, i}
    <div class="row">
      {#each row as column, j}
      <span class="column">
        <input
          class="col-input"
          type="text"
          bind:this="{boxes[i][j]}"
          on:keydown|preventDefault={e => isValid(e, i, j)}
        />
      </span>
      {/each}
    </div>
    {/each}
  </div>
</main>

Here is the link to that REPL example

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.