Javascript: Changing a string in a table on click - javascript

I'm on a Javascript class (still pretty new to the language) and trying to get my head around this problem. Basically I need to add an event listener which changes a string in a <td> row to something else when clicked on.
Now in theory what I've written below should work (I think) but for some reason k[i] returns an undefined value. Below is within a new function which triggers on page load:
var k = document.getElementsByTagName("td");
for( var i = 0; i < k.length; i++ ){
k[i].addEventListener("click", function(evt) {
k[i].textContent = "Success!";
});
}
If someone could help me get my head around where I'm going wrong I'd be really grateful!

evt.target.textContent = "Success!";
Instead of :
k[i].textContent = "Success!";
Good Luck ! Demo is below 👇🏻
var k = document.getElementsByTagName("td");
for( var i = 0; i < k.length; i++ ){
k[i].addEventListener("click", function(evt) {
evt.target.textContent = "Success!"; // ⚠️ So you have to do this
});
}
<table style="width:100%">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
</table>

As Tomas said, the issue is with your variable scope, as an alternative to his answer you can use this keyword.
Here's a working fiddle.
https://jsfiddle.net/d9pzyf15/1/
HTML
<table>
<tr>
<td class="thing"> 1 </td>
<td class="thing"> 2 </td>
</tr>
</table>
JS
let tds = document.getElementsByTagName('td')
for(let i = 0; i < tds.length; i++){
tds[i].addEventListener('click', function(ev) {
this.textContent = 'Changed'
});
}
EDIT
As suggested by Abdennour in the comments, this would be the answer using ES6 arrow functions
let tds = document.getElementsByTagName('td')
for(let i = 0; i < tds.length; i++){
tds[i].addEventListener('click', (ev) => {
ev.target.textContent = 'Changed'
});
}

The problem is variable scope. When event listener is called i is different from time when event listener was created so k[i] contains undefined variable. Solution is written by Abdennour TOUMI

The problem as mentioned is scope.
You can use the target like #abdennour mentions, but sometimes these might not be available.
The easiest way to capture scope if using esnext stuff, is use let to capture the var,..
Another way if using es5, is use a self invoking anonymous function to capture..
Below are the three examples, the esNext one's is commented out. The second esnext one uses the the new for of, construct.. please note, not for in..
var k = document.getElementsByTagName("td");
for( var i = 0; i < k.length; i++ ){
(function (e) {
e.addEventListener("click", function(evt) {
e.textContent = "Success!";
});
})(k[i]);
}
/*or use esnext stuff*/
/*for( var i = 0; i < k.length; i++ ){
let e = k[i];
e.addEventListener("click", function(evt) {
e.textContent = "Success!";
});
}*/
/*or maybe use the for of.. note not for in*/
/*
for (let e of document.getElementsByTagName("td")) {
e.addEventListener("click", function(evt) {
e.textContent = "Success!";
});
}*/
<table>
<tbody>
<tr>
<td>one</td>
<td>two</td>
<td>three</td>
</tr>
</tbody>
</table>

Related

Change the background colour of a table cell if the content is not the same as another cell

