Remove first TD of a TR Table in javascript - javascript

I am trying to remove a td at runtime using Javascript only (no JQuery intended). Here is my table :
<table id = "tab">
<tr>
<td>
test 1
</td>
<td>
test 2
</td>
<td>
test 3
</td>
</tr>
</table>
Here is what I tryied :
function removeFirstTD() {
var element = document.getElementById("tab");
element.removeChild(element.firstChild);
}
Note that this function works and I execute it by doing this :
<body onload = "removeFirstTD();">
<!-- the tab is here -->
</body>
The problem is that it seems to erase the <tr> because I didn't scope the tr before requesting remove the td. May someone help me doing this please ?

Maybe the simplest way is to use HTMLTableElement Interface:
function removeFirstTD() {
var element = document.getElementById("tab");
element.rows[0].deleteCell(0);
}
A live demo at jsFiddle.

Use can also use querySelector. It picks the first element that matches it like this;
function removeFirstTD() {
var element = document.querySelector("#tab tr td");
element.remove();
}

Go down one more level?
element.firstChild.removeChild(element.firstChild);

Due to white space (between the table and tr, and tr and td) this creates TEXTNODES which will be the first children, you would have to be more specific:
function removeFirstTD() {
var element = document.getElementById('tab');
var firstRow = element.getElementsByTagName('TR')[0];
var firstColumn = firstRow.getElementsByTagName('TD')[0];
firstColumn.remove();
}
But you might find this simpler and more reliable:
function removeFirstTD() {
document.querySelector('#tab tr td').remove();
}

You're close. td elements belong to the tr elements (and technically the tr elements should belong to a tbody element, not the table directly). You need to get the tr elements then find their first children.
function removeFirstTD() {
var element = document.getElementById("tab");
var tr = element.getElementsByTagName('tr');
var j = tr.length;
while (j--){
tr[j].removeChild(tr[j].firstChild);
}
}
Note that firstChild might be whitespace or some other entity that is no the first td element, and you should add a check for that before removing that child.

<table class="u-full-width">
<thead>
<tr>
<strong><td>Joke</td></strong>
</tr>
</thead>
<tbody>
<tr>
<td>This is a joke</td>
</tr>
</tbody>
</table>
I'm adding this to give a step by step procedure for beginners like me looking for this answer.
Let's say above is how our table looks
First, get a DOM reference to the table body let tbody = document.querySelector('u-full-width').getElementsByTagName('tbody')
The above statements return a HTML Collection. So, we take the zero index of that collections tbody = tbody[0]
tbody.removeChild(tbody.firstElementChild) firstElementChildignores text and comment nodes

Related

Hide tr (table row) with text content

I'm trying to hide a table row with javascript and css.
I have to admit that I'm still a beginner, so it's likely that I'll ask some stupid questions.
I want to hide a table row that somewhere in a td contains the text 'banana'.
Hopefully someone can help me out, thanks!
I've tried different kinds of code I found on the internet, but can't get anything working. This is what I got so far.
if(document.getElementsByTagName('tr').contains('banana'))
{
document.getElementsByTagName('tr').style.display = 'none';
}
Problem you have is you seem to be thinking in terms of jQuery. JQuery does loops under the hood. Since you are not in jQuery world, you need to do the loops yourself over the collection.
Select the rows
loop over the rows
Read the text
Check if text as match
if it does, hide it
// select all the rows
const rows = document.querySelectorAll('tr');
// loop over the rows
rows.forEach( function (row) {
// get the text of the row
var text = row.textContent; // case insensitive use .toLowerCase()
// see if it is in the string
if (text.includes('banana')) {
// add class to hide the row
row.classList.add('hidden')
}
})
.hidden {
display: none;
}
<table>
<tbody>
<tr>
<td>apple</td><td>$1.00</td>
</tr>
<tr>
<td>pear</td><td>$1.00</td>
</tr>
<tr>
<td>banana</td><td>$1.00</td>
</tr>
<tr>
<td>strawberry</td><td>$1.00</td>
</tr>
</tbody>
<table>
How will this fail? When the text joins between cells there is no whitespace so you can make matches that are not there. Is you search for something that is partial sting in another word that can also be wrong.
Other option you have is instead of looping over the rows, you loop over the cells. And if there is a match in the td, you hide the parent.
Instead of getting tr element you can get all td element and perform the following:
var tdcollection = document.getElementsByTagName('td');
for (var i = 0; i < tdcollection.length; i++) {
if (tdcollection[i].innerText.indexOf("banana") >= 0) {
tdcollection[i].parentElement.style.display = 'none';
}
}
<table>
<tr><td>banana</td><td>test1</td></tr>
<tr><td>grape</td><td>test2</td></tr>
</table>

jQuery to select elements based on the text inside them

