JavaScript cloneNode: SELECT keeping some undesirable binding - javascript

So, I have a simple table containing input elements and I need to dynamically add new rows to it.
One of the cells contains a SELECT element and a list of OPTIONS which are replicated with cloneNode().
The problem is: when I change any of the replicated SELECT elements, the index of the original SELECT also changes to that same value, like if there was some sort of "binding" left behind by the cloning process.
My table looks something like this:
<table>
<tr>
<th>Header</th>
</tr>
<tr>
<td>
<select>
<option>Options</option>
</select>
</td>
</tr>
</table>
The cloning routine is relatively complex, as I have to change cell IDs, element names and other things, but it boils down to something like this:
var table = document.querySelector('#table');
var rows = table.querySelectorAll('tr');
for (var x = 0; x < 5; x++)
{
row = rows[1].cloneNode(true);
// Changes everything that needs to be changed
table.appendChild(row);
}
Does anyone know what could be causing the original SELECT to be "bound" to the replicated ones?
Thanks in advance!

Thank you for all the comments! It turned out to be a simple mistake: I had an event added to the node via addEventListener() which was firing and updating the original SELECT to the same index of the replicated one.

Related

Apply selector to elements that have already been selected

I feel like I am asking something really dumb but perhaps it's not my day. If I have a selected element already, e.g:
let tables = $('table');
But now I want to apply another selector like .some-class on top of those tables, but without creating a new jQuery object like this
$('table.some-class')
How would I do it?
You need to use .filter() to adding filter to variable selector.
let tables = $('table');
tables = tables.filter('.some-class');
tables.css('color', 'red');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr>
<td>table</td>
</tr>
</table>
<table class="some-class">
<tr>
<td>table has .some-class</td>
</tr>
</table>
With the following code you would find all elements with class .some-class under the elements with tag name table.
let tables = $('table').find('.some-class');
Of course, it is just for the example, otherwise you can simply do:
let tables = $('table .some-class');
In your question it is not clear if you want children elements or just filter the elements. If you want the tables with a given class, you would do:
let tables = $('table').filter('.some-class');

Use Jquery to find the parent table, of a table, of a td

I am customising Sage CRM, so I have no control over the HTML that is written and can't add IDs or class's to the table layouts the CRM spits out. I want to hide a higher (not top) level table based on a users selection of a select dropdown. I can only get a jQuery selector hooked onto the title row of a table within the table I want to hide.
The DOM goes something like:
//Lots of other table structures above this in the DOM....
<table> <---- this is the table I want to show or hide based on the users selection
<tbody>
<tr>
<td>
<table>
<tbody>
<tr>
<td class="PANEREPEAT"> <---- this is the node I can get selector to
Valuation information
////
So I do the below client side javascript:
var val_information_screen;
$('.PANEREPEAT').filter(function () {
//Find the valuation information screen
return $(this).text() == 'Valuation information';
}).each(function () { //iterate through all of these (there should only be one!)
val_information_screen = $(this);
});
var sel_ofee_type = $('#ofee_type');
if (sel_ofee_type.val() == '006') {
val_information_screen.closest('table').parents("table:first").show();
} else {
val_information_screen.closest('table').parents("table:first").hide();
}
It does work, it just is not particularly beautiful. The bit that I really detest is below. Is there a better way to traverse up the DOM using jQuery?
val_information_screen.closest('table').parents("table:first").show();
val_information_screen.closest('table').parents("table:first").hide();
If you are sure that it has fixed structure, then you can use this,
$(td-selector).parents("table").eq(1).hide();
In your case,
val_information_screen.parents("table").eq(1).hide();
If your DOM (specifically starting from table you want to hide till the td you have as selector) is pretty much fixed, then the below selector can be used.
$('#element').parents('table').eq(1)

jQuery class count on a certain table column number

I've been at this for a while and want to know the best way of achieving my goal if anyone has any ideas!
Example:
<table>
<tbody>
<tr>
<td>Hello</td>
<td>Hello (I want to check this column)</td>
</tr>
<tr>
<td>Hello 2</td>
<td class="active">Hello 2 (this column)</td>
</tr>
</tbody>
</table>
jQuery I've got so far (I'm traversing from a clicked element):
var length = $(self).closest("tbody").find("tr").find("td.active").length;
Obviously this gets all the active classes of td, when I only want the second column. I've tried:
var length = $(self).closest("tbody").find("tr").find("td:eq(1).active").length;
This does not work.
Any ideas?
If I'm understanding correctly, you want to get the table cells in the second column (not the first as indicated in the question) which have the class active on them. If that's the case, you can use the following:
var length = $(self).closest('tbody').find('tr').find('td:eq(1)').filter('.active').length;
http://jsfiddle.net/mikemccaughan/g6mnn/
I think your selector isn't doing what you expect it to. I would have expected what you're expecting, but check out this paragraph from the eq() documentation (emphasis mine):
Note that since JavaScript arrays use 0-based indexing, these
selectors reflect that fact. This is why $('.myclass:eq(1)') selects
the second element in the document with the class myclass, rather than
the first. In contrast, :nth-child(n) uses 1-based indexing to conform
to the CSS specification.
So you're going to want to use td:eq(1) without the class selector, then filter your results, and then count them:
var length = $(self).closest("tbody").find("td:eq(1)").filter(".active").length;
Hope that helps!

