I'm doing a directive which is gonna work similar to an excel.
As Javascript does not support matrix as native, I had to do the workaround that usually people does. Array inside array.
Everything is working so far, but I've realized that when I applies a matrix of 100x100 I do have a big problema with performance. With a small matrix it's ok, but with big ones it's pissing me off.
Another big deal for me is that while this directive does not finish its loading the rest of the APP does not load if the problematic directive does not finish.
How can I improve this my current approach to have a better performance? And
How can I make the APP to load each part of itself in an asynchronous way?
From my service I have this:
this.getDataGrid = function(column, row){
var matrix = new Array(column);
for (var i = 0; i < column; i++) {
matrix[i] = new Array(row);
}
for (var i = 0; i < matrix.length; i++){
for (var j = 0; j < matrix[i].length; j++){
matrix[i][j] = {value: '', checked: false};
}
}
return matrix;
};
From my directive:
link: function postLink(scope, element, attrs) {
scope.dataGrid = info.getDataGrid(attrs.column, attrs.row);
}
From my view:
<table border="1">
<thead>
...
</thead>
<tbody>
<tr ng-repeat="items in dataGrid track by $index">
<td><strong>{{$index+1}}</strong></td>
<td ng-repeat="item in items track by $index"><input type="text" ng-model="item.value"/></td>
</tr>
</tbody>
</table>
UPDATE:
I gave a try to quick-ng-repeat as mentioned by apairet, but didn't work so far. Looks like the quick-ng-repeat doesn't work with nested array.
<tr quick-ng-repeat="item in dataGrid" quick-repeat-list="items">
<td><strong>{{$index+1}}</strong></td>
<td quick-ng-repeat="subItem in items" quick-repeat-list="subItems"><input type="text" ng-model="subItems.value"/></td>
</tr>
UPDATE:
After some research I started to go throughout to the lazy load approach. I've found some projects with RequireJS and Sript.js and I thought this was the solution for my problem, until I realize I had a diff situation.
The lazy load approach works by a route, it loads the related files of the route in a lazy way.
However, what about when you have inside the view of your route a directive which makes everything slow? That's my case.
So, I had to do some workaround as it follows below:
DIRECTIVE
link: function postLink(scope, element, attrs) {
var maxIntervalColumnLoad = 5;
var maxIntervalRowLoad = 5;
scope.gridLoadColumn = function(index){
if (index <= maxIntervalColumnLoad) {
return true;
}else{
return false;
}
}
scope.gridLoadRow = function(index){
if (index <= maxIntervalRowLoad) {
return true;
}else{
return false;
}
}
$interval(function(){
maxIntervalColumnLoad = (maxIntervalColumnLoad + 5);
}, 1500, (attrs.column/maxIntervalColumnLoad) );
$interval(function(){
maxIntervalRowLoad = (maxIntervalRowLoad + 5);
}, 1500, (attrs.row/maxIntervalRowLoad) );
scope.dataGrid = info.generateDataGrid(attrs.column, attrs.row);
}
VIEW
<thead>
...
</thead>
<tbody>
<tr ng-if="gridLoadRow($index)" ng-repeat="items in dataGrid track by $index">
<td><strong>{{$index+1}}</strong></td>
<td ng-if="gridLoadColumn($index)" ng-repeat="item in items track by $index"><input type="text" ng-model="item.value"/></td>
</tr>
</tbody>
It solved the problem with my first load page.
But I still have performance problems when I'm typing any value in the inputs.
<input type="text" ng-model="item.value" />
I'm guessing the relation through the ng-model seems to be the problem, but I need it to keep the data sync.
Any idea how could it be achieve? Or How to improve the performance?
Related
I'm having a bit of a weird problem and there are now 2 people in my office completely stumped by this (so I hope it's not something embarrassingly obvious!).
As an overview, we have a SQLite database that could contain any number of tables, each with any number of rows or columns. We want to display this data in a page in an Electron app.
We have an Angular page in our Electron app (with Express/Node as a backend) which makes some calls to an Express endpoint (which gets data from the SQLite db) and we end up with two 2D arrays of data - one for the table headers ($scope.tableHeaders) and one for the table content ($scope.documentsList).
The code for the calls is below:
$http.get('http://localhost:3000/tableCount').then(function (res) {
if (res.data.numberoftables) {
$scope.count = res.data.numberoftables;
var i;
$scope.documentsList = new Array();
$scope.tableHeaders = new Array();
for (i = 1; i <= $scope.count; i++) {
var tableNo = i;
//loop through tables
***$http.get('http://localhost:3000/doclist/' + tableNo).then(function (resDocs) {
//get table content
if (resDocs.data) {
***$scope.documentsList.push(resDocs.data);
}
});
$http.get('http://localhost:3000/tableColumns/' + tableNo).then(function (resHeads) {
//get table headers
if (resHeads.data) {
$scope.tableHeaders.push(resHeads.data);
}
});
}
}
});
Just for fun, here's our HTML:
<div id="documentTables">
<div ng-repeat="tableID in getNumberArray(count)">
<table st-table="documentsList[tableID]" class="table table-condensed table-hover">
<thead>
<tr st-safe-src="tableHeaders[tableID]">
<th ng-repeat="col in tableHeaders[tableID]">
{{col}}
</th>
</tr>
<tr st-safe-src="tableHeaders[tableID]" colspan="{{tableHeaders[tableID].length}}">
<th colspan="1" ng-repeat="col in tableHeaders[tableID]">
<input id="{{col + 'searchbox' + tableID}}" st-search="col" placeholder="{{col}}" class="input-sm form-control" type="search" />
</th>
</tr>
</thead>
<tbody>
<tr st-safe-src="documentsList[tableID]" ng-repeat="document in documentsList[tableID]">
<td ng-repeat="col in tableHeaders[tableID]">
{{document[col].value}}
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="{{tableHeaders[tableID].length}}" class="text-center">
<div st-pagination="" st-items-by-page="10" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
The problem we're coming across is that no actual data is displayed in the tables. Somewhere between the starred lines (***) in the JS above, an empty array for each table is added. So if we have two tables in our database, we end up with documentsList = Array[4] with documentsList[0] and documentsList[1] being empty arrays. Why/where would this be happening, and how can I fix it?
Note - if you'd like to test this out without our endpoints, try out these variables:
$scope.count = 2;
$scope.tableHeaders = [["TabNo", "Document", "PageNumber", "Date"],["TabNo", "Document", "PageNumber"]];
$scope.documentsList = [];
$scope.documentsList.push(JSON.parse('[{"idANX":1,"pathANX":"Product Overview.pdf","isHeaderANX":1,"isExcludedANX":0,"isNotHyperlinkedANX":0,"isSubHeaderANX":0,"HyperlinkedColumnANX":1,"TabNo":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"1"},"Document":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"Product Overview.pdf","isLastCell":false,"value":"Product Overview"},"PageNumber":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"1 - 2"},"Date":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":true,"value":"18 August 2015"}},{"idANX":2,"pathANX":"Spec.pdf","isHeaderANX":0,"isExcludedANX":0,"isNotHyperlinkedANX":0,"isSubHeaderANX":0,"HyperlinkedColumnANX":1,"TabNo":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"2"},"Document":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"Spec.pdf","isLastCell":false,"value":"Spec"},"PageNumber":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"3 - 4"},"Date":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":true,"value":"1 April 2015"}}]');
$scope.documentsList.push(JSON.parse('[{"idANX":1,"pathANX":"Product Overview.pdf","isHeaderANX":0,"isExcludedANX":0,"isNotHyperlinkedANX":0,"isSubHeaderANX":0,"HyperlinkedColumnANX":1,"TabNo":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"1"},"Document":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"Product Overview.pdf","isLastCell":false,"value":"Product Overview"},"PageNumber":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"1 - 2"}},{"idANX":2,"pathANX":"Spec.pdf","isHeaderANX":0,"isExcludedANX":0,"isNotHyperlinkedANX":0,"isSubHeaderANX":0,"HyperlinkedColumnANX":1,"TabNo":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"2"},"Document":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"Spec.pdf","isLastCell":false,"value":"Spec"},"PageNumber":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"3 - 4"}}]');
$scope.getNumberArray = function (num) {
var n = new Array(num);
var i;
for (i = 0; i < num; i++) {
n[i] = i;
}
return n;
}
$scope.rowClass = function (row) {
if (row.isSubHeaderANX == 1) {
return 'subheader';
} else if (row.isHeaderANX == 1) {
return 'header';
}
else { return 'doctablerow'; }
}
Also pagination and filtering don't work but that's an issue for when our table actually contains data.
Edit: Probably should have mentioned that this is using angular-smart-table
You should take a look at how asynchronous requests and promises works. To make your code run you could do this: var promises = []; // Creates an array to store the promises
$http.get('http://localhost:3000/tableCount').then(function (res) {
if (res.data.numberoftables) {
$scope.count = res.data.numberoftables;
var i;
$scope.documentsList = new Array();
$scope.tableHeaders = new Array();
var promises = [];
for (i = 1; i <= $scope.count; i++) {
var tableNo = i;
$scope.getHttp(i).then(function(resp){
$scope.documentsList.push(resolutions[0]);
$scope.tableHeaders.push(resolutions[1]);
});
}
}
});
$scope.getHttp = function(tableNo){
var promise;
promise = $http({
url:'http://localhost:3000/doclist/' + tableNo,
method:'get'
});
promises.push(promise);
promise = $http({
url:'http://localhost:3000/tableColumns/' + tableNo,
method:'get'
});
promises.push(promise);
return $q.all(promises);
}
Make the changes accordingly. I did not test it because dont have complete controller.But for sure, if you are looping http, make sure promises are stored.
I Have this code in my project. I try to add data from database using $http, but ng-repeat doesn't update de table, only shows a blank row.
When I check the scope, data is already there.
I've read many answers but they does not seem to be related to my problem.
<div ng-controller="TweetsController">
<table class="table table-striped table-bordered table-hover" width="100%">
<thead>
<tr>
<th class="col-md-5"><i class="fa fa-fw fa-twitter text-muted hidden-md hidden-sm hidden-xs"></i> Texto</th>
<th class="col-md-1 text-center"> Lista</th>
<th class="col-md-1 text-center"> Cuenta</th>
<th class="col-md-1 text-center"> Red</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="tuit in filtrado">
<td>{{tuit.texto}}</td>
<td class="text-center">{{tuit.lista.nombre}}</td>
<td class="text-center">{{tuit.lista.cuenta.nombre}}</td>
<td class="text-center">{{tuit.lista.cuenta.red.tipo}}</td>
</tr>
</tbody>
</table>
<div>
<pagination total-items="ufilter.length" itemsPerPage="itemsPerPage" ng-model="currentPage"></pagination>
</div>
</div>
Controller:
.controller('TweetsController', ['$scope','$http','filterFilter', function($scope,$http,filterFilter) {
$scope.currentPage = 1;
$scope.itemsPerPage = 10;
$scope.filtrado = [];
$scope.setPage = function (pageNo) {
$scope.currentPage = pageNo;
};
// retrieve tweets
$http.get('admin/twitter').success(function(tweets) {
$scope.tweets = tweets;
});
$scope.saveTweet = function(isValid) {
if(isValid) {
var tuit = {
texto: $scope.texto,
lista_id: $scope.lista
};
$http.post('admin/twitter', tuit).success(function(t) {
$scope.tweets.push(t);
});
}
};
$scope.filtrar = function(filtro) {
if($scope.tweets != undefined) {
$scope.ufilter = filterFilter(filtro, $scope.buscar);
var inicio = ($scope.currentPage - 1) * $scope.itemsPerPage;
var fin = inicio + $scope.itemsPerPage;
$scope.filtrado = $scope.ufilter.slice(inicio, fin);
}
};
$scope.$watch('tweets', function() {
$scope.filtrar($scope.tweets);
});
$scope.$watch('currentPage', function() {
$scope.filtrar($scope.tweets);
});
$scope.$watch('buscar', function() {
$scope.filtrar($scope.tweets);
$scope.setPage(1);
});
}])
EDIT:
I Solved it!
The problem is the way how the retrieve data is wrapped
$scope.tweets.push(t[0])
You need to apply to the scope
$http.get('admin/twitter').success(function(tweets) {
$scope.tweets = tweets;
$scope.$apply()
});
This is a great blog post that explains it:
http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
The reason why your ng-repeat is not updating after the $http request is due to the $http is async and your javascript turn for the controller has finished before the response is back from your $http request. So you must notify the scope that things have changed and push it to the scope.
The problem is the way how the retrieve data is wrapped
instead of this:
$http.post('admin/twitter', tuit).success(function(t) {
$scope.tweets.push(t);
});
this:
$http.post('admin/twitter', tuit).success(function(t) {
$scope.tweets.push(t[0]);
});
There is the easy way and the correct way.
This is the easy way
//Utility Functions
function Digest($scope) {
//this may not be future proof, if it isn't then you may have to try $timeout(function(){//do something})
if (!$scope.$$phase) {
try {
//using digest over apply, because apply fires at root, and I generally don't need root.
$scope.$digest();
}
catch (e) { }
}
//console.log('$scope snapshot:', $scope);
}
In your code use this by calling
$http.post('admin/twitter', tuit).success(function(t) {
Digest($scope.tweets.push(t));
});
This is the correct way.
https://docs.angularjs.org/api/ng/service/$q
Have your HTTP call wrapped in a $q promise. Then when it is either succeed or error, then fulfill the promise. The repeater will honor the promise.
For anyone else who may run into this issue where the UI just isn't updating - even after doing an $apply or sticking within a $timeout. I've just realised I made a stupid mistake and had added :: to the embedded property and forgotten about it.
So I had my ng-repeat and inside I had a {{ ::item.name }}. For those of you who don't know what the :: does; it tells angular to set the data and not set any additional watches, which in turn then stops angular trying to update that property. This is useful for data that you know won't ever change. See the below link for more information on one time bindings:
http://blog.thoughtram.io/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html
So my solution was just to remove the :: so: {{ ::item.name }} became {{ item.name }}
Being quite new to angular, I am searching the best way to achieve a quite simple task.
My aim is to update in a database, through angular $resource service, the order (I have a position attribute) of a Project model.
I have the following template structure :
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Date</th>
<th>Link</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody id="sortable" my-drag-list>
<tr ng-repeat="project in projects" ng-class="{warning: !project.publish}">
<td></td>
<td>{{project.title}}</td>
<td>{{project.date|date: 'MMMM yyyy'}}</td>
<td><a ng-href="{{project.link}}" target="blank">{{project.link}}</a></td>
<td><a ng-href="#/projects/{{project.id}}/"><span class="glyphicon glyphicon-edit"></span></a></td>
<td><a ng-click="deleteProject(project)"><span class="glyphicon glyphicon-trash"></span></a></td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-success" ng-show="data.saveSort" my-update-sort-button>Save new sort</button>
tr in my tbody element are movable. When I click on the button element (which is out of the scope created by the ng-repeat directive), I want to update my database whith new position values, which are determined by the dom position of each tr (upper tr will have a smaller position value).
At first glance, I intends to do a my-update-sort-button directive with the following :
var link = function(scope, el){
el.on('click', function(){
var els = el.parent().find('tr');
for(var i = 0, len = els.length; i<len; i++){
Projects.update({id: els.eq(i).data('projectId')}, {position: i});
}
});
};
But I am not sure about the "quality" of such solution. I do not like the fact of adding data-project-id attribute on my tr element.
Thanks for any ideas or solutions for this case !
You need to keep info about "project" position in it's model. When "project" is moved you save this info to its model. Do it in "my-drag-list".
When click to update button you just send info from model without scanning the DOM:
var link = function(scope, el){
el.on('click', function(){
var model;
for(var i = 0, len = scope.model.projects.length; i<len; i++){
model = scope.model.projects[i];
Projects.update({id: model .id}, {position: model.position});
}
});
};
Even better don't send a lot of requests to server. Send one request with all info together.
I am a student and learning angularJS for my project. Please bear with me for any silly questions.
I am trying to iterate through an array of challenges using ng-repeat[myChallenge in ListChallengesICreated.challenges]. In the mean time, each challenges, I am trying to call another function to pass an array of badge id, which return an array of badges URL's belongs to a that specific challenge.I also need to iterate through that badges URL's using ng-repeat[badge in filtered_badges] as well to display badges along each challenges.
But second ng-repeat only run after first ng-repeat is completed. So all the challenges display same badges pictures instead of it respective badges. Is there a way to run second ng-repeat concurrently while first being executed?
HTML code:
<table class="table table-condensed" style="border-style:solid; border-color:blue;width:100%" ng-init="list_challenges_I_created();">
<tr **ng-repeat="myChallenge in ListChallengesICreated.challenges" ** ng-init="get_badge(myChallenge.unlockRequiredBadges)">
<td class="cMName">{{myChallenge.name}}</td>
<td class="cMLocation" **ng-repeat="badge in filtered_badges" **>
<input type="image" ng-src="{{badge.imageURL}}" />
</td>
<td class="cMSDate">{{myChallenge.startDate}}</td>
<td class="cMEDate">{{myChallenge.endDate}}</td>
<td class="cMReview">
<div class="btn-group">
<button class="btn btn-warning" a data-target="#statsModal" role="button" data-toggle="modal">Stats</button>
<button class="btn btn-danger" a data-target="#tab2create">Edit</button>
</div>
</td>
<td class="cMPublish">nmfv</td>
<td class="cMShare">nmfnvm</td>
</tr>
</table>
JS File (which returns badges)
$scope.get_badge = function (allRequiredBadges) {
var path_badges = [];
var get_all_badges = {};
var all_badges = {};
var all_badges_URL = [];
$scope.filtered_badges = {};
path_badges = allRequiredBadges;
get_all_badges = $resource('/jsonapi/all_badges');
get_all_badges.get({}, function (response) {
all_badges = response;
for (var j = 0; j < path_badges.length; j++) {
for (var i = 0; i < all_badges.badges.length; i++) {
if (all_badges.badges[i].id == path_badges[j]) {
all_badges_URL.push(all_badges.badges[i]);
}
}
}
$scope.filtered_badges = all_badges_URL;
})
}
Actually the problem is not in ng-repeat directive but in incorrect scope usage. I think your $scope.get_badge function is defined in the controller and $scope used there is the controller's scope. So when get_badge called for 2 challenges then this line:
$scope.filtered_badges = all_badges_URL;
overwrites the previouly loaded badges, that's why you get the same badges for all challenges. The simplest solution is to but badges collection inside challenge object, like this:
In javascript:
myChallenge.filtered_badges = ...
In html:
<tr ng-repeat="myChallenge in ListChallengesICreated.challenges" ng-init="get_badge(myChallenge)">
<td class="cMLocation" ng-repeat="badge in myChallenge.filtered_badges" >
<input type="image" ng-src="{{badge.imageURL}}" />
</td>
I have a a and I would like to sort my list alphabetically (I don't want caps to matter) according to a class named "name". How would I do this?
<ul class="column">
<li>
<table>
<tr>
<td class="name" >Name of Item</td>
</tr>
<tr>
<td>Content</td>
</tr>
<tr>
<td>morecontent</td>
<td>morecontent</td>
</tr>
</table>
</li>
<li>
<table>
<tr>
<td class="name" >Another name of item</td>
</tr>
<tr>
<td>Content</td>
</tr>
<tr>
<td>morecontent</td>
<td>morecontent</td>
</tr>
</table>
</li>
</ul>
Thanks
Using jQuery, this should do it:
function sort() {
$($('ul.column>li').get().reverse()).each(function(outer) {
var sorting = this;
$($('ul.column>li').get().reverse()).each(function(inner) {
if($('td.name', this).text().localeCompare($('td.name', sorting).text()) > 0) {
this.parentNode.insertBefore(sorting.parentNode.removeChild(sorting), this);
}
});
});
}
The above is a little dense though, so if you want to understand what's going on, let's break it down line-by-line:
function sort() {
//get each <li> which is a child of <ul class="column">
//for each element in the results, execute a function
//also, we reversed the order (e.g. start at the bottom and go up
$($('ul.column>li').get().reverse()).each(function(outer) {
//this is the current <li> we're running against
var sorting = this;
//get the same set of elements again in their current state,
//so we can figure out where to put this one
$($('ul.column>li').get().reverse()).each(function(inner) {
//get the inner text of the <td class="name">
//for the item we're trying to replace,
//and for the current item in the inner loop
//use localeCompare to compare the two strings alphabetically
if($('td.name', this).text().localeCompare($('td.name', sorting).text()) > 0) {
//if the one we're trying to sort goes after the current one
//alphabetically, remove it from its current position
//and insert it after the current one
this.parentNode.insertBefore(sorting.parentNode.removeChild(sorting), this);
}
});
});
}
We can make it a little more reusable by passing in the selector for the list and the key:
sort('ul.column>li', 'td.name');
function sort(list, key) {
$($(list).get().reverse()).each(function(outer) {
var sorting = this;
$($(list).get().reverse()).each(function(inner) {
if($(key, this).text().localeCompare($(key, sorting).text()) > 0) {
this.parentNode.insertBefore(sorting.parentNode.removeChild(sorting), this);
}
});
});
}
Do keep in mind this requires jQuery, so you'll need a reference to it in your <head>:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
And this function should be called at some point in the page after the list is written in the HTML.
Mine answer is longer :p but work.
function SortLIs() {
var ColumnUL = $("ul.column");
var Columns = $(ColumnUL).children("li");
var ColumnNames = new Array();
var Columns_byName = new Array();
var Columns_Count = Columns.length;
for(var i = 0; i < Columns_Count; i++) {
var aColumn = Columns[i];
var aTD = $(aColumn).find(".name");
var aTDName = aTD.text();
ColumnNames.push(aTDName);
Columns_byName[aTDName] = aColumn;
$(aColumn).remove();
}
ColumnNames.sort(function(a, b){
return (a > b) - (a < b);
});
for(var i = 0; i < Columns_Count; i++) {
var aName = ColumnNames[i];
ColumnUL.append(Columns_byName[aName]);
}
}
EDIT: I saw you said that you are not good at JS. So here is the bigger picture for you.
(1) Add The following code to the header of the HTML. This will use jQuery library.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
(2) Add the 'sortLIs' code just right after the above code
<script>
<!--
function SortILs() {
...
}
-->
</script>
(3.1) If you want the sorting to begin at the load time. Add this right after the above code.
<script>
<!--
$(document).ready(function(){
SortILs();
});
-->
</script>
(3.2) Otherwise, you call the function from an event.
Hope this helps.
Here's another approach, stealing ideas from the other answers given so far (also requiring jQuery):
function sort(elementSelector, valueSelector, ascending) {
var sign = ascending ? -1 : 1;
var elements = jQuery(elementSelector);
elements.each(function() {
this.sortKey = jQuery(valueSelector, this).text();
});
var sorted = elements.get();
sorted.sort(function(a, b) {
var keyA = a.sortKey;
var keyB = b.sortKey;
return sign * ((keyA < keyB) - (keyA > keyB));
});
elements.parent().append(sorted);
}
sort('.column>li', '.name', true)
Just seconding the jQuery response above, have a look at this tutorial:
http://www.shopdev.co.uk/blog/sortable-lists-using-jquery-ui/
For semantics, you might be better off also placing the classname inside the actual <li> tag.
The use of a table inside a list aside though, you may want to post an example page to help further.