I have an output.php. It creates an html table with 3 columns: Question number, Correct Answer, Student Answer. Works just as I want it to.
Now what I would like is to paint the cells with incorrect student answers red. I think Javascript is the best way to do this, rather than php.
Looking at other answers here, I was hoping this might do the job, but, alas ...
Can you please help me get this working?
<script type="text/javascript" >
function paint_cells() {
var tds = document.querySelectorAll('tbody td'), i;
for(i = 0; i < tds.length; i += 3) {
if(tds[i+1].textContent != tds[i+2].textContent){
tds[i+2].bgColor = "red";
}
}
</script>
You code working good! I think your problem occurs that your js run before the dom already loaded. You have multiple opportunities to fix this. 1) you can add your script to the bottom inside the body tag. 2) work with onload event. https://developer.mozilla.org/de/docs/Web/API/GlobalEventHandlers/onload
Note Maybe you forgot to call the function? paint_cells()
function paint_cells() {
var tds = document.querySelectorAll('tbody td'), i;
for(i = 0; i < tds.length; i += 3) {
if(tds[i+1].textContent != tds[i+2].textContent){
tds[i+2].bgColor = "red";
}
}
}
paint_cells();
<table border="1">
<thead>
<tr>
<th>Question</th>
<th>Correct Answer</th>
<th>Students Answer</th>
</tr>
</thead>
<tbody>
<tr>
<td>abc</td>
<td>right</td>
<td>false</td>
</tr>
<tr>
<td>abc</td>
<td>right</td>
<td>right</td>
</tr>
<tr>
<td>abc</td>
<td>right</td>
<td>false</td>
</tr>
</tbody>
</table>
I think you need to wait for page DOM loaded, try below. And it also depends on how and when the table in your page is generated, if it doesn't work, please provide more details.
<script type="text/javascript" >
window.addEventListener('DOMContentLoaded', (event) => {
var tds = document.querySelectorAll('tbody td'), i;
for(i = 0; i < tds.length; i += 3) {
if(tds[i+1].textContent != tds[i+2].textContent){
tds[i+2].bgColor = "red";
}
}
});
</script>

for each table push to array

I'm a beginner with code,
I'm trying to run on this table and get the text from each .winner class and push it to an Array, so instead of getting:
["aa","aa","dd"]
I'm getting
["aaaadd","aaaadd","aaaadd"]
$(document).ready(function(){
var arr = [];
var winner = $('.winner').text() ;
for ( i = 0; i < $('table').length ; i++ ) {
arr.push(winner);
}
console.log(arr);
});
HTML:
<table>
<tr>
<td>#</td>
<td class="winner">aa</td>
<td>bb</td>
<td>cc</td>
<td>dd</td>
</tr>
</table>
<table>
<tr>
<td>#</td>
<td class="winner">aa</td>
<td>bb</td>
<td>cc</td>
<td>dd</td>
</tr>
</table>
<table>
<tr>
<td>#</td>
<td class="winner">dd</td>
<td>cc</td>
<td>bb</td>
<td>aa</td>
</tr>
</table>
I guess something is wrong with my for loop
var arr = [];
$('table .winner').each(function () {
arr.push($(this).text());
})
Example
or version without class .winner
$('table').each(function () {
arr.push($(this).find('tr').eq(0).find('td').eq(1).text());
});
Example
$('table .winner') - returns 3 td's with class .winner
$(this).text() - get text from current element.
In your example $('.winner').text() returns text "aaaadd", then you get $('table').length (will be 3) and three times push the same text to arr
The sentence
var winner = $('.winner')
will give you an array of objects, so you need to loop each of them and call text() method for each one.
With this:
var winner = $('.winner').text();
You are getting a combined texts from all the td elements marked as winner (see docs here).
Then, for each table, to push this value to the array:
for ( i = 0; i < $('table').length ; i++ ) {
arr.push(winner);
}
This is actually not necessary.
What you want is probably:
var winners = $('.winner');
for (var i = 0; i < winners.length(); ++i) {
arr.push(winners.eq(i).text());
}

Sorting function in javascript