How to select a tag having a specific text inside it and nothing else? For example if I have the following:
<table>
<tr>
<td>Assets</td><td>Asset</td>
</tr>
<tr>
<td>Play</td><td>Players</td><td>Plays</td>
</tr>
</table>
Is there any way that I may select the <td>Asset</td> and nothing else. I tried it with contains i.e. $("table tr td:contains(Play)") but it returns all the tds in the second tr (as it should). Whereas I just want <td>Play</td>.
Is there any way to achieve this, like there's a way to select elements based on their attributes. Is there any way to select elements based on the text inside them?
How about that:
var lookup = 'Asset';
$('td:contains('+ lookup +')').filter(function() {
return $(this).text() === lookup;
});
Demo
Try before buy
Try something like this :
$("table tr td").filter(function() {
return $(this).text() === "Play";
})
If it was an input field you can specify something similar, but a little more exact with $('input[name~="Plays"]) so that it would filter out every other word, leaving the value isolated.
Other than that, the only way I know of doing this with a table is with what you had, but also throwing a conditional statement to check the text inside them.
Here is my version of accomplishing this:
http://jsfiddle.net/combizs/LD75y/3/
var play = $('table tr td:contains(Play)');
for (var i = 0, l = play.length; i < l; i++) {
if ($(play[i]).text() === "Play") {
// your script to modify below
$(play[i]).css({"color" : "red"});
}
}

Clone and append previous tr row to current table

Im having a table with 3 rows, with inputs where you can fill values in.
Now I have a link in the last tr, that says "+ More".
The "+ More" link, should if worked as expected, clone the above tr and append it above the link.
This is how far I am:
http://jsfiddle.net/9s8LH/2/
$(document).ready(function(){
$('.appendRow').bind('click', function(){
var $table = $(this).closest('table');
var $tr = $(this).closest('tr');
var $trAbove = $(this).prev('tr');
$table.append($tr.clone());
});
});
I tried to use prev('tr') to grab the TR element before the one I am inside, but it does not really work.
The second issue is that it appends under the +More TR, and not above it.
How can this be done?
The reason that $(this).prev('tr') doesn't work is that your appendRow class is on the link, not the row. There is no tr immediately prior to the link, prev looks at the previous sibling element (sibling = within the same parent) to see if it matches the selector. It doesn't scan, and it doesn't go up the hierarchy.
You're pretty close though, see comments: Updated Fiddle
$(document).ready(function(){
$('.appendRow').bind('click', function(){
// First get the current row
var $tr = $(this).closest('tr');
// Now the one above it
var $trAbove = $tr.prev('tr');
// Now insert the clone
$trAbove.clone().insertBefore($tr);
});
});
Note the use of insertBefore, which will insert the clone before the current row.
Try to use on() for dynamically added elements and you HTML should be like,
HTML
<table class="extraBookingBox">
<tr><td>Fees</td><td>Amount</td></tr>
<tr>
<td><input type="text" style="width: 40px;" name="cancelfee_price[]" />€</td>
<td><input type="text" style="width: 40px;" name="cancelfee_price[]" />€</td>
</tr>
<tr>
<td colspan="2">+ More</td>
</tr>
</table>
SCRIPT
$(document).ready(function(){
$(document).on('click','.appendRow', function(){
var $tr = $('table tr:nth-child(2)').clone();
console.log($tr);
$tr.insertBefore($('.appendRow').closest('tr'));
});
});
Demo

How to modify HTML table with JavaScript to increase rowspan, without causing reflow?

To change HTML table so that some cell has given rowspan="n" (to increase rowspan) you have to delete n cells below the one that is getting extended.
The original HTML source (HTML structure) looks like this
<table border="1">
<tr id="1"><td>1</td><td>Zubenelgenubi</td></tr>
<tr id="2"><td>2</td><td>Algorab</td></tr>
<tr id="3"><td>3</td><td>Almach</td></tr>
<tr id="4"><td>4</td><td>Alula_Borealis</td></tr>
<tr id="5"><td>5</td><td>Rigil_Kentaurus</td></tr>
<tr id="6"><td>6</td><td>Menkent</td></tr>
</table>
and I like to transform it to something like this:
<table border="1">
<tr id="1"><td>1</td><td>Zubenelgenubi</td></tr>
<tr id="2"><td>2</td><td>Algorab</td></tr>
<tr id="3" rowspan="3"><td>3</td><td>Almach</td></tr>
<tr id="4"> <td>Alula_Borealis</td></tr>
<tr id="5"> <td>Rigil_Kentaurus</td></tr>
<tr id="6"><td>6</td><td>Menkent</td></tr>
</table>
Unfortunately SO formatting doesn't support tables, neither in Markdown, nor in HTML.
Is it possible to do it without causing unnecessary reflow? I mean here something better than simply
for (var i = 0; i < numlines; i++) {
...
if (i === 0) {
td.rowSpan = numlines;
...
} else {
tr.deleteCell(0); // or td.parentNode.removeChild(td);
}
}
which I think causes reflow after each iteration.
When adding elements one can use DocumentFragment; what to do when modifying number of elements at once?
Edit: added 03-05-2011
A solution using Range object (the W3C DOM version)
var range = document.createRange();
range.setStartBefore(document.getElementById(start+''));
range.setEndBefore(document.getElementById(start+numlines+''));
var fragment = range.cloneContents();
for (var i = 0; i < numlines; i++) {
var rownum = start + i;
var row = fragment.getElementById(rownum.toString()); // not always work
var td = row.firstChild;
if (i === 0) {
td.style.backgroundColor = 'yellow';
td.rowSpan = num.toString();
} else {
td.parentNode.removeChild(td);
}
}
range.deleteContents();
var rowAfter = document.getElementById(start+num+'');
rowAfter.parentNode.insertBefore(fragment, rowAfter);
Note that for some reason fragment.getElementById didn't work for me, so I had to cheat knowing what nodes are there.
deleteContents + insertBefore is needed because table.replaceChild(range, fragment); does not work, unfortunately (where table is element from which range was extracted).
Try to make the table element display:none
before the loop and restore the
display after.
Another option would be to assign
fixed dimensions and use
overflow:hidden for the the time of loop body.
This should isolate update tree by
the table only. Theoretically.
And the last is to compose HTML of
the table and replace the table as
whole - this will be made in single
transaction.

How to get a cell's location

I'm writing a script which manages a very large table. When a user clicks a table cell, I would like to know which cell they clicked.
e.g
--------------
| | |
| | Click|
--------------
should give me a cell reference of (1, 1).
Any way I could do this with javascript. The page it's running on uses jquery for other purposes, so any jquery based solutions are good aswell.
EDIT: To clarify, the top left cell is (0, 0). For performance reasons, the event needs to bound to the table, not the tds.
This is done very easily using the target property of the event object:
$('#mytable').click(function(e) {
var tr = $(e.target).parent('tr');
var x = $('tr', this).index(tr);
var y = tr.children('td').index($(e.target));
alert(x + ',' + y);
});
This approach allows you to only bind 1 event handler to the entire table and then figure out which table cell was clicked. This is known as event delegation and can be much more efficient in the right situation, and this one fits the bill. Using this you avoid binding an event to each <td>, and it does not require hard-coding coordinates. So, if your table looks like this:
<table id='mytable'>
<tr>
<td>hi</td>
<td>heya</td>
</tr>
<tr>
<td>boo</td>
<td>weee</td>
</tr>
</table>
It will alert the coordinates on click. You can do whatever with that. :)
If you find performance to be too slow (depending on just how large your table is) you would then have to resort to hardcoding or a combination of the two, maybe only hard coding the <tr> index, as that would be the slowest to get, and then getting the <td> index dynamically. Finally, if all this coordinate business is completely unnecessary and what you really just wanted was a reference to the clicked <td>, you would just do this:
$('#mytable').click(function(e) {
var td = $(e.target);
// go crazy
});
$('td').click(function(event) {
var row = $(this).parent('tr');
var horizontal = $(this).siblings().andSelf().index(this);
var vertical = row.siblings().andSelf().index(row);
alert(horizontal+','+vertical);
});
Walk the DOM hierarchy...
You should be able to do this by obtaining a reference to the DOM node for the clicked TD, then walking the previousSibling chain backwards until you reach the first column, counting as you go.
One you're at the start of that sibling chain (i.e., the first TD in the row), the parentNode should be the TR node, and with that you can perform a similar walk through previousSibling TRs to count up the number of rows.
..or tag each cell with additional attributes
Alternatively, when you create the table, add some fake rowidx="??" and colidx="??" attributes to each cell and retrieve these with getAttribute. If you want to use a legal attribute, you could put the row and column index in an axis="??,??" attribute.
You mentioned a very large table - it would be unwise to bind the click to every td. A better approach is to use the .live method, if your still on jquery 1.2.6 you can just bind a click event to the table and use event delegation. if you need code for this ask.
$("#tableId td").live('click', function () {
var $this = $(this);
var x = $this.prevAll().length;
var y = $this.parent().prevAll.length;
console.log(x + ", " + y);
});
assuming your 1st row, 1st column to be 0,0
var row = 0;
$('#tblImages > tbody > tr').each( function(){
var col = 0;
$(this).children('td').each( function(){
$(this).attr("currentRow", row).attr("currentCol", col);
col++;
});
row++;
}
);
$('#tblImages > tbody > tr > td').click(function(){
alert( $(this).attr("currentRow") + " " + $(this).attr("currentCol"));
This can certainly be improved more..but its working
window.onload = function () {
document.getElementsByTagName('table')[0].addEventListener('click', function(element) {
var rowIndex = element.target.parentElement.rowIndex;
var cellIndex = element.target.cellIndex;
document.getElementById('alert').innerHTML = ('Row index = ' + rowIndex + ', Column index = ' + cellIndex);
}, false);
}
tr, th, td {
padding: 0.6rem;
border: 1px solid black
}
table:hover {
cursor: pointer;
}
<table>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
<td>6</td>
</tr>
</tbody>
</table>
<p id="alert"></p>

Categories

Resources