Iterating over table cells

So I have this code (from the gracious help from this site!)
window.onload = function inventorytable() {
var tableRows = document.getElementById
("inventorytable").getElementsByTagName("tbody")[0].getElementsByTagName("tr");
Now from here, I want to get all of the TDs, under all of the TRs. I also want to be able to perform operations on the TDs, depending on which TD (i.e. which column) they are in the table.
So for example, if I have
<tr>
<th>Processor Speed</th>
<th>Amount of RAM</th>
<tr>
<td>2.0</td>
<td>3.0</td>
</tr>
<tr>
<td>3.2</td>
<td>4.0</td>
</tr>
I want to be able to select each TD separately, depending on its order within the TR, and then add text to it. There will be a variable number of TRs, at least 20, and possibly more. There are going to be about 10-15 TDs.
The text added would be something like " Ghz" or " GB"
You have to iterate over all tr elements (which is a NodeList [MDN], returned from getElementsByTagName [MDN]):
for(var i = 0, l = tableRows.length; i < l; i++) {
var row = tableRows[i];
//...
}
Inside the loop you can get all tds of one row again with getElementsByTagName or using the .cells [MDN] property. You can then decide to either iterate over them as well or to access the specific cells explicitly, such as cells[1] to access the second cell (second column) in that row.
If the cells contain simple text or you don't have any event handlers bound to their descendants, you can simply use innerHTML [MDN] to change the element's text content.
Otherwise you have to create a new text node and append it to the cell (that might be the best option in any case).
The Mozilla Developer Network is a great source for all kinds of information, including the DOM and JavaScript.
This is how I would do it:
var table = document.getElementById( 'inventorytable' );
[].forEach.call( table.rows, function ( row, i ) {
[].forEach.call( row.cells, function ( cell, j ) {
// this function runs for every cell in the table
cell // references the current cell
row // references the current row (the row the cell is in)
i // the row index (0 = first row, 1 = second row, etc.)
j // the cell index (0 = first cell in row, 1 = second cell in row, etc.)
});
});
Live demo: http://jsfiddle.net/6tXUm/
Note: You need to include ES5 shim since some older browsers (mainly IE8) don't implement the new ES5 features like forEach.
If you want to simplify your life and logic here is an example of what you want: http://jsfiddle.net/Akkuma/2wJ8G/
What I'm doing is first getting the exact table and from there grabbing all td elements. It will automatically run through each one in order of row, as that is their order in the markup. You don't need to first select document.getElementsByTagName('tr'). If you need to filter you can look up the tree or select elements at a higher level first, for instance the thead.
In the second example, I know explicitly there is only one tbody and can access the area, which is of length 1, and chain .getElementsByTagName('td') to get only those td within the tbody (you could have a td in your thead or tfoot)
The third example uses #Rob W 's recommendation of using table dom traversal. At least in my example using it only complicated the code.
The final example combines Rob W's recommendation (ex3) with ex2. This allows you to skip having to write two loops.

Using JQuery .append() to dynamically change table layout

I am using an HTML table but I want to dynamically change the layout of the table based on the browser size. My basic code looks like this:
<tr>
<td class="row">E1</td>
<td class="row">E2</td>
<td class="row">E3</td>
<td class="lastRow">E4</td>
</tr>
Then the JQuery should calculate the number of rows and insert row-breaks accordingly.
My JQuery looks like this for now:
$('td.row').append('</tr><tr>');
Yet, its still displaying all the elements on one line. Any idea why?
This is a perfect place to use fluid CSS layouts.
Instead of writing lots of crazy Dom-manipulating javascript, simply replace your TD tags with divs and have them float:left
Further- append does not do what you think it does. It's dom manipulation and not string manipulation- you can't use it to directly change HTML the way you're thinking.
Further reading
Try looking at .after(); function:
http://api.jquery.com/after
i think that you need to try with this selector
Asumming that your table haved an id called example try like this
$("#exmaple tr:last").append('<tr></tr>');
$('</tr><tr>').insertAfter('.row');
You'd want to do your append on the tr element, not on td.row
You need to not think in terms of constructing HTML markup. There's no way to splice a closing/opening </tr><tr> into the DOM (without some ugly .innerHTML hacks).
Instead, create a new row after the current row, and the relocate the cells into that new row.
var row_cells = $('td.row').slice(1);
row_cells.each( function( i, el ) {
$('<tr>').insertAfter( row_cells.eq(i).parent() )
.append( row_cells.eq(i) );
});
DEMO: http://jsfiddle.net/EDf5a/
Or maybe you wanted a new row for each cell:
var row_cells = $('td.row');
var row = row_cells.parent();
row_cells.each(function(i, el) {
$('<tr>').insertBefore(row)
.append(row_cells.eq(i));
});
DEMO: http://jsfiddle.net/EDf5a/1/

Categories