最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

JavaScript to add table row onclick events - Stack Overflow

programmeradmin3浏览0评论

I'm new to Javascript. I want to add onclick events to table rows. I'm not using JQuery.

I loop thru the rows and use a closure to make sure I have the state of the outer function for each row. The looping works. Using alerts, I see the function being assigned for each iteration. But when I click the row, no alert is displayed. Below is the HTML and code that can be loaded.

Why are the table row events not working?

function example4() {
  var table = document.getElementById("tableid4");
  var rows = table.getElementsByTagName("tr");
  for (var i = 0; i < rows.length; i++) {
    var curRow = table.rows[i];
    //get cell data from first col of row
    var cell = curRow.getElementsByTagName("td")[0];
    curRow.onclick = function() {
      return function() {
        alert("row " + i + " data=" + cell.innerHTML);
      };
    };
  }
}

function init() {
  example4();
}
window.onload = init;
Use loop to assign onclick handler for each table row in DOM. Uses Closure.
<table id="tableid4" border=1>
  <tbody>
    <tr>
      <td>Item one</td>
    </tr>
    <tr>
      <td>Item two</td>
    </tr>
    <tr>
      <td>Item three</td>
    </tr>
  </tbody>
</table>

I'm new to Javascript. I want to add onclick events to table rows. I'm not using JQuery.

I loop thru the rows and use a closure to make sure I have the state of the outer function for each row. The looping works. Using alerts, I see the function being assigned for each iteration. But when I click the row, no alert is displayed. Below is the HTML and code that can be loaded.

Why are the table row events not working?

function example4() {
  var table = document.getElementById("tableid4");
  var rows = table.getElementsByTagName("tr");
  for (var i = 0; i < rows.length; i++) {
    var curRow = table.rows[i];
    //get cell data from first col of row
    var cell = curRow.getElementsByTagName("td")[0];
    curRow.onclick = function() {
      return function() {
        alert("row " + i + " data=" + cell.innerHTML);
      };
    };
  }
}

function init() {
  example4();
}
window.onload = init;
Use loop to assign onclick handler for each table row in DOM. Uses Closure.
<table id="tableid4" border=1>
  <tbody>
    <tr>
      <td>Item one</td>
    </tr>
    <tr>
      <td>Item two</td>
    </tr>
    <tr>
      <td>Item three</td>
    </tr>
  </tbody>
</table>

Share Improve this question edited Dec 28, 2023 at 8:10 mplungjan 178k28 gold badges181 silver badges240 bronze badges asked Dec 26, 2012 at 17:58 Alex_BAlex_B 1,6615 gold badges17 silver badges26 bronze badges 4
  • 1 You're returning a function within a function. This would require for another function within the onclick to call the returned function. – jeremy Commented Dec 26, 2012 at 18:00
  • This question really has nothing to do with closures. – Evan Davis Commented Dec 26, 2012 at 18:06
  • @Mathletics: I'm using closures because I want the event to return the row that was clicked. On an earlier version of the code, I kept getting row 3 always being returned. My understanding is that closures will solve this problem. – Alex_B Commented Dec 26, 2012 at 18:13
  • You understand correctly – mplungjan Commented Dec 26, 2012 at 18:38
Add a ment  | 

3 Answers 3

Reset to default 10

Task

Use loop to assign onclick handler for each table row in DOM. Use Closure.

function example4() {
  let table = document.getElementById("tableid4");
  let rows = table.rows;

  for (let i = 0; i < rows.length; i++) {
    // Using a closure in a modern way
    ((row, index) => {
      row.addEventListener('click', () => {
        alert(`row ${index} data=${row.querySelector('td').textContent}`);
      });
    })(rows[i], i);
  }
}
window.addEventListener('DOMContentLoaded', example4);
<table id="tableid4" border="1">
  <tbody>
    <tr>
      <td>Item one</td>
    </tr>
    <tr>
      <td>Item two</td>
    </tr>
    <tr>
      <td>Item three</td>
    </tr>
  </tbody>
</table>

However with let there is no need for a closure

function example4() {
  let table = document.getElementById("tableid4");
  let rows = table.rows;

  for (let i = 0; i < rows.length; i++) { // the "let" does the job
    rows[i].addEventListener('click', function() {
      alert(`row ${i} data=${this.querySelector('td').textContent}`);
    });
  }
}
window.addEventListener('DOMContentLoaded', example4);
<table id="tableid4" border="1">
  <tbody>
    <tr>
      <td>Item one</td>
    </tr>
    <tr>
      <td>Item two</td>
    </tr>
    <tr>
      <td>Item three</td>
    </tr>
  </tbody>
</table>

This brings me to a pet peeve of mine: looping eventListeners

If we delegate, the script is simpler and easier to read

const items = ["Item one", "Item two", "Item three"];

window.addEventListener('DOMContentLoaded', () => {
  // create a table using an array. This can be as plex as needed depending on the data.
  document.getElementById('container').innerHTML =
    `<table id="tableid4" border="1">
      <tbody>
        ${items
          .map((item,i) => `<tr data-idx="${i}"><td>${item}</td></tr>`)
          .join('')}
      </tbody>
    </table>`;


  document.querySelector('#tableid4 tbody')
    .addEventListener('click', (e) => {
      const tgt = e.target.closest('tr');
      if (!tgt) return; // not clicking in a row
      alert(`row ${tgt.dataset.idx} data=${tgt.querySelector('td').textContent}`);
    });
});
<div id="container"></div>


This was the canonical way in 2012

By using a closure (function() {...})(i), you're creating a new scope where the current value of i is passed and preserved in cnt for each iteration. This way, each click handler retains its own separate cnt value, which corresponds to the row index at the time the handler was created.

function example4() {
    var table = document.getElementById("tableid4");
    var rows = table.rows; // or table.getElementsByTagName("tr");
    for (var i = 0; i < rows.length; i++) {
        rows[i].onclick = (function() { // closure
            var cnt = i; // save the counter to use in the function
            return function() {
              alert("row"+cnt+" data="+this.cells[0].innerHTML);
            }    
        })(i);
    }
}
window.onload = function() { example4(); }​

@ParkerSuperstar suggested that the i in (i) is not needed.

for (var i = 0; i < rows.length; i++) {
    rows[i].onclick = (function() {
        var cnt = i; 
        return function() {
            alert("row" + cnt + " data=" + this.cells[0].innerHTML);
        }    
    })();
}

That is because the closure (function() {...})() is immediately invoked at each iteration of the loop. This creates a new execution context for each iteration.

Within this closure, var cnt = i; captures the current value of i for each iteration. Because a new closure is created for each iteration of the loop, each closure has its own execution context, and thus its own cnt variable.

I'm not quite sure why you're using a closure here, could you be a bit more elaborate?

The reason you're not seeing the desired alert is because within the onclick function, you're returning another function. I.e:

window.onload = function() {
    return function() {
        alert("Closure... why?");
    };
};

Something like this won't really work because you're never calling the nested function... try it without using the closure, or ment explaining why you want a closure because you're explanation didn't make much sense to me.

You just have to remove an extra function and script will be like this

<script>
 function example4() {
    var table = document.getElementById("tableid4");
cells = table.getElementsByTagName('td');

 for (var i=0,len=cells.length; i<len; i++){
 cells[i].onclick = function(){
    alert(this.innerHTML);

}
 }
}
 function init() { example4(); }
 window.onload = init;   
 </script>
发布评论

评论列表(0)

  1. 暂无评论