I have a data table with alternating row background colors. I have an AJAX script to delete a row. I can't come up with a way to change the class of all the rows beneath the one that was deleted so that it alternates correctly again.
For example, considering the following:
`<tr id="1" class="row1">
<td>blah</td>
</tr>
<tr id="2" class="row2">
<td>blah</td>
</tr>
<tr id="3" class="row1">
<td>blah</td>
</tr>
<tr id="4" class="row2">
<td>blah</td>
</tr>`
Now, using my AJAX script, I remove id2, then id3 will move underneath id1 and they will have the same row color. I managed to make my script change the next tr class, but that doesn't really help because then it's just the same color as the one after that. I can't figure out how to iterate through all of the next tr's, and change their class accordingly.
What I have so far:
$('#news_' + id).fadeOut('slow');
var currtr = $('#news_' + id).attr('class');
var nexttr = $('#news_' + id).closest('tr').next('tr').attr('id');
$('#' + nexttr).removeClass($('#' + nexttr).attr('class'));
$('#' + nexttr).addClass(currtr);
You could just iterate over the visible<tr> elements, and remove the class from the even ones, and apply to the odd ones.
Something like this:
Example: http://jsfiddle.net/2CZdT/
$('tr:odd').addClass('odd');
$('td').click(function() {
$(this).parent().fadeOut(function() {
$(this).siblings('tr:visible').filter(':even').removeClass('odd')
.end().filter(':odd').addClass('odd');
});
});
I have the click event on the <td>, so when one is clicked, it traverses up to the parent <tr> element, fades it out, the in the callback, it grabs all visible sibling <tr> elements, filters the even ones, removes the .odd class, then goes back and filters the odd ones, and adds the .odd class.
Note that this presumes there's a default class applied in your CSS, then you override the odd ones (or even ones) with the alternating class.
Easiest way is to go over the whole table again, e.g. add this after the fadeOut:
$('#id_of_your_table tr:even').addClass('even');
Edit: on second thought, that won't work since the row you faded still exists, but just isn't visible. You need to remove it from the DOM, or skip it when re-applying the zebra-effect. Example:
$('#news_' + id)
.fadeOut('slow')
.remove()
.closest('table')
.find('tr:even').addClass('even');
Or:
$('#news_' + id)
.fadeOut('slow')
.addClass('skip')
.closest('table')
.find('tr:not(.skip):even').addClass('even');
You can also target the table directly as in the first example, but you might as well move up from the faded row to the table its in.
You could use the next siblings selector to get all the rows following the one you are going to delete. Delete the desired row. Then, you should already have the following siblings, so just .each() them and change their class.
E.g.
var followingRows = $("#id2 ~ tr");
$("#id2").remove();
followingRows.each(function() {
if (this.is('.even')
this.removeClass('even').addClass('odd');
else
this.removeClass('odd').addClass('even');
});
Something close to that...
Let CSS do the work for you.
table tr:nth-child(2n+1) {
background-color: #eef;
}
no JavaScript required! =)
I would do something like this:
$('news_' + id).fadeOut('slow', function() {
$(this).remove();
});
var i = 1;
$('tr').removeClass().each(function() {
if (i == 1) {
$(this).addClass('row' + i);
i++;
} else {
$(this).addClass('row' + i);
i--;
}
});
Related
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 with around 30 columns and the idea is to let the user select which columns to be hidden or shown. The reason for this is to let them select which columns will be visible when they print.
To tackle this problem, I have assigned a class name to each column and i'm using jQuery's toggle function. This works fine, but I was wondering if there is a better way to go about it that is more efficient and cleaner than what I am currently using. I have a separate function for each column and my code looks like this:
jQuery
function tablecolumn1toggle(){
$(".column1").toggle();
}
function tablecolumn2toggle(){
$(".column2").toggle();
}
function tablecolumn3toggle(){
$(".column3").toggle();
}
HTML Simplified
toggle column 1
toggle column 2
toggle column 3
<table class="table table-bordered" id="points_table">
<tbody>
<tr>
<th class="column1>Route</th>
<th class="column2">Location</th>
<th class="column3>Track</th>
</tr>
</tbody>
</table>
and so on..
I am using a button to call each toggle function, I will use checkboxes once I have the basic code working. So, is there a way for me to cut down the amount of code?
EDIT: Thank you all for your answers, it was really hard to pick a single answer but i'm grateful for all your input.
If you want to do it dynamically using checkboxes, add a data property to the checkbox
<input class='toggleColumns' type="checkbox" data-target="column1" />
<input class='toggleColumns' type="checkbox" data-target="column2" />
<input class='toggleColumns' type="checkbox" data-target="column3" />
<input class='toggleColumns' type="checkbox" data-target="column4" />
then add a change event on the checkbox:
$('.toggleColumns').on('change', function (e) {
// get the target for this checkbox and toggle it
var tableColumn = $(e.currentTarget).data('target');
$('.' + tableColumn).toggle();
});
Here is working fiddle: https://jsfiddle.net/9Ls49w97/
A bit of a late addition, but to add one more alternative: if you have multiple setups of this kind and you don't want to add classes each time, you can show or hide a column with something like $('tr *:nth-child(' + (ColumnIndex + 1) + ')', table).toggle();. Especially if you change the column order in the future, the class approach can come to bite you.
One step further, is not to define the checkboxes beforehand, but have JQuery create them on the fly. This is also easier in maintaining the page and with the added benefit that you can assign the column index while creating the input objects and don't have to add any special attributes in design time.
All in all, the html would be as light as possible (no classes or properties) and doesn't have to be maintained. An example where the checkboxes are added in a div:
var table = $('table'); //add an id if necessary
var cols = $('th', table); //headers
var div = $('<div>'); //new div for checkboxes
cols.each(function(ind){
$('<label>').text($(this).text()).append(
$('<input type="checkbox" checked=true>') //create new checkbox
.change(function(){
$('tr *:nth-child(' + (ind + 1) + ')', table).toggle();
})
).appendTo(div);
});
table.before(div); //insert the new div before the table
Fiddle
/* number is a parameter now */
function tablecolumntoggle(i){
$(".column"+i).toggle();
}
/* example to iteratly call */
for (var i = 1; i <= 3; i++) {
tablecolumntoggle(i);
};
Here's one way to make it simpler.
Give each button a data-col value and the matching column element(s) the same data-col value, then they can be paired in a simple function:
<button data-col='column1'>toggle</button>
<button data-col='total'>toggle</button>
<button data-col='other'>toggle</button>
<div class="col" data-col="column1">
column 1
</div>
<div class="col" data-col="total">
total column
</div>
<div class="col" data-col="other">
other
</div>
and code
$(function() {
$("button[data-col]").on("click", function() {
var col = $(this).data("col");
$(".col[data-col='" + col + "']").toggle();
});
})
Simple fiddle demo: https://jsfiddle.net/bb1mm0cp/
You Pass number 1,2,3 function
Try this
function tablecolumn1toggle(id){
$(".column"+id).toggle();
}
function call like this
tablecolumn1toggle(1); or
tablecolumn1toggle(2); or
tablecolumn1toggle(3);
OR
function tablecolumn1toggle(colum_name){
$(colum_name).toggle();
}
function call like this
tablecolumn1toggle(column1); or
tablecolumn1toggle(column2); or
tablecolumn1toggle(column3);
The use of "this" and ".parent()" in jquery gets a bit confusing when it goes past simple divs or datatables. I have a table with the following structure: (I can't rename any of the classes or id)
<table class="table1">
<tbody>
<tr>
<div class="detail">
<table>
<thead></thead>
<tbody>
<tr>
<td>
<img class="picture_open" src="../location">
</td></tr></tbody></table></div></tr></tbody></table>
What I'm trying to do is have a click function on that img which will be able to grab the full RowElement.
What I have now:
$(".table1 tbody tr td img.picture_open").live('click', function () {
var overallTable = jQuery(this).parent("table").dataTable();
console.log("overallTable: " + overallTable);
var elementRow = this.parentNode.parentNode;
console.log("elementRow: " + elementRow);
var rowData = overallTable.fnGetData( elementRow );
console.log("rowData: " + rowData);
if ( this.src.match('img_name') )
{
//kills the table that was created if that row is opened
}
else
{
//runs ajax call to create another table since row is NOT opened
}
} );
However the code I have above prints out this:
overallTable: [object Object]
elementRow: [object HTMLTableRowElement]
TypeError: 'null' is not an object (evaluating 'oSettings.aoData')
In my problem is the $(this) incorrect? (Not getting the img with class "picture_open")
Or is my overallTable variable set up incorrectly with the .parent()?
Or is it my elementRow variable set up improperly with the parentNode?
Any help to clarify my errors would be amazing.
Thanks!
parent() in jQuery will parse only one level up the DOM, you should use .parents()/.closest(). This will fix your issue.
NOTE: .live() is turned into .on() in latest jQuery versions. Better to use .on()/.click()
parent() in jQuery only moves one level up the DOM, so what you probably want there is parents('table'). This should fix your overallTable issue.
in jQuery, .parent() only goes up the DOM once. What you should be using it .parents() to look up the DOM until it finds table
You need to use .closest('table') to find the closest table
Similary to find the element row
var elementRow = $(this).closest('tr');
Try this :
$('.table1').on('click', '.picture_open', function(ev) {
this // is .picture_open element
ev.target // is .picture_open element
ev.delegateTarget // is .table1 element
var $row = $(ev.delegateTarget).find('.detail tr').first();
});
I have a function that hides/shows a table by clicking on it's header which is contained in a <thead> tag. When clicked the table hides and all that is left is the header, which, by clicking again, can un-hide the table.
I have multiple tables and would like to only have to use on function, instead of writing one for each table. To do this I am trying to pass the arguments (this,this.lastSibling). For some reason this.lastSibling is not targeting any object. I've tried every way of navigating the node tree I can think of, but I cannot target the tbody.
My Javascript/Jquery
function ToggleTable(trigger,target){
$(trigger).click(function(){
$(target).toggle();
ToggleTable(trigger,target)
});
}
My HTML
<table class="format2" >
<thead onmouseover="ToggleTable(this,this.lastSibling)">
<!--Title-->
</thead>
<tbody>
<!--Cells with information in here-->
</tbody>
<!--Note No TFooter Tag-->
</table>
<--Other tables similar to the one above-->
Thanks in advance!
I have a function that hides/shows a table by clicking on it's header which is contained in a <thead> tag. When clicked the table hides and all that is left is the header, which, by clicking again, can un-hide the table.
I'm lost in your current code. But If you want to toggle the visibility of the tbody (or the last child element in your <table> tag you could try this.
function ready() {
$('table > thead')
.each(function(e){
$(this).siblings(':last').hide();
})
.click(function(e) {
$(this).siblings(':last').toggle();
});
}
$(ready);
Live sample: http://bl.ocks.org/3078240
If you would like to try a solution that utilizes core JavaScript instead of jQuery shims, this might work for you. It's a function I quickly wrote that returns the last sibling that is an HTML element (e.g. not a text node) although you should be able to easily modify it to accept any node in the DOM:
function getLastSibling(el) {
var siblings, x, sib;
siblings = el.parentNode.children;
x = siblings.length;
while ((sib = siblings[x - 1]) && x >= 0) {
console.log(sib);
console.log(sib.nodeType);
if (sib.nodeType != 1 || sib.tagName == 'SCRIPT') {
x--;
} else {
return sib;
}
}
return null;
}
Assuming all your tables will have the class format2 .
Try this:
$("table.format2 > thead").click(function(){
$(this).next("tbody").toggle();
});
JSFiddle: http://jsfiddle.net/KcY4X/
I have a html table which has several rows - lets say 17 for this example. On row 2, 9 and 15 I have some BOLD text which are basically headers for the rows after it. I've used the following code to add an IMAGE after each header:
$("#tblResults tr.lGreyBG td span.gridTXT b").each (function(index) {
$(this).after(" <img class='ChartButton' id='c"+ index +"'src='Images//chart_bar.png' alt='Chart' width='20' />");
});
I also have the following bit of code which binds a click event to each of the chart buttons.
$("img.ChartButton").click(function(){
alert ($(this).attr("id")); // THIS LINE WILL BE REPLACED
});
At the moment, it simply displays the ID of the chart button. What I need to do is replace the alert to pull back the rows afters the header row that was clicked, upto the next header row, of until the end of the table, (whichever comes first). So if the first button was clicked then rows 3 to 8 will be pulled back. Once I have these I can then iterate through each of the TD cells to look at the data in the table.
Many thanks for any help on what "selectors" I need to use to pull back the correct rows. Also note that this needs to be dynamic as other tables will have different number of rows.
Thanks
H
If there is a set of rows that belong together my first instinct would be to declare classes that help me select all of them at once e.g.
<tr class="group-1"> ... </tr>
<tr class="group-1"> ... </tr>
<tr class="group-2"> ... </tr>
<tr class="group-2"> ... </tr>
...
Or multiple theads and tbodies as Tomalak suggests.
If this is not possible and you want to do this using jQuery you can select all the rows after the header using nextAll(). You'll just have to filter out all rows that are after the next heading.
var nextBlockAndTheRest = $(this). // create jQuery object out of this img
closest("tr"). // find the parent tr
nextAll("tr.lGreyBg"). // find all next lGreyBg rows
first("td span.gridTXT b"). // find the first with b
nextAll(). // select all following rows
andSelf(); // add the row with b
var thisBlock = $(this). // create jQuery object out of the img
closest("tr"). // find the parent tr
nextUntil("td span.gridTXT b"). // select everything after the tr
andSelf(). // add the current block heading
not(nextBlockAndTheRest); // remove all the rest of the rows
jsFiddle
// notice that use after() directly, without each()
$("#tblResults tr.lGreyBG td span.gridTXT b").after(function (index) {
return "<img class='ChartButton' id='c"+ index +"'src='Images//chart_bar.png' alt='Chart' width='20' />";
});
$("#tblResults").delegate("img.ChartButton", "click", function () {
var currentRows = $(this).closest("tr").nextUntil("tr:has(span.gridTXT b)");
});
BTW: You definitely should think about a more semantic markup using multiple <thead> and <tbody> tags if your table has multiple heads and bodies.
$("#tblResults thead span.gridTXT b").after(function (index) {
return "<img class='ChartButton' id='c"+ index +"'src='Images//chart_bar.png' alt='Chart' width='20' />";
});
$("#tblResults").delegate("img.ChartButton", "click", function () {
var currentRows = $(this).closest("thead").next("tbody").find("tr");
});
Edit: Changed answer to use nextUntil().