sorting table rows by columns containing markup - javascript

I'm using the tablesorter JQUery plugin to sort the rows of a HTML table by clicking on a column. In most cases, the table cells contain simple markup like <td>hello</td>, so the default behaviour works fine.
However, one column of the table contains cells like this
<td>
<a id="1" class="festivalSubscribe " action="create"
controller="festivalSubscription" onclick="/* Lots of Javascript */">Not Subscribed
</a>
<a id="1" class="festivalUnsubscribe hide" action="delete"
controller="festivalSubscription" onclick="/* Lots of JavaScript */">Subscribed
</a>
</td>
Each cell of this column contains two links (as above), one of which will always be invisible (the one with the hide class). I want to sort this column by the visible link text ("Not Subscribed" or "Subscribed").
The plugin provides am option to define a function, the results of which will be used to determine how to sort the column, e.g.
textExtraction: function(node) {
// extract data from markup and return it
return node.childNodes[0].childNodes[0].innerHTML;
}
However, I can't find a way to define this function such that it will correctly sort the simple case <td>hello</td> and the more complex case described above.

Try this, it should handle both cases of having child elements and not having child elements:
textExtraction: function(node) {
// extract data from markup and return it
var $node_a = $(node).find("a");
if ($node_a.length) {
return $node_a.filter(":visible").text();
}
// normal case
return node.innerHTML;
}

I think #Kevin B's answer is best for the original plugin, but I wanted to add that I have forked a copy of the original plugin on github and added functionality to the textExtraction option to allow specifying columns. So this is possible.
$("table").tablesorter({
// Define a custom text extraction function for each column
// default extraction is obtained like this: `$(node).text()`
textExtraction: {
0: function(node) {
return $(node).find("a:visible").text();
}
}
});

You can grab first visible element found inside the node and extract text from it using this:
$(node).find(':visible:eq(0)').text();

Try this:
textExtraction: function(node) {
return $('td :not(.hide)', node).text();
}

Related

jQuery how to do the following

I've the following question, let say we have a div like this:
These are dynamically formatted divs, with the classes 'row', 'element' and 'isotope-item' are always present. Everything in between can vary per div.
What I want is to the following:
As you see the commmas are no longer there and seperate classes between the commas are now one class.
Anyone any idea?
I already have the following to remove the commas:
$('div.element').each(function () {
var _sCurrClasses = jQuery(this).attr('class');
jQuery(this).attr('class', _sCurrClasses.replace(/,/g, ' '));
});
I would advise doing this backend,but in JavaScript you could:
This will not account for the space in the words though.
You would need to pass then trough separately one by one and replace.
or store them in a data-attribute and format when you need them.
<string>
var classesFormat = classes.replace(/,/g, '');
var classesList = classesFormat.split(" ");
for(String c : classesList)
{
$("#id").addClass(c);
}
</string>
So you could create a data-attribute for each one instead.
Go through each one, format and the add to class.
<div data-id="Microsoft Office," class="test test test">
With the script
$(this).attr("data-id") // will return the string "Microsoft Office,"
or .data() (if you use newer jQuery >= 1.4.3)
$(this).data("id") // will return the Microsoft Office,
And then do your replace after that and addClass.
I don't think classes work like you think they do
the first PICTURE you posted would result in that div having the follwing classes
row
element
Microsoft
Office,
My
SQL,
Page
Rank
isotope-item
Note the , is PART of the class
You want, according to the second PICTURE
row
element
MicrosoftOffice
MySQL
Page
Rank
isotope-item
Removing , is just as you posted ... the problem is, how do you determine which spaces to remove, and which to keep?
(I posted this as an ANSWER, but I KNOW IT IS NOT AN ANSWER)

Rendering dynamic Enyo Repeaters

I need to make a data table for an enyo project I am working on that will ultimately display the result of an Ajax call.
This (Blatantly stolen from ryanjduffy here)seems to be a good starting point, but when I try to call setData() from a button event (rather than in the constructor) as I have here I get the following error:
InvalidCharacterError: String contains an invalid character # http://enyojs.com/enyo-2.1/enyo/source/dom/Control.js:681
I looked at the Control.js code and it seems that it tries to create a new node, but the this.tag property is set to null and things break.
I feel like I am missing something really simple, but I just can't see the problem yet...
Can someone tell me what I am doing wrong?
Thanks!
EDIT 1:
Apparently calling render() is not needed. Here is the original working version with render() commented out. Everything looks great. However, if I try to remove render() from the version that requires a button click, the repeater starts creating div's above the table instead of tr td's inside the table...
EDIT 2:
Basically, from what I can tell, the Repeater inside of a table will lose it's parent once the table is rendered (or something like that). The result is the Repeater will start rendering its new items outside of the original table and because a td tag without a table makes no sense, it just renders a div.
The solution I have come up with is to give the Repeater itself a table tag so its children always wind up in the right spot. This adds the challenge of needing to recreate the header line each time, but it is not that big of a deal. I have a working example if anyone is interested.
I'm sure you're not looking for a solution any longer but since I was mentioned in the post, I thought I'd let you know what I figured out. In short, my code shouldn't have worked but since browsers are forgiving, it did ... sort of.
When the code run at create time, it renders something like this:
<table>
<tr> <!-- header row --> </tr>
<div> <!-- repeater tag -->
<tr> <!-- repeater row --> </tr>
</div>
</table>
The browser looks at that and says, "Hey, dummy! No <div>s in a <table>" and kicks it out but leaves the <tr>s.
In your example, since you're delaying the render of the rows, Enyo renders:
<table>
<tr> <!-- header row --> </tr>
<div></div>
</table>
And the browser ejects the <div> and you're left with an empty table. When you later set the data, those rows are rendered into the div. Unfortunately, since you're rendering <tr> and <td>, those aren't valid outside a table so you just get text.
I found a couple solutions. The simplest was to set the tag of the Repeater to be TBODY which is allowed inside a table. The slightly more involved solution was to make the DataTable inherit from Repeater and set the header row to be chrome so they're not removed when updating the data.
Option #2 Fiddle
enyo.kind({
name:"DataTable",
tag: "table",
kind: "Repeater",
published:{
map:0,
data:0
},
handlers: {
onSetupItem: "setupItem"
},
components:[
{name:"row", kind:"DataRow"}
],
create:function() {
this.inherited(arguments);
this.mapChanged = this.dataChanged = enyo.bind(this, "refresh");
this.refresh();
},
refresh:function() {
if(this.map && this.data) {
this.buildHeader();
this.setCount(this.data.length);
}
},
buildHeader:function() {
if(this.$.header) {
this.$.header.destroyClientControls();
} else {
this.createComponent({name:"header", tag:"tr", isChrome: true});
}
for(var i=0;i<this.map.length;i++) {
this.$.header.createComponent({content:this.map[i].header, tag:"th"});
}
this.$.header.render();
},
setupItem:function(source, event) {
for(var i=0;i<this.map.length;i++) {
event.item.$.row.createComponent({content:this.data[event.index][this.map[i].field]});
}
event.item.render();
return true;
}
});

