skip to Main Content

I have created a HTML table where every row retrieves data from an sql database table.

?>
<div class="container">
    <h2>Activities</h2>
    <table class="table table-striped">
    <thead>
        <tr>
        <th scope="col">#</th>
        <th scope="col">Date</th>
        <th scope="col">Position</th>
        <th scope="col">Activity Type</th>
        <th scope="col">Edit</th>
        </tr>
    </thead>
    <tbody>
        <?php
        $incremental=0;
        while($selRow=$res_selPostData->fetch_assoc()) {
            $incremental +=1;
            $filepath='.outputs\' . 'verbale' . $selRow['IDIntervento'] . '.pdf'; 
        ?>
            <tr>
                <td><?php echo $selRow['IDActivity']; ?></td>
                <td><?php echo $selRow['DateActivity']; ?></td>
                <td><?php echo $selRow['Position']; ?></td>
                <td><div id=<?php echo "TypeAct" . $incremental; ?>><?php echo $selRow['Activity_type']; ?></div></td>
                <td><div id=<?php echo "button" . $incremental; ?>><span id='edit'>EDIT</span></div></td>
            </tr>

        <?php
        }
        ?>
    </tbody>
    </table>
</div>

The last column of the HTML table contains for each row a ‘button’ which is connected to the following javascript function.

<script>
    window.onload = function() {
                var maximum=parseInt(<?php echo $incremental; ?>);
                for (let i = 1; i<maximum; i++) {
                    var makeMod = document.getElementById('TypeAct' + 'i');
                    var div = document.getElementById('button' + 'i');
                    div.onclick = function(e) {
                        makeMod.contentEditable = true;
                        makeMod.focus();
                        makeMod.style.backgroundColor = '#E0E0E0';
                        makeMod.style.border = '1px dotted black';
                    }
                }
            }
</script>

I had tried using the script without the for loop with

<td><div id=<?php echo "button"; ?>><span id='edit'>EDIT</span></div></td>

and

var makeMod = document.getElementById('TypeAct')` and `var div = document.getElementById('button')

but in that case the function works only for one row.

Changing ‘i’ to i solved the problem with the ‘button’ but when i click the button in different rows only the activity_type in the last row becomes editable and not the one in the same row as the button; as if the function only acts on the last generated row in the while loop of the table. Can you guess why?

3