Hi I have html structure with table. I want to sort td according to their value. I trying it but cant find the logic to make it happen. My function is
<head>
<script type="text/javascript">
function sorting(){
var sortvalue= document.getElementsByTagName('td');
for(i=0; i<sortvalue.length;i++){
var val= sortvalue[i].value
}
}
</script>
</head>
<body>
<table width="500" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>5</td>
</tr>
<tr>
<td>3</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>4</td>
</tr>
<tr>
<td>1</td>
</tr>
</table>
click to sort
</body>
If you plan to do more than just organize those numbers: those saying you should use a plugin are correct. It'd take more effort than it's worth to try to make your own table sorter.
If all you want to do is sort those numbers (small to large):
function sorting() {
td = document.getElementsByTagName("td");
sorted = [];
for (x = 0; x < td.length; x++)
sorted[x] = Number(td[x].innerHTML);
sorted.sort();
for (x = 0; x < sorted.length; x++)
td[x].innerHTML = sorted[x];
}
Largest to smallest:
function sorting() {
td = document.getElementsByTagName("td");
sorted = [];
for (x = 0; x < td.length; x++)
sorted[x] = Number(td[x].innerHTML);
sorted.sort().reverse();
for (x = 0; x < sorted.length; x++)
td[x].innerHTML = sorted[x];
}
Assuming that you're putting the script under your link, or adding it on domready:
function sorting(){
var tbl = document.getElementsByTagName('table')[0];
var store = [];
for(var i=0, len=tbl.rows.length;i<len; i++){
var row = tbl.rows[i];
var sortnr = parseFloat(row.cells[0].textContent || row.cells[0].innerText);
if(!isNaN(sortnr)) store.push([sortnr, row]);
}
store.sort(function(x,y){
return x[0] - y[0];
});
for(var i=0, len=store.length; i<len; i++){
tbl.appendChild(store[i][1]);
}
store = null;
}
link here: http://jsfiddle.net/UMjDb/
For your example you can make an array of the cell data from the node list and sort that array, and then replace the cells data with the sorted data. Simpler than moving elements.
<head>
<script type= "text/javascript">
function sorting(){
var T= [], tds= document.getElementsByTagName('td');
for(var i= 0;i<tds.length;i++){
T.push(tds[i].firstChild.data);
}
T.sort(function(a, b){
return a-b
});
for(var i= 0;i<tds.length;i++){
tds[i].replaceChild(document.createTextNode(T[i]), tds[i].firstChild);
}
}
</script>
</head>
<body>
<table width= "500" border= "0" cellspacing= "0" cellpadding= "0">
<tr>
<td>5</td>
</tr>
<tr>
<td>3</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>4</td>
</tr>
<tr>
<td>1</td>
</tr>
</table>
click to sort
</body>
i giving a jquery solution, hope this post helps you.
var tdData = Array();
$(document).ready(function(){
$('td').each(function(i){
tdData [i] = $(this).text();
});
});
function sorting(){
var sortedData = tdData.sort();
$('td').each(function(i){
$(this).text(sortedData[i]);
} );
}
complete solution: link
step 1: find all td's
step 2: fetch their values
step 3: sort them on their values
step 4: put them back into their parent in the correct order. This can simply be done with an $(parent).html("").append(sortedNodes) (if you use jQuery that is).
As #FelixKling points out below, the .html("") is not strictly necessary other than for code clarity since "If you have nodes that already exist in the tree, then .append will remove them first from their current location and add them to the new parent"
You need to re-organise the table.
The simplest approach would be to use a plugin like this one for jQuery
You need to modify the DOM, they would be different way to do that, like grabbing all the data, removing all the rows and adding them back in the right order.
It could be improved better using detach and reattaching.

Javascript increment variable to update table cells onclick