Sending appropriate input to plugin

I am using a third party javascript plugin to develop a two-column table functionality as per my requirements. The plugin accepts an input json array which looks something like the following:
var data = {"Project" : [{'Start':'02/20/2012',
'End':'02/20/2012',
'Content':'Project Description',
'Group' : 'Table Group 1'
},...
] };
Group in the above json when used in the plugin can be given any type of content (custom html content as well) which it renders internally as:
var items = []; //json array items will be pushed by items.push from input `data` json
for(var i=0,im = items.length;i<im;i++){
var group = items[i].Group;
groupDiv.innerHtml = group;
}
In my case, to the Group item of the json array item, I am passing a custom HTML which looks like the following:
var customHtml = '<div><span class="button-red"></span>'+
'<div class = "quick-info-hide">' +
'Some Custom Content from input' +
'</div></div>'
I am generating this customHtml from some input (another larger json) and sending it to the plugin and was trying not to modify the plugin itself. However, I had to modify the plugin to add a certain functionality here (row expansion on/off) which is kind of troubling me now.
When the row is not expanded, customHtml's span and div should have class style1, and if it expanded, customHtml's span and div should have class style2.
However, inside the plugin, customHtml is assigned without any if condition of expansion/collapse. Since the plugin was not built with a logic of row expansion/collapse on click, it wasn't expected to do so as well. I want to change the styles here on expansion/collapse to assign to groupDiv.innerHtml i.e customHtml which probably can be done by another variation of customHtml1 which will have different styles.
What is the best approach to do this?
Should I add an if condition here and send two variations of customHtml in the plugin and apply the suitable one on respective if condition? Is there a better method as well in this case to not spoil the generic approach of the plugin (which I seem to be doing here with modification)?

Browser doesn't renders table element | jsFiddle example

My code makes a cross domain request to fetch the source of a webpage, using James Padolsey's "cross domain mod for jQuery": https://github.com/padolsey/jQuery-Plugins/tree/master/cross-domain-ajax;
Then it selects the first table and appends it to a existing div.
But the appended table doesn't get rendered properly. Can anyone take a look at this fiddle and tell me why?
http://jsfiddle.net/6ZgRf/
You're using jQuery.. so you can traverse it by turning it into a jQuery object
function stripViewResponse() {
// Select table element
var fetchedTable = $(antwort).find('table')[0]; // find first table
// append table
$('#new').append(fetchedTable);
}
http://jsfiddle.net/6ZgRf/1/
You can also try this
function stripViewResponse() {
// Select table element
var fetchedTable = $(antwort).find('table').eq(0);
// append table
$('#new').append(fetchedTable);
}

jQuery break out of table

I have a standard HTML formatted table, that dynamically generates the content, via Zend Framework. From which I have an issue altering the form internally PHP side. So I need to alter the tables appearance somehow. With that on the occasion I have an element show up in one of the rows and when this element shows up I want to break out of the table and then do something after it then start the table again.
Basically I want to inject the equivlant of
</tbody></table>/*other stuff*/<table><tbody> after the row containing the one element I seek which in this case is a label.
I tried $("label[for='theLable']").parents('tr').after('</tbody></table><br><table><tbody>') which appears to ignore the ending table parts add the br, and then does a complete open/close tag for table and tbody within the same table I am trying to break out of so inbetween tr tags basically it adds this new table
Whats the best way to approach this concept?
update with jsfiddle link
http://jsfiddle.net/cPWDh/
You can't really modify the HTML of the document the way you're thinking, since it's not a legitimate way to alter the DOM.
Instead, I would create a new table and .append the rows you want to move to it, which will automatically move them from their current location (instead of copying them):
$(document).ready(function() {
var $trow = $('label[for="specialLabel"]').closest('tr'),
$table = $trow.closest('table');
$('<table>').append( $trow.nextAll().andSelf() ).insertAfter($table);
});​
http://jsfiddle.net/cPWDh/1/
this approach won't work in js. What you could do if the table has not too many rows is this:
var nextTable = $("table").after("/*other stuff*/<table id="nextTable"></table>");
//now you loop over the lines of your original table that you want to have after the "break", and move them to the nextTable:
var before = true;
$("table tr").each(function(){
if ($(this).has("[for='theLable']")) {
before = false;
}
if (!before) {
nextTable.append($(this));
}
});

Categories

Resources