I have a table, and I'd like to add a hideable/showable panel below each row for more controls and info than can reasonably fit in the table row. My first thought was to have a sibling tr for each original tr, and put a single td inside with an appropriate colspan:
<tbody>
<tr>
<td>...</td>
...
<tr>
<tr class="tablesorter-childRow">
<td colspan="4">...</td>
</tr>
...
</tbody>
Each original row would have a button that would hide() or show() the corresponding tr, and the td in the child row would have all the extra controls that don't need to be seen normally.
This gets tricky because I'm using d3 to build the table, and d3 doesn't like multiple elements per datum (see this stack post and this other thing).
It's also tricky because I'm using tablesorter, which sorts the table client-side using the values in tds, so the original data has to stay in table format. (It could keep pairs of rows together using a css class "tablesorter-childRow".) I also don't believe I can have multiple tbodies because they aren't sorted along with rows -- each tbody's rows are sorted.
I thought about using jquery afterwards to insert a tr after each original tr, but then d3 won't update the table properly when something changes (since the data won't join properly), and the reason I'm using d3 is because it makes building lots of dom elements easier (for me at least).
So, question time: how else can I create this panel that
moves with the original table rows
doesn't affect sorting
can be hidden or shown?
If you want two sibling elements to share the same data, the easiest way in d3 is to group them under a parent element. You assign the parent element the data, and then when you create its two child elements (without assigning data) they both inherit the parent's data.
In SVG, the usual parent element is a <g>. For your purpose, the natural parent element would be <tbody> which can be used to group table rows. However, you'd have to modify the table sorting code that you're using, to sort individual <tbody> elements instead of individual rows.
The only other option would be to dynamically set the content of the info row and insert it in the correct place every time you want to show it, similar to how many tooltip examples work: it's the same tooltip, just moved around and with new data. If you're using d3 to attach the event handler on the table rows, it will pass the data object of the clicked-on row to the event handling function, so you can use that data to fill the information content without creating a data-join. To insert the info row after the clicked <tr> element, you could use d3's insert() function, but the format isn't ideal; better to use plain Javascript or JQuery. You'd also have to remove the info row before running your sort.
tableRows.on("click", showInfo);
/* remember to give your rows a tabIndex, so that keyboard users can
trigger the click action */
/* Create the persistent info box */
var infoRow = d3.select( document.createElement("tr") )
//create a new <tr>, unattached to the document
.attr("class", "infoBox";//set class to allow unique formatting
infoRow.append("td") //add a <td> within the <tr>
.attr("colspan", colNames.length); //set to fill all columns
/* Show the info row for a clicked element */
function showInfo(d,i) {
/* Hide info box if currently shown */
infoRow.style("display", "none");
/* Set content to match clicked row */
infoRow.select("td") //select the <td> element
.html(/* create html from the d object
you don't need a function(d), just
concatenate the string. */)
/* Insert the row in the correct place.
This will automatically remove it from any current location */
this.parentNode.insertBefore(infoRow, this.nextSibling);
//"this" is the tableRow object that received the click event
infoRow.style("display", null);
//revert to default display setting (i.e. table-row)
}
function sort(/*parameters*/) {
infoRow.remove();
//remove the infoRow element from the document,
//so it only exists as a Javascript object again
/* Run your sort code */
}
Related
Is there a way to use angular.element(...).on('click', onTdClick); (for example) in a way that executes onTdClick (providing the element to it) on every that gets clicked?
Let's say I have 2 tables, both have cells and columns.
I want to be able to click on CELLS and send the element of what I clicked and send it to onTdClick($event).
$scope.onTdClick = function(ev){
window.getSelection().selectAllChildren(ev.target);
};
Essentially doing the same as: ng-click="onTdClick($event)" without having to put ng-click on hundreds of <td>'s
I dynamically add table rows and table cells with .insertRow and .insertCell
https://www.w3schools.com/jsref/met_table_insertrow.asp
So statically doing angular.element().find('td').on() wont work well here.
Whats my end result?
I'm attempting to make it so I can click <td>'s to essentially highlight cells by just clicking them.
The highlight code is already tested and works:
window.getSelection().selectAllChildren(ev.target);
(where ev is the td element)
You shouldn't use insertRow with AngularJS. Instead, you should use ng-repeat with an array that has an object for every row, and just push objects when you want to insert a row.
$scope.insertRow = function(){
$scope.tdList.push({});//push whatever you want
}
If you want the number of cells to be variable, you can store some param in this object to tell the view how many cells the row has, and put a nested ng-repeat inside to create the cells
I am using columnClass property to assign different rendering (and event processing) to cells belonging to different columns.
When the table fits within the screen every element gets the class as specified by the columnClass property of its column and I can easily hook some rendering/processing functions to the elements.
However, when some of the columns don't fit within the page and I expand the row (by clicking the '+' button) the cells are renedered with no reference to the class of the column. I reviewed the custom renderer for the cells of the expanded columns and also see no way finding out what the column class of a cell is.
Ideally, I would like to use the same custom field renderer as defined in the table. Is there a way I can do it?
Example:
Let's say I have 3 columns: name, dob, salary.
I defined 3 classes for the columns: name_class, dob_class, salary_class.
For each class I use a jquery selector and assign diffrerent rendering/behavior for each cell of that column.
Now, I resize the table so that dob and salary get overflown and click on the '+' button to expand the row of interest.
I would expect to get the dob cell with the dob_class attribute and salary cell with the salary_class attribute, so I am able to link the same cell renderer to them.
However, ****the class attribute is no assigned (not retained) and I cannot link with jquery using the class attribute. ****
**Could you please suggest an solution that would allow me to retain the column class on the cells of the expanded row? **
Sorry, no, that's not currently possible. It has been raised against the code already, see https://github.com/DataTables/Responsive/issues/93, so it is on the todo list. I'm not sure why the OP has been voted down, seems a bit unfair to me, as it is a valid question.
I am currently using an open-source javascript table sorter: http://mottie.github.io/tablesorter/docs/
The problem I have is that I dynamically add tables to my page in reaction to the user pressing a button. Only the most recently added table becomes sortable and the previous tables are not sortable anymore after I add the new table. Currently, I have this code at the end of my table creation:
//Make table sortable
$(document).ready(function () {
$("#" + thisTable.ID).tablesorter();
});
I don't understand why the old tables lose their sortability if they're not being reloaded. I am only appending a new table with a different ID under the previously added table.
You can change your selector to $('table'), and this will add the sorter to all tables. Your current code will only run on page load though, so you'll have to execute the $('table').tablesorter(); line every time you dynamically add a new table (in your button's click handler).
It would be better though if you added a class, such as sortedTable to every table, and made your selector $('.sortedTable'), rather than simply $('table'), because you may at some time want a table that isn't sorted and the first version will always sort all tables. The second version will only sort those tables that you explicitly mark as sortable.
I have an html table in which all the cells within a column have the same css class 'className'.
I want to apply hammer.js on 'hold' event to these cells. I call hammer.js like so:
Hammer('td.className').on('hold', doModal);
I need to find out exactly what cell was 'hold'. In my modal window I have a select element that will set the new value of the cell.
I can not set an ID for each cell on that column, because the table is dynamically generated. My doModal callback gets called several times, because I select all cells with class 'className'. I can get the doModal to be called only once, but I can not determine which cell (row + column) triggered the 'hold' event.
How can I achieve that ?
You can find out the held table cell using the target property in the event object.
for example,
var tab = document.getElementById("tab"); // reference to your table
Hammer(tab).on('hold',function(ev){
console.log(ev)
ev.target.style.background='red'; //will give red background for the held cell
});
i tried to put together a fiddle but can't find a working link to hammer.
update: fiddle
The program has two javascript functions to generate a table (generateTable) and delete one selected row (deleteOneRow).
generateTable function generates a table based on server data. Each row is in the following format:
<tr><td><span class="desc">aaa</span>Delete</td> </tr>
Basically, each row is some text followed by a 'delete' link.
When the script finish the table generation, it calls $(".delete-choice").click(deleteOneRow) to bind click to the event handler.
In the event handler function deleteOneRow, it first simply deletes all the rows in the table. Then call generateTable again to display the remaining data entries. Code structure is like follows:
function deleteOneRow() {
var choiceIdx = ... //get index
$(".delete-choice").unbind('click');
CurrentChoices.splice(choiceIdx, 1); // remove the entry from the array
$("#problem-choice-table tr").each(function(){ // delete the DOM entries
$(this).remove();
})
generateTable();
}
If the table have two rows, after clicking the delete link in the 1st row, the first row was deleted. Then it generates a table with the 2nd row moving to the 1st. However, the deleteOneRow is called again, then the 2nd row is removed eventually.
I guess this is due to event is fired multiple times. I tried unbind, stopPropagation, etc. Nothing works.
Using jquery's live to attach an event is perfect for situations like this where content is added / removed dynamically and you want to match against the same selector, have you looked into using that?