Search code examples
javascriptgetelementbyidcreateelement

getElementByID doesn't work when the element is created


I use javascript to make a table that have rows and columns based on the number of cells of a matrix. I'll try to use a function, but it doesn't recognize the ID that was be created.

<!DOCTYPE html>
<html lang="en">
  <head >
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="src/style.css">
  </head>
  <body onload="tableCreate()">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>

    <script src="src/script.js"></script>
  </body>
</html>

the first part creates the table, the second part when enter key was released increment the cursor, the third is if the table doesn't contain other rows and the enter key was pressed in the last cell it created a new row

var c = [
  ['casa', 'casa1', 'casa2'],
  ['casa3', 'casa4', 'casa5'],
  ['casa6', 'casa7', 'casa8'],
  ['casa9', 'casa10', 'casa11'],
];
function tableCreate() {
  const body = document.body,
    tbl = document.createElement('table');
  tbl.setAttribute('id', 'tab_1');
  tbl.style.width = '100px';
  tbl.style.border = '1px solid red';

  for (let i = 0; i < c.length; i++) {
    const tr = tbl.insertRow();
    for (let j = 0; j < 3; j++) {
      const td = tr.insertCell();
      var x = document.createElement('INPUT');
      x.setAttribute('type', 'number');
      x.setAttribute('class', 'username');
      td.appendChild(x);
      td.style.border = '2px solid yellow';
    }
  }
  document.body.appendChild(tbl);
}


$('.username').focus(function (event) {
  console.log('gd');
});



var currentBoxNumber = 0;
$('.username').keyup(function (event) {
  if (event.keyCode == 13) {
    textboxes = $('input.username'); 
    console.log('o');
    currentBoxNumber = textboxes.index(this);
    if (textboxes[currentBoxNumber + 1] != null) {
      nextBox = textboxes[currentBoxNumber + 1];
      nextBox.focus();
      nextBox.select();
      event.preventDefault();
    } else {
     
      const bd = document.body,
      tbl = document.getElementById('tab_1');
      
      const tr = tbl.insertRow();
      for (let j = 0; j < 3; j++) {
        const td = tr.insertCell();
      var x = document.createElement('INPUT');
        x.setAttribute('type', 'number');
        x.setAttribute('class', 'username');
        td.style.border = '2px solid green';
        td.appendChild(x); 
      }
      bd.appendChild(tbl);
    }
    return false;
  }
});

anyone can help me? thanks

i try to use the function whit some keys, but not recognized the ID


Solution

  • The problem here isn't that getElementById isn't finding the element by the ID, it's that your event binding is occurring before the elements it's binding to exist.

    The order of events you have in your script is:

    1. Create tableCreate function.
    2. Bind focus event to .username elements.
    3. Bind keyup event to .username elements.
    4. On document onload execute tableCreate()

    The .username elements are created in the tableCreate function which is executed after the events are bound.

    When binding events, you're basically saying (to jQuery): get me all the .username elements, and bind this event to them. But because those elements don't exist yet, nothing is bound.

    There's two ways to fix this in your script, either:

    1. Execute tableCreate() before binding your events.

      • This will ensure the elements are created first.
    2. Bind the event listeners to the document and use jQuery's context to filter the events to just .username elements (preferred)

    Point #2 would be simpler to use as you're binding a single event listener, and it will handle events to elements added to the DOM after the listener is bound (since document is always there).

    Example:

    // Bind Target  Context
    // |            |   
    // v            v
    $(document, '.username').focus(...);
    
    $(document, '.username').keyup(...);
    

    Update 2023-01-15

    As in the original answer, when you are binding the keyup handler, you binding to the .username elements available at the time the binding occurred.

    Any new .username elements which are added after that statement will not have the bindings.

    Per my #2 suggestion above, you can bind the event listener to document and provide jQuery a context filter for which to execute the listener for.

    For completeness, I've taken your code and updated it with this context filter, and also simplified your listener to allow it to shift the focus to the next row even after it has been added.

    // Bind listener to document and filter by `.username`.
    $(document, '.username').keyup(function (event) {
      if (event.keyCode !== 13) return; // Quick exit if not Enter key
    
      event.preventDefault(); // Prevent default straight away
    
      let textboxes = $('.username'); // Use `let` as we'll re-assign if we add more rows
      console.log('Current textboxes.length', textboxes.length);
    
      const currentBoxNumber = textboxes.index(event.target); // Search using `event.target` because `this` is the `document` as it's the element the listener is bound to, but the event's target is sourced from the `.username` element
    
      const nextBoxIndex = currentBoxNumber + 1;
      const hasNextCell = Boolean(textboxes[nextBoxIndex]); // false if no cell found
    
      if (!hasNextCell) {
        add_row(); // Create the next row if there isn't the next cell
        textboxes = $('.username'); // Query for textboxes again since we've added some more
        console.log('Updated textboxes.length', textboxes.length);
      }
    
      const nextBox = textboxes[nextBoxIndex];
      nextBox.focus();
    });
    
    // Bind to document and filter to `.username`
    $(document, '.username').focus(function (event) {
      console.log('on_focus');
    });