I'm building a system which outputs a list of cars on a screen(in a table element). The page loads the updates automatically with an HTTP call to the server.
Every car has an ID, a status and some irrelevant things.
Now when the user loads the page, the cars without the statuses 'maintenance' and 'wrecked' are shown and every five seconds new JSON data will be loaded from the server. If there comes a car out of the maintenance this one should be added to the table. The table is sorted by the car id and here comes the problem.
I have written a bit of pseudo code to clarify my problem:
if (row_with_greater_id_exits && row_with_lower_id_exists) {
place_row_between_firstRowWithGreaterId_and_FirstRowWithLowerId();
} else if (is_row_with_greater_id) {
jQuery('table#cars tbody').append(generatedHtml);
} else if (is_row_with_lower_id) {
jQuery('table#cars tbody').prepend(generatedHtml);
}
The problem is I don't know how to find the first row with a greater id or the first row with a smaller ID. The ID's are not always succeeding because some cars are wrecked and have the status wrecked.
I hope someone here has had a similar problem and can help me on my way to the solution.
Many thanks in advance!
You need to loop over the rows, and compare the ids. When your new row has and ID less than the current row in the loop, insert the new row before the current row, and break the loop.
rows.each(function(i, el) {
if (+newrow.id < +el.id) {
$(el).before(newrow);
return false;
}
})
if (!newrow.parentNode)
rows.parent().append(newrow);
This code assumes rows is a jQuery object that has a collection of all the rows, and newrow is a DOM element representing the new row.
Also assumes that the IDs are simple numbers. Note that numeric IDs are only valid in HTML5.
Personally, I'd just use the native API for this:
var tbody = document.querySelector("tbody");
tbody.insertBefore(newrow, [].reduce.call(tbody.rows, function(bef, el) {
return bef || +newrow.id > +el.id ? bef : el;
}, null));
No need to test for the isFirstLoad. When the tbody is empty, it'll still work. But you'll need a .reduce() shim for older browsers.
I have this: id="car-1" data-id="1"
I'll assume that data attribute is on the tr elements. Obviously you can adapt the following if it is on a cell within a row.
Loop through the table checking each row's id, and insert the new row immediately before the first row that has an id greater than the new one:
// assume newId is the new ID, somehow set from your JSON
var rowInserted = false;
jQuery('#invoices-outstanding tbody tr').each(function() {
var $row = $(this);
if (+$row.attr("data-id") > newId) {
$row.before(generatedHtml);
rowInserted = true;
// break out of each loop
return false;
}
});
if (!rowInserted)
jQuery('#invoices-outstanding tbody').append(generatedHtml);
Related
I am trying to iterate over all cells in a column (column index 1) in a DataTable and change the background color based on cell value using the following code:
var table = $('#my_table').DataTable( {...});
console.log("next, iterate over rows in table: "+table);
table.rows().every( function ( rowIdx, tableLoop, rowLoop ) {
console.log("looping over rows");
var cell = table.cell({ row: rowIdx, column: 1 }).node();
if (cell.data() == 'mouse'){
$(cell).css('background-color', 'orange');
}
});
console.log("finished iterating over rows ");
The table is displaying the data fine.
However the console log prints:
>>next, iterate over rows, table: [object Object]
>>finished iterating over rows
ie the
table.rows().every( function (...){...}
is not entered. I copied and pasted from DataTables examples and I have no idea why it is not being executed.
The only thing I can think of is that the DataTables docs here: https://datatables.net/reference/api/rows().every() mention- Iterate over each selected row. None of the rows in the table are selected, I just want to loop through every row (and change cell color) regardles if it is selected or not.
Note I also tried:
table.rows().eq(0).each( function ( index ) {
var row = table.row( index );
var data = row.data();
console.log(data)
});
And this is not executed either (console.log doesn't print anything from inside the function).
You can iterate over the table's td elements and capture the text content of each cell.
Apply some logic to these and you can easily assign each cell's background colour.
I have used arr.map() as it's ES6 JavaScript, but .each() works too.
$('table td').map(function(i, cell) {
var cellContent = $(cell).text();
console.log(i,cellContent); // for demonstration
if (cellContent === 'pending') $(cell).css('background-color', '#ccc');
});
This can be easily changed to get the cell element's data, class, or id too.
I'm using the DataTables library to create a table with extra functionality. What I'd like to achieve, is that the user can select rows and then press a button. This will call the server, do some stuff and the rows should be updated accordingly.
However, after I'm done iterating over the rows to be changed and setting its values, re-drawing the table does not actually update its values. I update the data object, invalidate the cache and call table.draw(). It's very similar to the last example on this page.
I have created a JSFiddle of this issue. The button updates the date objects of the selected rows and the table is re-drawn, but the data inside the table is not updated. The core JS code:
$('#updateRow').click(function() {
//Get the table
var table = $('#example').DataTable();
//Iterate over selected rows
var rowData = table.rows({
selected: true
}).every(function() {
//For every selected row, update startDate
var d = this.data();
d.startDate = "01/01/2017";
console.log('Set startDate of ' + d.name + ' to ' + d.startDate);
//Invalidate the cache
this.invalidate();
});
//Re-draw the table
table.draw();
});
I forked and did the solution from your JsFiddle. Here's the relevant snippet from fiddle https://jsfiddle.net/k38r9be5/1/
var rowData = table.rows({
selected: true
}).every(function(rowIdx) {
var colIdx = 4; // startDate is the fifth column, or "4" from 0-base (0,1,2,3,4...)
table.cell( rowIdx, colIdx).data('01/01/2017').draw();
});
Basically, via the API you can get the cell object itself, and modify the contents with .data(). In your version you weren't actually getting a particular cell object and instead just copied the data contents of the row to a variable, and modified that.
I'm working on making a dynamic HTML table using jQuery. In a table, my user has two interactions:
Append a row
Remove a specific row
The problem with numbering the rows is that if a user removes a specific row, all of the rows following that row need to be renumbered. I would have to select all rows following the removed row and subtract their number by 1.
Is there a better way to go about this?
EDIT: Here's a JSFiddle demonstrating the problem: http://jsfiddle.net/LNXae/2/
I'm aware that an ordered-list would automatically renumber my rows, but I'd rather use a table since the example I'm giving now is pretty boiled-down.
http://jsfiddle.net/mblase75/LNXae/1/
First, wrap the counter number in a <span> with a class for easy finding later:
$new_row.children('td').prepend('Row #<span class="num">' + ($new_row.index() + 1) + "</span>");
Then update all these spans with an .each loop after you remove the desired row. The first argument passed into .each's callback function is a zero-based index number, and the second is the HTML element:
var $row = $(this).closest('tr'),
$table = $row.closest('table');
$row.remove();
$table.find('tr').each(function(i, v) {
$(v).find('span.num').text(i + 1);
});
After the user has appended a row, or deleted one, you just need to iterate over the "number" cells. If we assume that you have a <td> element, we:
1) give them a nice ID, e.g. row_0, row_1, etc...
2) write a function to iterate over them:
function updateRows(){
$('[id*="row_"]').each(function(index){
$(this).html(index + 1); // add +1 to prevent 0 index.
};
};
I have written a jquery plugin which does exactly this, and you shouldnt need to "number" the rows per-se. All you need to do when deleting a row is to pass the index of the row being deleted.
eg, if you have a delete button in a row:
<table>
<tr>
<td> <input type="button" class="delete" value="delete this row" /></td>
</tr>
</table>
The jQuery might look like
$('.delete').click(function(){
var index = $(this).parents('tr').index();
// tell your plugin to delete row "index"
});
The method from my plugin which does this looks something like:
removeRow: function (index) {
return this.each(function () {
var $this = $(this);
var $tbody = $('tbody', $this);
var $tr = $('tr', $tbody).eq(index);
$this.trigger('rowRemoved', [$tr]);
$tr.remove();
});
}
I have a table that is populated by user input. For example, there is a text input for First Name, and Last Name. John in one input and Smith in the next, will add the the table under the Name column as John Smith, one string of 2 values. This is working correctly, along with the Address column... but getting the values FROM the table TO the inputs is the issue. Clicking the corresponding row populates the inputs, but I need to split these values to populate the correct inputs (so that John Smith is split up again to first and last name for example). Any ideas? Thanks in advance.
http://jsfiddle.net/Z85QC/6/
jQuery
/* Add a click handler to the rows - this could be used as a callback */
$("#example tbody tr").click(function (e) {
if ($(this).hasClass('rowSelected')) {
$(this).removeClass('rowSelected');
} else {
oTable.$('tr.rowSelected').removeClass('rowSelected');
$(this).addClass('rowSelected');
}
var properties; // all td from .row_selected
properties = fnGetSelected(oTable).find("td");
$("#fName").val(properties.eq(0).text());
$("#email").val(properties.eq(1).text());
$("#company").val(properties.eq(2).text());
});
I advise you to wrap your data row elements in span with corresponding class names. Example given for first name and last name,
js
$('#addRow').click(function () {
var row =$('#example').dataTable().fnAddData([
'<span class="fname">'+$("#fName").val()+'</span> <span class="lname">' + $("#lName").val()+'</span>',
$("#email").val(),
html of the fiddle
<td><span class='fname'>John</span> <span class='lname'>Smith</span></td>
Then it is straighforward and clear to retrieve the values independent from their textual format.
http://jsfiddle.net/Z85QC/10/
In the fiddle you will also find code for associating click function logic to new added rows so that they can be selected.
$('#addRow').click(function () {
var row =$('#example').dataTable().fnAddData([
'<span class="fname">'+$("#fName").val()+'</span> <span class="lname">' + $("#lName").val()+'</span>',
$("#email").val(),
$("#company").val() + '<br>' + $('#address').val()]);
$(oTable.fnGetNodes(row)).click( function( e ) {
if ($(this).hasClass('rowSelected')) {
$(this).removeClass('rowSelected');
} else {
oTable.$('tr.rowSelected').removeClass('rowSelected');
$(this).addClass('rowSelected');
}
var properties; // all td from .row_selected
properties = fnGetSelected(oTable).find("td");
$("#fName").val(properties.eq(0).find('.fname').text());
$("#lName").val(properties.eq(0).find('.lname').text());
$("#email").val(properties.eq(1).text());
$("#company").val(properties.eq(2).text());
});
In order to keep your code DRY it is best to place the click function logic inside a function and call that directly, instead of copying the code.
If you are 100% sure that the last name and first name are seperated by a space, you can use this code :
$("#fName").val(properties.eq(0).text().split(' ')[0]);
$("#lName").val(properties.eq(0).text().split(' ')[1]);
For address :
$("#company").val(properties.eq(2).html().split('<br>')[0].trim());
$("#address").val(properties.eq(2).html().split('<br>').splice(1).join('\n').trim());
Fiddle : http://jsfiddle.net/Z85QC/11/
You can do a simple change like:
var properties; // all td from .row_selected
properties = fnGetSelected(oTable).find("td");
var names = properties.eq(0).text().split(' ');
$("#fName").val(names[0]);
$("#lName").val(names[1]);
$("#email").val(properties.eq(1).text());
$("#company").val(properties.eq(2).text());
JSFiddle Demo
But only if you're sure the First and Last name are separated by a constant single space, otherwise you'd have to change it a little bit more...
Is there a way that i can select multiple nth-childs at once like:
$("#table").find("tr > :not(td:nth-child(1,3,5))");
which doesn't work
I want to select all td in each row but not for column 1,3,5 (this can be any combination).
Is there a way to do this? I cannot assign classnames.
thanks for any help!
UPDATE:
I want to search in all rows of a table, but exclude some columns.
I have this code right now:
elem.keyup(function() {
$(options.table).find("tr").hide();
var data = this.value.split(" ");
var jo = $(options.table).find("tr > :not(td:nth-child("+cols+"))");
$.each(data, function(i, v){
jo = jo.filter(":containsIgnoreCase('"+v+"')");
});
jo.parent().show();
});
It works when I pass a single value, but i want to exclude multiple columns.
thanks
From your example, it looks like you're trying to exclude the odd numbers. Try:
$("#table").find("tr > :not(td:nth-child(odd))");
Although, it may be more efficient to just select the even ones.
$("#table").find("tr > td:nth-child(even)");
You can also use formulas in nth-child. See this link for more detail.
Okay, as per comments below/clarification on the question, here is another solution.
$("#table").find("tr > td").filter(function(index){
return index == 1 || index == 2 || index == 5;
});