I have table with rows where as in between have hidden rows and because of that odd even css class not able to set. How can I avoid those hidden rows?
HTML
<tr class="oddRow">
<td>Avinash</td>
<td>18-Jun-2010</td>
<td>LI1004</td>
<td>5,600.00</td>
<td>Sort</td>
</tr><tr class="oddRow" style="display:none;">
<td>Ajith</td>
<td>18-Jun-2010</td>
<td>LI1006</td>
<td>5,001.00</td>
<td>!</td>
</tr><tr class="evenRow">
<td>Ankur</td>
<td>14-Jun-2010</td>
<td>LI1005</td>
<td>5,000.00</td>
<td>me</td>
</tr><tr class="oddRow">
<td>Ajith</td>
<td>18-Jun-2010</td>
<td>LI1006</td>
<td>5,001.00</td>
<td>!</td>
</tr>
I know this isn't tagged jQuery but this would be the easiest way to apply this solution...
You don't need two CSS classes here (odd and even), just one. Start by setting the CSS for every row to use the "oddRow" style declarations by default. The "evenRow" style declarations should simply overwrite the defaults.
Add this JS function
var zebraStripes = function($) {
$('table.stripes tr').removeClass('evenRow')
.filter(':visible:odd').addClass('evenRow');
// using the :odd selector as it is zero-based
}
You can then bind this function to the document ready event as well as any event that changes row visibility.
Edit
Updated to work with jQuery 1.7, example here - http://jsfiddle.net/UZNKE/6/
Assuming your question is asking what I posted in the comments, you'll have to have a more in-depth 'hide' function which will change the classes of all subsequent functions. I expect you'll want to use something like this:
function hideRow(rowNum)
{
var rows = document.getElementById('table-id').getElementsByTagName('table');
// get current class and hide the row
var currentClass = rows[rowNum].className;
rows[rowNum].style.display = 'none';
// set up classname array
var classNames = new Array("oddRow", "evenRow");
// make sure 'j' points to the next desired classname
var j = 0;
if (classNames[j] == currentClass)
j = 1;
// make all subsequent visible rows alternate
for (i=rowNum+1; i<rows.length; i++)
{
// ignore empty rows
if (rows[i].currentStyle.display == "none")
continue;
// set class name
rows[i].className = classNames[j];
j = (j+1) % 2;
}
}
Note: I haven't tested the code, but I commented it so you should be able to figure out what's going on
Related
Ladies and gents of the internet I have an issue with jquery.
I have 3 templates on a page one for the top of a table one for the bottom and one for the rows. They look like this.
Header
<div class="col-md-12 tableContainer">
<table class="table table-hover table-condensed">
<thead>
<tr>
<th class="hidden-xs"></th>
<th></th>
<th>Artist</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
Row
<tr data-bind='{"visible":"!isFiltered"}' class="handPointer">
<td style="width: 22px;">
<img alt="{{Libary}}" src="#:libaryImageSrc#" width="22"/>
</td>
<td style="width: 22px;">
<span
class="glyphicon glyphicon-cog"
aria-hidden="true"
data-bind='{"click":"spawnAddToPlaylistModal"}'>
</span>
</td>
<td><span>{{Name}}</span></td>
<td><span data-bind='{"click":"loadAlbums"}'>Albums</span></td>
<td><span data-bind='{"click":"loadSongs"}'>Songs</span></td>
</tr>
Footer
</tbody>
</table>
</div>
The issue is that when I try and use my templates as html objects jquery assumes I have just been lasy and left of the closing tag and breaks every thing. I have a function that parses and creates the templates in batches so the execution of the rendering of 1000s of lines does not lock up the user interface. I also have a widget binding engine I made so I can bind functions using data-bind on the html elements.
The catch is I want them to render all at the same time not section at a time so I create document fragments and then once all the batch is completed I Inject the pre-bound html like so. This is in a function that first calls the headers then x rows then the footer.
// Bootstrap render call function
// Results in correct markup as a string
template = bootstrapTemplateObject(dataSource[0]);
// At this point I need a htmlObject so I call it in jquery
// but then jquery decides it can fix it
htmlObject = $(template);
// Then I add the fragment
fragment.appendChild(htmlObject[0]);
// Then I bind the fragment
widget.bind(htmlObject, 0);
Question is can I stop jquery "fixing" my code. If so how?
----- Update 1 -----
Ok so following on from PJs answer I have created the following.
// First empty the target
target.empty();
// As Paul-Jan suggested we create one big string of html so it is well formed(or at least should be).
template = "";
// If we have a header we add it
if (header !== null) {
template += header(dataSource[0]);
}
// Then we start the hackyness by adding a target placeholder for later.
// I choose script as far as im aware this can go anywhere with out being incorrect html.
// And at this point I dont know if the header / footer are acting as a container ie nested or not.
template += "<script id='superMergerTarget'></script>";
// If we have footer add it.
if (footer !== null) {
template += footer(dataSource[0]);
}
// Now we make the string a html object
htmlObject = $(template);
// So at this point we could have nothing but the script or a container or a wrapper
// So we iterate over like so.
for (var k = 0; k < htmlObject.length; k++)
{
var targetReplaceo = $(htmlObject[k]).
// check if its nested in container situation.
find('#superMergerTarget').
// Check self if wrapper or single situation.
addBack('#superMergerTarget');
if (targetReplaceo.length === 1) {
var element = targetReplaceo[0];
if (element.parentElement === null)
{
// If we are in here then either the header and or footer dont exists.
// Or the header and footer are stand alone html not wrapping the content.
// So we just go ahead and chuck it in the target.
if (header !== null) {
template = header(dataSource[0]);
htmlObject = $(template);
widget.bind(htmlObject, 0);
target.append(htmlObject);
}
target[0].appendChild(fragment);
if (footer !== null) {
template = footer(dataSource[0]);
htmlObject = $(template);
widget.bind(htmlObject, 0);
target.append(htmlObject);
}
break;
}
widget.bind(htmlObject, 0);
// This part seems filthy and there is probably a much better way of doing it.
// First we get an element to add the fragment too.
var newElement = element.cloneNode();
newElement.innerHTML = "";
newElement.appendChild(fragment);
// then we get the individual templates and add them one at a time.
var childNodes = newElement.childNodes;
for (var l = 0; l < childNodes.length; l++)
{
element.parentNode.insertBefore(childNodes[l], element);
}
// then remove the placeholder
element.parentNode.removeChild(element);
// then we add it to the target.
target.append(htmlObject);
break;
}
}
I feel like it could be much better but it does work so I'm happy with that, can anyone suggest a better way?
You can't. DocumentFragments are DOM nodes, as such they cannot be constructed from "badly formed HTML". That's hardly jQuery's fault :)
However, I don't see (yet) why you need to construct your document fragments from malformed HTML. For instance, what is stopping you from initializing the fragment from the header and the footer (together they make a valid DOM node), then getting the tbody from the fragment, and batchwise attach your rows to it?
jqTds =[
<td class="hidden-xs sorting_1">text1</td>
<td class=" ">text2</td>
<td class=" ">text3</td>
<td class=" ">text4</td>
<td class=" ">Edit</td>
<td class=" ">Delete</td>]
How can I get all elements that has a "anchor" with class "edit-row" or "delete-row" and get all other that does not have it
// I am editing a script that uses DataTables.Js what I am trying to do is getting all elements from a table row into (var jqTds = $('>td:not(.hide_me)', nRow)) and now I want to include an input in all elements except the ones that has save-row class and edit-row class cos they are link to save/delete
thanks in advance
No idea why you have them in "an array", but if you run this while they are still in the DOM, use the :has pseudo selector:
var $tds = $('td:has(a:.edit-row,a:delete-row)');
var $otherTds = $('td').not($tds);
The first one reads. *find any td that has an anchor within it with class edit-row or an anchor within it with class delete-row".
The second one simply says, find all tds and exclude the first lot from the matches :)
You can use $.grep() to filter your array.
Using the function passed to $.grep() you can try and find your elements within the current <td>. If neither a.edit-row or a.delete-row are found return true, otherwise return false:
var filteredTds = $.grep(jqTds, function(td){
var $td = $(td);
return !$td.find('a.edit-row').length && !$td.find('a.delete-row').length;
});
JSFiddle
I was able to do like this:
for (var i = 0; i < editColumn.length; i++) {
if ($(editColumn[i]).find("a").hasClass("edit-row") == true) {
editColumn[i].innerHTML = '<a class="save-row" href="">Salvar</a>';
}
else if ($(editColumn[i]).find("a").hasClass("delete-row") == true) {
editColumn[i].innerHTML = '<a class="cancel-row" href="">cancelar</a>';
}
i have a requirement to highlight cells in different tables which are created dynamically. i have created tables and now looking the same to highlight with some condition. my code look like as follows,
<script type="text/javascript">
function compare()
{
var rows =document.getElementsByClassName('highlight').tBodies[0].rows, numOfRows = rows.length;
for (var i = 0; i < numOfRows ; i++)
{
if(rows[i].cells[1].innerHTML !== rows[i].cells[2].innerHTML )
{
rows[i].className='rowChange';
rows[i].cells[1].className = 'cellChange';
rows[i].cells[2].className = 'cellChange';
}}
}
</script>
<body>
<div><input type="button" value="highlight" onclick="compare" /></div>
<table id='main'>
<tr>
<td>table1</td>
<td><table class='highlight' id='child1'><tr><td>test1</td><td>test2</td><td>test3</td></tr></table></td>
</tr>
<tr>
<td>table2</td>
<td><table class='highlight' id='child2'><tr><td>test2</td><td>test2</td><td>test2</td></tr></table></td>
</tr>
----
----
----
<tr>
<td>table'n'</td>
<td><table class='highlight' id='childn'><tr><td>test'n'</td><td>test'n'</td><td>test'n'</td></tr></table></td>
</tr>
</table>
</body>
if i highlight using 'id' attribute that's working fine. but it's making code to be too complex. so please suggest me to me meet my goal efficiently. thanks in advance.
You are basically using the document.getElementByClassName incorrectly:
https://developer.mozilla.org/en-US/docs/Web/API/document.getElementsByClassName
The document.getElementByClassName does not return any tBodies, but the HTMLCollection of the mached elements i.e. an array of elements with the searched class.
You should also note that its IE9+ compadible, but I guess you already know that.
Anyway, the correct implementation to get the rows is this:
var rows = document.getElementsByClassName('highlight')[0].rows
As soon as I did that change it worked like a charm.
EDIT: Well the simplest way to get your code to work is to simply add in another loop. One for the tables and one for the table rows.
function compare() {
var tables =document.getElementsByClassName('highlight'), rows, numOfRows, i,j;
for(j = 0; j < tables.length; j++) {
rows = tables[j].rows;
numOfRows = rows.length;
for (var i = 0; i < numOfRows ; i++)
{
if(rows[i].cells[1].innerHTML !== rows[i].cells[2].innerHTML )
{
rows[i].className='rowChange';
rows[i].cells[1].className = 'cellChange';
rows[i].cells[2].className = 'cellChange';
}
}
}
}
But this is not actually how I would advise you to do this (on the assumption you are creating the tables on the client side dynamically). The problem is that you are looking up your tables with document.getElementByClassName however, as you pointed out, in your actual code, you are creating the tables dynamically, with means, at the time of the creation of the tables you could simply store a reference to those tables into an array and go through that instead. There really is no need for you to use document.getElementByClassName you are simply taxing the system with it.
I have a table with a list of records. each row has class "list_request" and has a cell of class "record_approval":
<table>
<tr>
<th>name</th><th>date</th><th>id</th><th>group</th><th>approval</th>
<tr class="list_request">
<td>Frank</td><td>2012-2-15</td><td>01</td><td>Account</td><td class="record_approval">Dave Ellis</td>
</tr>
<tr class="list_request">
<td>Ellen</td><td>2012-2-19</td><td>04</td><td>Admin</td><td class="record_approval">Susan Peters</td>
</tr>
<tr class="list_request">
<td>Michael</td><td>2012-2-26</td><td>06</td><td>Admin</td><td class="record_approval"></td>
</tr>
I'd like to construct a javascript function that checks whether or not "record_approval" has a value (which value is unimportant), and if so, change the css color value for that row. Essentially, the approved records should have a different color than the unapproved ones.
something like...
function check_approval(){
var checkrow = document.querySelectorAll( "tr.request_list" )
var checkcell = document.querySelectorAll( "td.record_approval" )
for (i=0;i<checkcell.length;i++){
if (!checkcell.value){
this.parentNode.style.color = "ff9900";
}
else{
}
}
is this essentially the wrong approach?
Mistakes I found:
Unclosed for loop (missing closing })
You're looking for class request_list, but on your html it's list_request
You should be using checkcell[i] instead of checkcell inside your loop
Your color hex value should begin with a #.
There's no need to get all rows and cells from an event listener
It's unclear when you want that function to run. Should it respond to an event?
Also, I'd set a new css class on the row, instead of setting the color directly.
Apparently, you're looking for this:
var checkcell = document.querySelectorAll( "td.record_approval" );
for (i=0;i<checkcell.length;i++){
if (checkcell[i].innerHTML){
checkcell[i].parentNode.style.color = "#ff9900";
}
}
http://jsbin.com/anadij/1/edit
checkcell is an array of elements. you'll want to loop through them, accessing 'checkcell[i]' instead of checkcell.value.
your hex color should be defined with a "#" preceding ff9900
your for loop isn't closed properly
basically update it s.t.
if (!checkcell[i].value){
checkcell[i].parentNode.style.color = "#ff9900";
} else{
}
I have a table in a repeater. The data gets entered into the table and a class is assigned to each row depending on the type of data being loaded. For example:
<table id="detail-table">
<tr class="typeA">
<td>Value</td>
</tr>
<tr class="typeB">
<td>Value</td>
</tr>
<tr class="typeC">
<td>Value</td>
</tr>
<tr class="typeB">
<td>Value</td>
</tr>
<tr class="typeA">
<td>Value</td>
</tr>
</table>
The table is a collapsed table. typeA is the header, typeB is the child of typeA and typeC is the child of typeB. In the table example typeA has two immediate children and the first child also has a child.
When the user clicks typeA, the two typeB rows appear, and when typeB has children it will also expand to reveal the typeC row. I have an event handler that does this on click.
I need to apply the full detail expansion (typeA to typeC) for an item that has a specific value. I need to trigger the expand event on the parent and all children of that parent when the value of the cell in typeA equals the value received from the query string. I have some code that does this already. However, I feel it is a little bit of a hack. I was wondering if anyone had some suggestions? Here is what I have so far.
$(function() {
var tableRows = $('#detail-table').find("tr:gt(0)");
$(tableRows).each(function(i, val) {
//ExpandValue is a value in my C# page.
if ($.trim(val.cells[0].innerText) == '<%= ExpandValue %>'){
expandRows(i);
}
}
function expandRows(startIndex) {
// Expand the first row, the typeA class
// row that matches the value
$(tableRows[startIndex]).trigger('expand');
startIndex += 1;
while($(tableRows[startIndex]).attr("class") != "typeA"){
$(tableRows[startIndex]).trigger('expand');
startIndex++;
}
}
});
This finds all of the rows of the table. Then loops through the array of table rows. The condition checks the innerText of the first cell in each row. If the innerText of the cell matches the ExpandValue, the target row has been found. Send the index of this row to the expandRows function. In the expandRows function, expand the row at the startIndex, this is the typeA class row that matched the ExpandValue. Then increment the startIndex by one to point to the next row. The while loop checks the class of each row. Trigger the expand event on each row until the next typeA row is hit. This works and expands each row correctly. But again, I am new to jquery and I feel that there is probably some way to accomplish this in a better way. Any ideas?
You can speed-up your selector and loop at the top of your code:
//instead of using a pseudo-selector, we can use the `slice()` function to get all but the first element returned
var $tableRows = $('#detail-table').find("tr").slice(1);
//this `for` loop will perform a lot faster than the `.each()` loop
for (var i = 0, len = $tableRows.length; i < len; i++) {
if ($.trim($tableRows.eq(i).find('td').eq(0).text()) == '<%= ExpandValue %>') {
expandRows(i);
}
}
Here is a demonstration of how much faster a properly formatted for loop is than using jQuery's .each(): http://jsperf.com/jquery-each-vs-for-loops/2
Here are some docs for ya:
.slice(): http://api.jquery.com/slice
.eq(): http://api.jquery.com/eq
.text(): http://api.jquery.com/text
Also, you keep wrapping your tableRows variable in jQuery, which is not necessary because it is already a jQuery object from the start.
$(function() {
var $tableRows = $('#detail-table').find("tr").slice(1);
for (var i = 0, len = $tableRows.length; i < len; i++) {
if ($.trim($tableRows.eq(i).find('td').eq(0).text()) == '<%= ExpandValue %>') {
expandRows(i);
}
}
function expandRows(startIndex) {
// Expand the first row, the typeA class
// row that matches the value
$tableRows.eq(startIndex).trigger('expand');
startIndex++;
while($tableRows.eq(startIndex).attr("class") != "typeA"){
$tableRows.eq(startIndex).trigger('expand');
startIndex++;
}
}
});
I'm not sure if this is already doing what you want but on your expandRows function you should probably increment the startIndex by one before doing anything because if the first iteration through the for loop triggers the expandRows() function then it will pass an index of zero which will target the top row (which you exclude from your selector at the start).
$(function(){
var $list = $('#detail-table tr.typeA:gt(0) > td:eq(0):contains("<%= ExpandValue %>")');
for (var i = 0, len = $list.length; i < len; i++)
{
var $nextrows = $list[i].nextAll("tr");
for (var x = 0, ilen = $nextrows.length; x < ilen; x++)
{
if ($nextrows[x].hasClass("typeA")) break;
$nextrows[x].trigger('expand');
}
}
});