I have a page where the user inputs a color and I call the onClick method to change the color of the individual cells of the table. However, when I click any cell, only the last cell (cell3 in this case) will change color. What am I doing wrong?
I get the error:
Message: 'document.getElementById(...)' is null or not an object
Line: 24
Char: 4
Code: 0
My code is:
<html>
<body>
<input type='text' id='userInput' value='yellow' />
<table border="1">
<tr>
<td id="1">cell1
</td>
</tr>
<tr>
<td id="2">cell2
</td>
</tr>
<tr>
<td id="3">cell3
</td>
</tr>
</table>
<script type="text/javascript">
for(var i = 1; i <= 3; i++){
document.getElementById(i).onclick = function(){
var newColor = document.getElementById('userInput').value;
document.getElementById(i).style.backgroundColor = newColor;
}
}
</script>
</body>
</html>
Change your HTML to this: An ID must start with an an alpha character. It is not valid to start with a number.
<table border="1">
<tr>
<td id="td1">cell1
</td>
</tr>
<tr>
<td id="td2">cell2
</td>
</tr>
<tr>
<td id="td3">cell3
</td>
</tr>
</table>
This is a very common Javascript issue: All the code shares the value of i which is 3 at the end of the loop. You can solve it by using another helper function like this:
function changeIt(i) {
// This inner function now has its own private copy of 'i'
return function() {
var newColor = document.getElementById('userInput').value;
document.getElementById("td" + i).style.backgroundColor = newColor;
}
}
for(var i = 1; i <= 3; i++){
document.getElementById(i).onclick = changeIt(i);
}
It can also be written using an anonymous function, but those are harder to read.
First of all, your for loop is wrong. Try:
for(var i = 1; i <= 3; i++) {
//code
}
Second, instead of retrieving the element each time in your loop, you could use this:
this.style.backgroundColor = document.getElementById('userInput').value;
Jeremy's answer is close but there is still a problem in that changeIt is not being called until the element is clicked, by which time the value of i is still three. Using Jeremy's update to the HTML the correct script can be written as...
function createChangeColorHandler(n) {
return function() {
var newColor = document.getElementById('userInput').value;
document.getElementById("td" + n).style.backgroundColor = newColor;
}
}
for(var i = 1; i <= 3; i++) {
// We pass i to the function createChangeColorHandler by value
// at the time of this pass of the loop rather than referencing
// the variable directly after the loop has finished
document.getElementById(i).onclick = createChangeColorHandler(i);
}
As an anonymous function...
for(var i = 1; i <= 3; i++) {
// We pass i to the function createChangeColorHandler by value
// at the time of this pass of the loop rather than referencing
// the variable directly after the loop has finished
document.getElementById(i).onclick = (function(n) {
return function() {
var newColor = document.getElementById('userInput').value;
document.getElementById("td" + n).style.backgroundColor = newColor;
}
})(i);
}
EDIT Jeremy's answer is now correct

Why would a script differ in evaluating 'element.checked' in the same script, acting on different table-rows with the same structure?

I wrote an answer to this question: Fetch content of next td on checkbox click, the answer was accepted (as of writing this question).
The intent was to find the text-value of the table-cell following the current cell that contained the input checkbox; for the second row this works (in Chrome 18/WinXP), but in the first row the evaluation console.log(that.checked); evaluates to false (regardless, so far as I can see, of it being checked or not).
The supplied HTML:
<table>
<tr>
<td>
<input type=checkbox name=t>
</td>
<td width=25%>
FOOBAR
</td>
<td width=73%>
BAZ
</td>
</tr>
<tr>
<td>
<input type=checkbox name=t>
</td>
<td width=25%>
FOO
</td>
<td width=73%>
BAR
</td>
</tr>
</table>
And my JavaScript:
var c = [];
c = window.document.getElementsByTagName('input');
for (var i = 0; i < c.length; i++) {
var that = c[i];
if (that.type == 'checkbox') {
that.onchange = function() {
console.log(that.checked);
if (that.checked){
console.log(that.parentNode.nextElementSibling.firstChild.nodeValue.trim());
}
};
}
}​
JS Fiddle demo.
Note that it seems to work reliably for the second row (and logs FOO to the console), but in the first row the console logs only false. Is there an obvious mistake I'm making?
You're actually running into an issue unrelated to checked. Your that variable is outside the scope of the event handler, and so it is always resolving to c[1]. You need to either wrap the thing in a closure (aka function () { ... }(); or just change that to this inside your event handler, like in this: http://jsfiddle.net/z88HH/3/
for (var i = 0; i < c.length; i++) {
var that = c[i];
if (that.type == 'checkbox') {
that.onchange = function() {
console.log(this.checked);
if (this.checked){
console.log(this.parentNode.nextElementSibling.firstChild.nodeValue.trim());
}
};
}
}​
Isn't that always the last row e.g. try console.log(that, that.checked) , wrap it in a closure see your edited jsFiddle
for (var i = 0; i < c.length; i++) {
if (c[i].type == 'checkbox') {
c[i].onchange = function(){
var that = c[i];
return function() {
console.log(that, that.checked);
if (that.checked){
console.log(that.parentNode.nextElementSibling.firstChild.nodeValue.trim());
}
}}();
}
}​

Categories

Resources