I am not at all an HTML expert. I program microcontrollers and got off onto a tangent.
I created an html doc to show a table of microcontroller registers, register addresses and register descriptions. I created a table with 3 columns - and about 120 rows. Some of the register addresses are bit addressable - if their addresses end in 0 or 8.
I wanted to highlight these 'special' register addresses - by showing them in red. So, in the table cells with a register address ending in 0 or 8, I use "" and "" to surround the address value.
My table has 3 columns, Register, Address, and Description. Then one row might look like
"ACC 0xE0 Accumulator".
I got my table all done and it looks great. Then I got the idea that I want to be able to sort the table on any column. For example, if I click on "Address" I want the table to re-display and sort by the values in that column.
I searched, and found a way to do it. It works by having a "sort" button - click on that and it re-displays sort on the first column values. I implemented a simple version of it and got it working. I then changed it to instead sort on the second column when the "sort" button was clicked.
That didn't exactly work .... because of all those "" whatevers.
The example I copied from his here:
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_sort_table
Is there anything 'simple' I can do to use this method - but still be able have certain entries in the Address column be shown red ?
I should probably stick to programming microcontrollers, but I like a challenge.
sample code for sort on a selected column,
that's free...
const
myButtonSort = document.querySelector('#my-button-sort')
, colSelector = document.querySelector('#sel-col')
, myTable_tBody = document.querySelector('#my-table > tbody')
;
myButtonSort.onclick = _ =>
{
let col = parseInt(colSelector.value );
[...myTable_tBody.querySelectorAll('tr')]
.map(row=>({row, str:row.cells[col].textContent }))
.sort((a,b)=>a.str.localeCompare(b.str))
.forEach(el=>
{
myTable_tBody.appendChild(el.row)
})
}
table {
border-collapse : collapse;
margin : 2em 1em;
}
td,th {
padding : .2em .8em;
border : 1px solid darkblue;
}
thead {
background : lightseagreen ;
}
<select id="sel-col">
<option value="0">column x</option>
<option value="1">column y</option>
</select>
<button id="my-button-sort">sort</button>
<table id="my-table">
<thead>
<tr> <th>x</th> <th>y</th> </tr>
</thead>
<tbody>
<tr> <td>aa</td><td> 1 </td></tr>
<tr> <td>zz</td><td> 2 </td></tr>
<tr> <td>ee</td><td> 3 </td></tr>
<tr> <td>cc</td><td> 4 </td></tr>
</tbody>
</table>
Ascending and descending sort example:
const myArray =
[ { worth: '100', name: 'jessca', reason: 'money', email: 'j#gmail.com', number: '4456', instagram: 'hvg_ujh', tiktok: 'hhgh.thg' }
, { worth: '265', name: 'e', reason: 'money', email: 'e#gmail.com', number: '3456', instagram: 'hvg_ujh', tiktok: 'hhgh.thg' }
, { worth: '6000', name: 'ssica', reason: 'sex', email: 's#gmail.com', number: '0456', instagram: 'hvg_ujh', tiktok: 'hhgh.thg' }
, { worth: '855', name: 'sica', reason: 'sex', email: 'ss#gmail.com', number: '9456', instagram: 'hvg_ujh', tiktok: 'hhgh.thg' }
, { worth: '8679', name: 'ica', reason: 'sex', email: 'i#gmail.com', number: '0756', instagram: 'hvg_ujh', tiktok: 'hhgh.thg' }
, { worth: '1', name: 'ca', reason: 'money', email: 'c#gmail.com', number: '14856', instagram: 'hvg_ujh', tiktok: 'hhgh.thg' }
]
const
t_Head = document.querySelector('#myTable thead')
, t_Head_THs = document.querySelectorAll('#myTable thead tr th')
, th_list = [...t_Head_THs].map( TH => TH.dataset.column)
, t_Body = document.querySelector('#myTable tbody')
, sortOrder = [ 'none' ,'asc', 'desc' ]
, sortType = { worth: 'num', name:'str', reason:'str', email:'str', number:'num', instagram:'str', tiktok:'str' }
, sortProcess =
{ 'asc:num' : (a,b) => +a.str - +b.str
, 'desc:num' : (a,b) => +b.str - +a.str
, 'asc:str' : (a,b) => a.str.localeCompare(b.str)
, 'desc:str' : (a,b) => b.str.localeCompare(a.str)
};
myArray.forEach( row =>
{
let TR = t_Body.insertRow()
for (col of th_list)
TR.insertCell().textContent = row[col]
})
t_Head.onclick = ({target}) =>
{
if (!target.matches('th')) return
let
dataOrder = sortOrder[(sortOrder.indexOf(target.dataset.order) +1 )% sortOrder.length]
, dataType = sortType[target.dataset.column]
;
t_Head_THs.forEach( TH => { TH.dataset.order = (TH===target) ? dataOrder : 'none' })
if (dataOrder !== 'none')
{
[...t_Body.querySelectorAll('tr')]
.map ( row => ({row, str:row.cells[target.cellIndex].textContent }))
.sort ( sortProcess[`${dataOrder}:${dataType}`])
.forEach ( elm => t_Body.appendChild(elm.row ))
}
}
body {
font-family : Arial, Helvetica, sans-serif;
font-size : 16px;
}
table {
border-collapse : separate;
border-spacing : 1px;
background-color : darkblue;
margin : 1em;
}
th, td {
border : none;
background : whitesmoke;
padding : .3em .4em;
}
th {
background-color : #76ced1;
white-space : nowrap;
cursor : pointer;
}
th::before {
content : attr(data-column) ' ';
text-transform : capitalize;
}
th[data-order=asc]::after { content : '\25B2'; }
th[data-order=desc]::after { content : '\25BC'; }
th[data-order=none]::after { content : '\25B2'; color:transparent; } /* get the same width */
<table id="myTable">
<thead>
<tr>
<th data-column="worth" data-order="none"></th>
<th data-column="name" data-order="none"></th>
<th data-column="reason" data-order="none"></th>
<th data-column="email" data-order="none"></th>
<th data-column="number" data-order="none"></th>
<th data-column="instagram" data-order="none"></th>
<th data-column="tiktok" data-order="none"></th>
</tr>
</thead>
<tbody></tbody>
</table>
This might get you started:
const table = document.getElementById("test"),
th = test.querySelectorAll("th"),
sortDefault = 0, //default sorted column number
orderDefault = 0; //default order: 0 = ascending, 1 = descending
table.dataset.sort = sortDefault;
table.dataset.order = orderDefault;
/* add click listeners on table headers */
for(let i = 0; i < th.length; i++)
{
th[i].addEventListener("click", e =>
{
/* if this column was sorted, change it's order */
if (+table.dataset.sort == e.target.cellIndex)
table.dataset.order = +table.dataset.order ? 0 : 1;
/* tell table which column is currently sorted */
table.dataset.sort = e.target.cellIndex;
sortColumn();
});
}
populateTable(); //fill table with random data
sortColumn(); //initial sort of the table
function sortColumn()
{
const rows = Array.from(table.children);
rows.splice(0, 1); //remove header from the list
rows.sort((a, b) =>
{
a = a.children[table.dataset.sort].textContent;
b = b.children[table.dataset.sort].textContent;
a = a.replace(/\W/g, ""); //remove non alphanumerical characters
b = b.replace(/\W/g, ""); //remove non alphanumerical characters
a = a.replace(/0x[a-fA-F0-9]+/, n => Number(n)); //convert 0xHEX to decimal
b = b.replace(/0x[a-fA-F0-9]+/, n => Number(n)); //convert 0xHEX to decimal
return a.localeCompare(b, undefined, {numeric: true, sensitivity: 'base'})
});
if (+table.dataset.order)
rows.reverse();
for(let i = 0; i < rows.length; i++)
{
table.appendChild(rows[i]);
}
}
function populateTable()
{
for(let i = 0, _tr = document.createElement("tr"), _td = document.createElement("td"); i < 10; i++)
{
const tr = _tr.cloneNode(true);
let td = _td.cloneNode(true);
td.textContent = "reg " + rand(0, 199);
tr.appendChild(td);
td = _td.cloneNode(true);
td.textContent = "address 0x" + rand(0, 255).toString(16);
if (rand(0,1))
td.textContent = td.textContent.replace(/ (.*)/, ' "$1"');
tr.appendChild(td);
td = _td.cloneNode(true);
td.textContent = "desc " + rand(0, 99999).toString(16);
tr.appendChild(td);
table.appendChild(tr);
}
function rand(min, max)
{
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
#test
{
border-collapse: collapse;
}
#test tr td
{
border: 1px solid black;
}
#test td
{
padding: 0.5em 1em;
}
#test th
{
user-select: none;
cursor: pointer;
}
#test th:after
{
visibility: hidden;
}
#test[data-order="0"] th:after
{
content: "▲";
}
#test[data-order="1"] th:after
{
content: "▼";
}
#test[data-sort="0"] th:nth-child(1):after,
#test[data-sort="1"] th:nth-child(2):after,
#test[data-sort="2"] th:nth-child(3):after
{
visibility: visible;
}
<table id="test">
<tr>
<th>Register</th>
<th>Address</th>
<th>Description</th>
</tr>
</table>
Related
I'm using ajax to load json data into several accordion tables on the same html page. So far, I have the same function written twice to output 2 tables, but I need 16 more tables. Having the same function written out 18 times doesn't seem dry at all, so I'm thinking there must be better way.
let myArray = [];
$.ajax({
method: "GET",
url: "https://chrisjschreiner.github.io/data/verbs1.json",
success: function (response) {
myArray = response;
buildTable1(myArray[0]);
buildTable2(myArray[1]);
},
});
let buildTable1 = (data) => {
let table = document.getElementById("myTable1");
for (const [key, value] of Object.entries(data)) {
let row = `<tr>
<td>${key}</td>
<td>${value}</td>
</tr>`;
table.innerHTML += row;
}
};
let buildTable2 = (data) => {
let table = document.getElementById("myTable2");
for (const [key, value] of Object.entries(data)) {
let row = `<tr>
<td>${key}</td>
<td>${value}</td>
</tr>`;
table.innerHTML += row;
}
};
Any suggestions?
In order to avoid rewriting functions, pass in variables:
buildTable(selector, response, ...indices)
Parameters
selector: string - selector of the element that'll contain all of the <table>s
response: array of objects - the parsed JSON from the server
...indices: '*' or comma delimited list of numbers - the spread operator will gather all of the numbers or '*' into an array
If '*' is passed, it will be an array of numbers that represent all of the indices within response.
'*' passed as [0 to data.length-1]
If number(s) are passed, those will represent the index of each object of response. The following example would be for the first, third and seventh object of response
0, 2, 6 passed as [0, 2, 6]
The following example will create a <table> for each applicable object within the array
$.ajax({
method: "GET",
url: "https://chrisjschreiner.github.io/data/verbs1.json",
success: function(response) {
buildTable1('main', response, '*');
},
});
const buildTable1 = (selector, response, ...indices) => {
const node = document.querySelector(selector);
const data = JSON.parse(JSON.stringify(response));
const idcs = indices.includes('*') ? data.map((o, i) => i) : indices;
const table = document.createElement('table');
data.forEach((obj, idx) => {
if (idcs.includes(idx)) {
let tClone = table.cloneNode(true);
node.appendChild(tClone);
Object.entries(obj).forEach(([key, val]) => {
let row = tClone.insertRow();
row.innerHTML = `<td>${key}</td><td>${val}</td>`;
});
}
});
};
table {
width: 80%;
margin: 10px;
table-layout: fixed;
border: 1px solid black;
border-collapse: collapse;
}
td {
padding: 0 5px;
border-top: 1px solid red;
vertical-align: top;
}
tr:first-child td {
border-top: 0px solid transparent;
}
td:first-child {
width: 35%;
border-right: 3px dotted red;
}
<main>
<!--Tables will be added here -- selector = 'main'-->
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I'm trying to display my DataTables table with 3 columns (ID, Name, Date) showing values in my first column (ID) as URLs:
Edited as per #andrewjames request in comments:
<head>
<link rel='stylesheet' href='https://cdn.datatables.net/v/dt/dt-1.10.15/datatables.min.css'>
<script src='https://code.jquery.com/jquery-1.12.4.min.js'></script>
<script src='https://cdn.datatables.net/v/dt/dt-1.10.15/datatables.min.js'></script>
<script src='https://cdn.datatables.net/scroller/2.0.3/js/dataTables.scroller.min.js'></script>
</head>
<body translate="no" >
<table id="example" class="display" cellspacing="0" width="75%">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Date</th>
</tr>
</thead>
<tfoot>
<tr>
<th>ID</th>
<th>Name</th>
<th>Date</th>
</tr>
</tfoot>
</table>
<script id="rendered-js" >
$(document).ready(function () {
$('#example').DataTable({
"ajax": "url/to/data3.json",
"columns": [
{ "data": "ID" },
{ "data": "Name" },
{ "data": "Date" },
{
"data": "ID",
"render": function(data, type, row, meta){
if(type === 'display'){
data = '<a href="https://test.com"'+ data +'>' + data + '</a>';
}
return data;
}
}
],
"order": [[ 2, "desc" ]],
"paging": true,
"deferRender": true,
"scrollY": 500,
"scrollCollapse": true,
"scroller": true,
"initComplete": function () {
this.api().row( 50000 ).scrollTo();
}
} );
} );
</script>
</body>
Error I'm getting: TypeError: undefined is not an object (evaluating 'n[m].style')
it relates to the following part of datatables.min.js:
else {
j = h(b).clone().css("visibility", "hidden").removeAttr("id");
j.find("tbody tr").remove();
var t = h("<tr/>").appendTo(j.find("tbody"));
j.find("thead, tfoot").remove();
j.append(h(a.nTHead).clone()).append(h(a.nTFoot).clone());
j.find("tfoot th, tfoot td").css("width", "");
n = ta(a, j.find("thead")[0]);
for (m = 0; m < i.length; m++)
p = c[i[m]], n[m].style.width = null !== p.sWidthOrig && "" !== p.sWidthOrig ? v(p.sWidthOrig) : "", p.sWidthOrig && f && h(n[m]).append(h("<div/>").css({
width: p.sWidthOrig,
margin: 0,
padding: 0,
border: 0,
height: 1
}));
Could someone tell me what am I doing wrong? thank you in advance!
Your DataTable defines 4 columns: ID, Name, Date, and the link.
It does not define any column title values, only the column data values - therefore it relies on the HTML table for these. However, there are only 3 columns defined. Add that 4th missing <tr> to both the <thead> and <tfoot> sections.
That should help to resolve the specific error you are getting.
I have a list of languages, and I want to render them each as buttons within a column, within a div. I want to be able to render them based on a variable I set, buttonsPerColumn.
For example, if I have 40 languages, and four columns, I will render 10 buttons per column. If I have 36 languages, I will render 10 buttons for the first three, and 6 for the remainder. However, I am at a loss for how to do this. I have console logged my desired output, however, I would like this in button form. How can I create all of the column and button divs I need within my method, and then output them all at once?
css
.languageList {
position: absolute;
height: 35%;
width: 25%;
left: 43%;
top: 15%;
background-color: #b6bbf4;
display: flex;
justify-content: space-between;
}
.languageColumn {
position: relative;
width: 25%;
height: 100%;
background-color: red;
}
languagelist.jsx
class LanguageList extends Component {
render() {
this.renderButtons();
return (
<React.Fragment>
<div className="languageList">
<div className="languageColumn" />
</div>
</React.Fragment>
);
}
renderButtons = () => {
let buttonsPerColumn = 6;
const languages = LanguageList.getLanguages();
const myArray = LanguageList.arrayFromObject(languages);
var i, language;
let column = 0;
for (i = 0; i < myArray.length; i++) {
language = myArray[i];
console.log("Render " + language + " in column " + column);
if (i == buttonsPerColumn) {
column++;
buttonsPerColumn += buttonsPerColumn;
}
}
};
static arrayFromObject = object => {
var myArray = [];
var key;
for (key in object) {
myArray.push(key);
}
return myArray;
};
static getLanguages = () => {
return {
Azerbaijan: "az",
Albanian: "sq",
Amharic: "am",
English: "en",
Arabic: "ar",
Armenian: "hy",
Afrikaans: "af",
Basque: "eu",
German: "de",
Bashkir: "ba",
Nepali: "ne"
};
};
}
Link to code sandbox:
https://codesandbox.io/s/practical-chatelet-bq589
Try this:
import React, { Component } from "react";
class LanguageList extends Component {
render() {
return (
<React.Fragment>
<div className="languageList">{this.renderButtons()}</div>
</React.Fragment>
);
}
renderButtons = () => {
const buttonsPerColumn = 6;
const languages = LanguageList.getLanguages();
const myArray = LanguageList.arrayFromObject(languages);
const columns = [];
for (let i = 0; i < myArray.length; i++) {
const columnIndex = Math.floor(i / buttonsPerColumn);
if (!columns[columnIndex]) columns[columnIndex] = [];
columns[columnIndex].push(
<button className="languageButton">{myArray[i]}</button>
);
}
return columns.map((singleColumn, index) => (
<div key={index} className="languageColumn">
{singleColumn}
</div>
));
};
static arrayFromObject = object => {
var myArray = [];
var key;
for (key in object) {
myArray.push(key);
}
return myArray;
};
static getLanguages = () => {
return {
Azerbaijan: "az",
Albanian: "sq",
Amharic: "am",
English: "en",
Arabic: "ar",
Armenian: "hy",
Afrikaans: "af",
Basque: "eu",
German: "de",
Bashkir: "ba",
Nepali: "ne"
};
};
}
export default LanguageList;
I have the following class that gets sent as an IEnumerable from an API:
public class LogsDto
{
public decimal Value { get; set; }
public DateTime Time_Stamp { get; set; }
public string TagName { get; set; }
}
This is the Angular class that the data is passed into as an array:
export class Logs {
value: number;
timestamp: string;
tagName: string;
}
Sample data will come across as so:
{ "value": 100, "time_Stamp": "2017-05-04T00:07:47.407", "tagName": "Tag 1" },
{ "value": 200, "time_Stamp": "2017-05-04T00:07:47.407", "tagName": "Tag 2" },
{ "value": 300, "time_Stamp": "2017-05-04T00:07:47.407", "tagName": "Tag 3" },
{ "value": 150, "time_Stamp": "2017-05-04T00:07:57.407", "tagName": "Tag 1" },
{ "value": 250, "time_Stamp": "2017-05-04T00:07:57.407", "tagName": "Tag 2" },
{ "value": 350, "time_Stamp": "2017-05-04T00:07:57.407", "tagName": "Tag 3" }
In Angular, I want to turn this into a table that is read like so:
<table>
<th>Time_Stamp</th> <th>Tag 1</th> <th>Tag 2</th> <th>Tag 3</th>
<td>2017-05-04T00:07:47.407</td> <td>100</td> <td>200</td> <td>300</td>
<td>2017-05-04T00:07:45.407</td> <td>150</td> <td>250</td> <td>350</td>
</table>
I can hard code it fine as shown above. The problem I am having is I don't know how to parse the data from columns to rows. The "tagName" property will not always be the same. Sometimes there will be 10 different tag names.
Any help is appreciated.
This is how I ended up making it work. It is pretty fast, but I am sure I am not doing it the most efficient way. Would appreciate constructive criticism.
parseLogs(logs: Logs[]): void {
const ts: string[] = logs.map(data => data.time_Stamp.toString());
var timestamps = ts.filter((x, i, a) => x !== undefined && a.indexOf(x) === i);
var jsonString: string = '[';
for (var j = 0; j < timestamps.length; j++) {
var date = new Date(timestamps[j]);
var hours = date.getHours().toString();
if (date.getHours() < 10)
hours = '0' + hours;
var minutes = date.getMinutes().toString();
if (date.getMinutes() < 10)
minutes = '0' + minutes;
var dtString: string = (date.getMonth() + 1) + '-' + date.getDate() + '-' + date.getFullYear() + ' ' + date.getHours() + ':' + date.getMinutes();
jsonString = jsonString + '{"Time Stamp":"' + dtString + '"';
for (var i = 0; i < logs.length; i++) {
if (logs[i].time_Stamp === timestamps[j])
jsonString = jsonString + ',"' + logs[i].tagName + '":' + logs[i].value + '';
}
if (j === (timestamps.length - 1)) {
console.log('j:' + j + 'logs.length:' + logs.length.toString());
jsonString = jsonString + '}';
} else {
console.log('j:' + j + 'logs.length:' + logs.length.toString());
jsonString = jsonString + '},';
}
}
jsonString = jsonString + ']';
console.log(jsonString);
this.myLogs = JSON.parse(jsonString);
//From example shown above
this.generateColumnHeaders(this.myLogs);
A quick way to solve it would be to find all the unique properties off the objects being returned through reflection and store them. Then you could render out a header, then render each object and access via the columns in a nested set of ngFors. This, of course, requires the ordering to remain the same as you evaluate over the columns, incidentally. You'll also want to perform a projection of your data to perform something like a pivot. Then we will perform a group by of the data by timestamp and for that we will 100% steal someone else's SO group by function.
The control order is pivot then column header eval.
The column eval:
generateColumnHeaders(myObjectArray) {
this.columns = [];
for(var i = 0; i < myObjectArray.length; ++i) {
for(var col in myObjectArray[i]) {
if(myObjectArray[i].hasOwnProperty(col)) {
if(this.columns.findIndex(colmn => colmn === col) === -1) {
this.columns.push(col);
}
}
}
}
}
The pivot and other transforms:
transformArray(objectArray, accessObject) {
return this.groupByArray(objectArray.map(obj =>
{
var tagNameString = obj.tagName;
let tempObj = {};
tempObj[accessObject.pivotColumn] = obj.time_Stamp;
tempObj[obj.tagName] = obj.value;
return tempObj;
}), accessObject.pivotColumn).map(tsg => {
let tempObj = {};
tempObj[accessObject.pivotColumn] = tsg.time_Stamp;
for(var i = 0; i < tsg.values.length; ++i) {
for(var tag in tsg.values[i]) {
if(tag.indexOf(accessObject.dynamicColumnCommon !== -1)) {
tempObj[tag] = tsg.values[i][tag];
}
}
}
return tempObj;
});
}
Controlling code:
this.myObjectArray = this.transformArray(this.myObjectArray, { pivotColumn: "time_Stamp", dynamicColumnCommon:"Tag"});
this.generateColumnHeaders(this.myObjectArray);
The template evaluation:
<table>
<tr>
<th *ngFor="let column of columns">{{column}}</th>
</tr>
<tr *ngFor="let obj of myObjectArray">
<td *ngFor="let column of columns">{{obj[column]}}</td>
</tr>
</table>
Plunker
I am trying to display data from mysql data base in HTLM-css (used bootstrap) tabel. But the query result is not displaying as expected in the order of 'ID'. Can somebody help me to solve my issue?
Thanks in advance.
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered" id="example">
<div class="alert alert-info">
<strong><center>Delete Multiple Data:Check the Box and click the Delete button to Delete Data </strong></center>
</div>
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th>Value Date</th>
<th>Type</th>
<th>Amount</th>
<th>Donated By</th>
<th>Paid To</th>
</tr>
</thead>
<tbody>
<?php
$query=mysql_query("select * from temple_txn order by id desc")or die(mysql_error());
while($row=mysql_fetch_array($query)){
$id=$row['id'];
?>
<tr>
<td><input name="selector[]" type="checkbox" value="<?php echo $id; ?>"></td>
<td><?php echo $row['id'] ?></td>
<td><?php echo $row['value_date'] ?></td>
<td><?php echo $row['txn_type'] ?></td>
<td><?php echo $row['txn_amount'] ?></td>
<td><?php echo $row['donated_by'] ?></td>
<td><?php echo $row['paid_to'] ?></td>
</tr>
<?php } ?>
</tbody>
</table>
(JS DT_bootstrap)
/* Set the defaults for DataTables initialisation */
$.extend( true, $.fn.dataTable.defaults, {
"sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>",
"sPaginationType": "bootstrap",
"oLanguage": {
"sLengthMenu": "_MENU_ records per page"
}
} );
/* Default class modification */
$.extend( $.fn.dataTableExt.oStdClasses, {
"sWrapper": "dataTables_wrapper form-inline"
} );
/* API method to get paging information */
$.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
{
return {
"iStart": oSettings._iDisplayStart,
"iEnd": oSettings.fnDisplayEnd(),
"iLength": oSettings._iDisplayLength,
"iTotal": oSettings.fnRecordsTotal(),
"iFilteredTotal": oSettings.fnRecordsDisplay(),
"iPage": oSettings._iDisplayLength === -1 ?
0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
"iTotalPages": oSettings._iDisplayLength === -1 ?
0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
};
};
/* Bootstrap style pagination control */
$.extend( $.fn.dataTableExt.oPagination, {
"bootstrap": {
"fnInit": function( oSettings, nPaging, fnDraw ) {
var oLang = oSettings.oLanguage.oPaginate;
var fnClickHandler = function ( e ) {
e.preventDefault();
if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) {
fnDraw( oSettings );
}
};
$(nPaging).addClass('pagination').append(
'<ul>'+
'<li class="prev disabled">← '+oLang.sPrevious+'</li>'+
'<li class="next disabled">'+oLang.sNext+' → </li>'+
'</ul>'
);
var els = $('a', nPaging);
$(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
$(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler );
},
"fnUpdate": function ( oSettings, fnDraw ) {
var iListLength = 5;
var oPaging = oSettings.oInstance.fnPagingInfo();
var an = oSettings.aanFeatures.p;
var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
if ( oPaging.iTotalPages < iListLength) {
iStart = 1;
iEnd = oPaging.iTotalPages;
}
else if ( oPaging.iPage <= iHalf ) {
iStart = 1;
iEnd = iListLength;
} else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) {
iStart = oPaging.iTotalPages - iListLength + 1;
iEnd = oPaging.iTotalPages;
} else {
iStart = oPaging.iPage - iHalf + 1;
iEnd = iStart + iListLength - 1;
}
for ( i=0, ien=an.length ; i<ien ; i++ ) {
// Remove the middle elements
$('li:gt(0)', an[i]).filter(':not(:last)').remove();
// Add the new list items and their event handlers
for ( j=iStart ; j<=iEnd ; j++ ) {
sClass = (j==oPaging.iPage+1) ? 'class="active"' : '';
$('<li '+sClass+'>'+j+'</li>')
.insertBefore( $('li:last', an[i])[0] )
.bind('click', function (e) {
e.preventDefault();
oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength;
fnDraw( oSettings );
} );
}
// Add / remove disabled classes from the static elements
if ( oPaging.iPage === 0 ) {
$('li:first', an[i]).addClass('disabled');
} else {
$('li:first', an[i]).removeClass('disabled');
}
if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) {
$('li:last', an[i]).addClass('disabled');
} else {
$('li:last', an[i]).removeClass('disabled');
}
}
}
}
} );
/*
* TableTools Bootstrap compatibility
* Required TableTools 2.1+
*/
if ( $.fn.DataTable.TableTools ) {
// Set the classes that TableTools uses to something suitable for Bootstrap
$.extend( true, $.fn.DataTable.TableTools.classes, {
"container": "DTTT btn-group",
"buttons": {
"normal": "btn",
"disabled": "disabled"
},
"collection": {
"container": "DTTT_dropdown dropdown-menu",
"buttons": {
"normal": "",
"disabled": "disabled"
}
},
"print": {
"info": "DTTT_print_info modal"
},
"select": {
"row": "active"
}
} );
// Have the collection use a bootstrap compatible dropdown
$.extend( true, $.fn.DataTable.TableTools.DEFAULTS.oTags, {
"collection": {
"container": "ul",
"button": "li",
"liner": "a"
}
} );
}
/* Table initialisation */
$(document).ready(function() {
$('#example').dataTable( {
"sPaginationType": "bootstrap",
"oLanguage": {
}
} );
} );
It seems that you fetch the data from your database in the correct order. Based on our conversation in the question's comments, I think that your problem is caused by the JS library DataTables or the bootstrap plugin after pageload. To ensure this, deactivate the JavaScript and see if the rows are in correct order if they are not touched by the plugin.
If DataTables tries to sort by the values in the first column (you have some HTML in there), this could explain the seemingly random sorting. Try setting an implicit order/sort setting (reference). Your ID is in the second column and the column index is zero-based, so this would be it:
$(document).ready(function() {
$('#example').dataTable( {
"sPaginationType": "bootstrap",
"oLanguage": {},
"aaSorting": [[1,'desc']]
});
});