Answers


  1. On this line:

    var div = document.getElementById('button' + 'i');
    

    the ‘i’ is in quotes, thus not getting the calculated value but literally the letter "i". Instead, try:

    var div = document.getElementById('button' + i);
    

    and ideally use a hyphen between the word button and the increment variable like:

    id="button-1" id="button-2" etc.
    
    Login or Signup to reply.
  2. Im using a sample HTML with sample data I made just to test the code, since PHP can’t be reproduced in here so.

    1. First thing first, replace the string i with the variable (which you already did)

      makeMod = document.getElementById('TypeAct-' + i);

    2. This line of code is inside a loop. <span id='edit'>EDIT</span> which means you will have multiple elements with the edit id. Can’t have that so change it like you did with the other ids. You can concatenate your php variable $incremental like you did with the others.

    3. Change your loop to this for (let i = 1; i <= maximum; i++) { if you leave it to i < maximum then it won’t add the event to your last button.

    4. It is much better to use let and const instead of var now. More info here

    5. Use addEventListener instead of onclick. Why? read more about it here addEventListener-vs-onclick

    window.onload = function() {
      let maximum = 3;//sample value, leave this line like in your original code
      let makeMod;
      for (let i = 1; i <= maximum; i++) {
        document.getElementById('button-' + i).addEventListener("click", function(e) {
          makeMod = document.getElementById('TypeAct-' + i);
          makeMod.contentEditable = true;
          makeMod.focus();
          makeMod.style.backgroundColor = '#E0E0E0';
          makeMod.style.border = '1px dotted black';
        });
      }
    }
    <div class="container">
      <h2>Activities</h2>
      <table class="table table-striped">
        <thead>
          <tr>
            <th scope="col">#</th>
            <th scope="col">Date</th>
            <th scope="col">Position</th>
            <th scope="col">Activity Type</th>
            <th scope="col">Edit</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>some data</td>
            <td>some data</td>
            <td>some data</td>
            <td>
              <div id="TypeAct-1">act 1</div>
            </td>
            <td>
              <div id="button-1"><span id='edit-1'>EDIT</span></div>
            </td>
          </tr>
          <tr>
            <td>some data</td>
            <td>some data</td>
            <td>some data</td>
            <td>
              <div id="TypeAct-2">act 2</div>
            </td>
            <td>
              <div id="button-2"><span id='edit-2'>EDIT</span></div>
            </td>
          </tr>
          <tr>
            <td>some data</td>
            <td>some data</td>
            <td>some data</td>
            <td>
              <div id="TypeAct-3">act 3</div>
            </td>
            <td>
              <div id="button-3"><span id='edit-3'>EDIT</span></div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    Login or Signup to reply.
  3. It would be better, IMO, to remove the ID attributes entirely if you need to use a numerical suffix to help identify elements and rely on other, more flexible means of identifying DOM elements, such as querySelector, querySelectorAll and other parent/child/sibling type properties & methods. In the above the only use that the PHP variable $incremental has is as that numeric suffix – it would play no part in whatever actual edit operation (Form submission/ ajax etc) when the record is to be edited so could be removed. The table is generated with content from a database so you have an IDActivity which will be useful in an edit operation so assign that as the data-id value!

    The identifying of click events can be done using a delegated event listener rather than assigning an event listener to each button.

    I also added a blur event handler to the contentEditable DIV elements that returns them to their normal state when no longer used. That blur handler could be used to fire off the ajax request to edit the record in db.

    const d=document;
    const CN='editable';
    
    d.querySelector('.container table').addEventListener('click', e=>{
      if( e.target instanceof HTMLSpanElement && e.target.dataset.type == 'edit' ) {
        let id=e.target.parentNode.dataset.id;
        let content = e.target.parentNode.closest('tr').querySelector('div[data-type="content"]');
            content.contentEditable=true;
            content.focus();
            content.classList.add( CN );
            
        console.log( ' -> Id=%s',id )
      }
    });
    
    d.querySelectorAll('[data-type="content"]').forEach(n=>n.addEventListener('blur',e=>{
      if( e.target.classList.contains( CN ) ){
        e.target.classList.remove( CN );
        e.target.contentEditable=false;
        
        // update using ajax?
        console.log(e.target.dataset.id,e.target.textContent);
      }
    }));
    .editable{
      background:#E0E0E0;
      border:1px dotted black
    }
    <div class="container">
      <h2>Activities</h2>
      <table class="table table-striped">
        <thead>
          <tr>
            <th scope="col">#</th>
            <th scope="col">Date</th>
            <th scope="col">Position</th>
            <th scope="col">Activity Type</th>
            <th scope="col">Edit</th>
          </tr>
        </thead>
        <tbody>
          <!-- emulated content -->
          <!--
            The `IDActivity` value is what would be assigned
            to the `data-id` attribute below. That `data-id`
            is easily accessible in Javascript and can be used
            for whatever EDIT operation you wish.
            
            ie: 
            <td>
              <div data-id="<?=$selRow['IDActivity'];?>">
                <?=$selRow['Activity_type'];?>
              </div>
            </td>
          -->
          <tr>
            <td>1</td>
            <td>2023/04/15</td>
            <td>Midfield</td>
            <td><div data-id=1 data-type='content'>Banana Bouncing</div></td>
            <td><div data-id=1><span data-type='edit'>EDIT</span></div></td>
          </tr>
          <tr>
            <td>2</td>
            <td>2023/04/17</td>
            <td>Ground-Level</td>
            <td><div data-id=2 data-type='content'>Hedgehog Juggling</div></td>
            <td><div data-id=2><span data-type='edit'>EDIT</span></div></td>
          </tr>
          <tr>
            <td>3</td>
            <td>2023/04/18</td>
            <td>Upstairs</td>
            <td><div data-id=3 data-type='content'>Dwarf Tossing</div></td>
            <td><div data-id=3><span data-type='edit'>EDIT</span></div></td>
          </tr>
          <!-- end emulated content -->
        </tbody>
      </table>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search