Im using Jquery datatables to construct a table.
My requirement is like below
This is not a static table, and we are rendering it using json data. Here I'm, rendering the rows dynamically using "aoColumns".
Is there any way to use rowspan so that the cells (1,2,David,Alex) can be spanned.
Does datatables support this kind of table ?
Datatables does not support this kind of grouping out of the box.
But, as in many cases, there is a plugin available.
It is called RowsGroup and is located here: Datatables Forums. A live example is also included.
If you change the JS part in this example to the below you will have your desired output presented to you in the output window.
$(document).ready( function () {
var data = [
['1', 'David', 'Maths', '80'],
['1', 'David', 'Physics', '90'],
['1', 'David', 'Computers', '70'],
['2', 'Alex', 'Maths', '80'],
['2', 'Alex', 'Physics', '70'],
['2', 'Alex', 'Computers', '90'],
];
var table = $('#example').DataTable({
columns: [
{
name: 'first',
title: 'ID',
},
{
name: 'second',
title: 'Name',
},
{
title: 'Subject',
},
{
title: 'Marks',
},
],
data: data,
rowsGroup: [
'first:name',
'second:name'
],
pageLength: '20',
});
} );
Here is a screenshot of the result:
I tried the RowsGroup plugin, but it achieves this just by hijacking the DataTables sort mechanism. If you tell it to group a given column, what it does for you is basically to apply a sort to that column that you can't turn off. So, if you want to sort by another column, you can't. That didn't work in my application.
Instead, here's a working fiddle for a recipe that allows you to achieve this result:
https://jsfiddle.net/bwDialogs/fscaos2n
The basic idea is to flatten all of your multi-row data into a single row. Content in your 2nd, 3rd, etc. rows are stored as a hidden <script> template tag within your first row.
It works by using DataTables' drawCallback function to manipulate the DOM once DataTables has rendered it, without confusing DataTables by having to try parsing rowspan cell content.
Since this modifies the DOM after DataTables has done its magic, your multi-row sections will stick together even with pagination, searching, and sorting.
Cheers.
add a below code and modify according to your requirement
$(window).on("load",function() {
MakeRows();
addRowspan();
$(".paginate_button").on("click", function() {
MakeRows();
addRowspan();
});
});
function MakeRows() {
var tmp_tbl = $("#dashboardDetails");
var _l = tmp_tbl.find("tr");
var _td = "",_t_td = "", old_txt = "",_t_txt = ""; _tr_count = 1;_tr_countadd = 1;
for(i = 0;i< _l.length; i ++) {
_t_td = tmp_tbl.find("tr").eq(i).find("td").eq(0).find("span");
_t_txt = $(_t_td).text();
_t_txt = _t_txt.replace(/\//,"_");_t_txt = _t_txt.replace(/\//,"_");
if (_t_txt.length > 0) {
if(_t_txt != old_txt) {
if($(_l).eq(i).hasClass(_t_txt) == false) {
_tr_count = 1;_tr_countadd = 1;
$(_l).eq(i).addClass("" + _t_txt + "").addClass(_t_txt + "_" + i);
}
old_txt = _t_txt;
} else {
_tr_count = _tr_count + 1;
if (_tr_countadd == 1) {
$(_l).eq(i).addClass("" + _t_txt + "").addClass(_t_txt + "_" + i)
.addClass("hiddenClass").addClass("maintr").attr("trcount", _tr_count).attr("addedtrcount", "maintr");
_tr_countadd = _tr_countadd + 1;
} else {
$(_l).eq(i).addClass("" + _t_txt + "").addClass(_t_txt + "_" + i)
.addClass("hiddenClass").attr("trcount", _tr_count)
}
}
}
_t_td = "";
}
}
function addRowspan() {
var t_txt = "";
var _alltrmain = $(".maintr");
var _h_td = ["0","10","11","12","13"];
for (i = 0; i <= _alltrmain.length; i ++) {
for (j = 0; j <= _h_td.length; j ++) {
t_txt = $(_alltrmain).eq(i).attr("trcount");
$(_alltrmain).eq(i).prev().find("td").eq(_h_td[j]).attr("rowspan", t_txt);
}
}
}
I defined some tags in an array:
var myArray = [
"mouse",
"common",
"malcom",
"mountain",
"melon",
"table"
];
Now I want to extract my defined tags from a string, for example
from the string: the mouse is on the desk, I want to extract the "mouse" tag
or from the string the mouse is on the table, I want to extract the tags "mouse" and "table"
This code partially works, but there is some problem:
var myArray = [
"mouse",
"common",
"malcom",
"mountain",
"melon",
"table"
];
Object.defineProperty(Array.prototype, "MatchInArray", {
enumerable: false,
value: function(value) {
return this.filter(function(currentItem) {
return currentItem.match(value);
});
}
});
function doSearch(text){
out = myArray.filter(function(currentItem){
return currentItem.toLowerCase().indexOf(text) !== -1;
});
return out;
}
myInput.oninput = function(){
var XXX = this.value;
var YYY = XXX.replace(/ /g,',');
var ZZZ = YYY.split(',');
for(var i=0; i<ZZZ.length; i++){
output.innerHTML = doSearch(ZZZ[i]);
}
//output.innerHTML = doSearch(this.value);
};
What I'm doing wrong?
DEMO
Flip the comparison of text to item so that you don't have to do all that regex and splitting:
function doSearch(text){
out = myArray.filter(function(currentItem){
return text.toLowerCase().indexOf(currentItem) !== -1;
});
return out;
}
myInput.oninput = function(){
output.innerHTML = doSearch(this.value);
};
I have a reasonably simple idea that I would like to implement.
I have an array of objects with two properties: "id" and "name" I would like to list these in a series of "p" tags that would be within a "div".
So here is the HTML:
<body>
<div id="listView"></div>
</body>
And here is the JavaScript in the tag:
sessionStorage.eventid = 2;
var playerList = [];
playerList[0].id = 0;
playerList[0].name = "Add New Player";
playerList.push({
id: 5,
name: "Asako"
});
playerList.push({
id: 6,
name: "James"
});
playerList.push({
id: 7,
name: "Brian"
});
playerList.push({
id: 8,
name: "Helmut Spargle"
});
function listAll() {
var element = document.getElementById("listView");
var div;
var node;
for (var i = 0; i < playerList.length; i++) {
div = document.createElement("div");
div.setAttribute("onClick", "javascript: formOver(" + playerList[i].id + ")");
node = "<p>" + playerList[i].name + "<br />\n" +
"<small> </small>\n" +
"</p>\n";
div.appendChild(node);
element.appendChild(div);
}
}
window.onLoad = function(){
listAll();
}
This doesn't fill the with anything. I have put this up on JSFiddle as well.
Have I misunderstood how Array.push works? Or something to do with appendChile and createElement?
Thanks in advance for your help.
Two problems - up front, trying to set the id and name on playerList[0] (which doesn't exist yet) won't work.
Second, trying to add a whole "node" full of html, jQuery-style, doesn't work in a plain-JS world. You need to build up the individual elements.
sessionStorage.eventid = 2;
var playerList = [];
playerList.push({
id: 0,
name: "Add New Player"
});
playerList.push({
id: 5,
name: "Asako"
});
playerList.push({
id: 6,
name: "James"
});
playerList.push({
id: 7,
name: "Brian"
});
playerList.push({
id: 8,
name: "Helmut Spargle"
});
function listAll() {
var element = document.getElementById("listView");
var div = document.createElement("div");
var node = "some string";
for (var i = 0; i < playerList.length; i++) {
div = document.createElement("div");
div.setAttribute("onClick", "formOver(" + playerList[i].id + ")");
var p = document.createElement('p');
p.innerHTML = playerList[i].name;
var br = document.createElement('br');
p.appendChild(br);
var small = document.createElement('small');
small.innerHTML = ' ';
p.appendChild(small);
div.appendChild(p);
element.appendChild(div);
}
}
listAll();
Example: http://jsfiddle.net/NF45y/
Initially:
var playerList = [];
playerList[0].id = 0;
You should get an error here about trying to set the id property of undefined. You can do:
var playerList = [{}];
playerList[0].id = 0;
playerList[0].name = "Add New Player";
or add the object the same way the others are. Within the function;
function listAll() {
var element = document.getElementById("listView");
var div;
var node;
for (var i = 0; i < playerList.length; i++) {
div = document.createElement("div");
div.setAttribute("onClick", "javascript: formOver(" + playerList[i].id + ")");
Don't use setAttribute to add listeners that way, it's not how it's intended to be used and doesn't work everywhere, use DOM properties for simplicity:
div.onclick = function(){...};
Since you are setting values inside a loop, you need to break the closure with the variables. An immediately invoked function expression (IIFE) can help:
div.onclick = (function(id) {
return function(){formOver(id);}
}(playerList[i].id));
Don't use XML syntax in an HTML document, and there isn't much point in the '\n' unless you are going to read the markup:
node = "<p>" + playerList[i].name + "<br><small> </small></p>";
div.appendChild(node);
appendChild expects a DOM element, not markup. Since this is the only content of the div, you can use the markup to set its innerHTML property (you might want to change the name of node to say markup):
div.innerHTML = node;
element.appendChild(div);
}
}
I have 3 variables with strings containing comma separated values (I don't know how many) which I want to combine into jQuery objects.
"name1,name2,name3,nameN"
"value1,value2,value3,valueN"
"id1,id2,id3,idN"
to:
var item1 = { name: name1, value: value1, id: id1 };
var item2 = { name: name2, value: value2, id: id2 };
var item3 = { name: name3, value: value3, id: id3 };
var itemN = { name: nameN, value: valueN, id: idN };
To then iterate an operation over each item, for example to append a list:
<h3>items</h3>
<ul>
<li>item1</li>
<ul>
<li>value: <b>value1</b></li>
<li>id: <b>id1</b></li>
</ul>
[...]
<li>itemN</li>
<ul>
<li>value: <b>valueN</b></li>
<li>id: <b>idN</b></li>
</ul>
<ul>
What is the best way to do this?
You can build an array of your items like this:
var names = "name1,name2,name3,nameN";
var values = "value1,value2,value3,valueN";
var ids = "id1,id2,id3,idN";
var namesArray = names.split(",");
var valuesArray = values.split(",");
var idsArray = ids.split(",");
var item, items = [];
for (var i = 0; i < namesArray.length; i++) {
item = {};
item.name = namesArray[i];
item.value = valuesArray[i];
item.id = idsArray[i];
items.push(item);
}
Then, to build the HTML from that, you can do this:
var main = $("<ul>");
var str = "";
for (var i = 0; i < items.length; i++) {
str += "<li>" + items[i].name + "</li><ul><li>value: <b>" + items[i].value + "</b></li>";
str += "<li>id: <b>" + items[i].id + "</b></li></ul>";
}
main.html(str);
$(document.body).append("<h3>items</h3>")
$(document.body).append(main);
You can see it work here: http://jsfiddle.net/jfriend00/yWU3L/4/.
You may want to use the DOM for this.
Using innerHTML means having in-line HTML in your javascript. This breaks Seperations of concerns and leads to maintenance hell.
Live Example
var createListFragment = (function () {
function createItems(names,value,ids) {
var namesArray = names.split(",");
var valuesArray = value.split(",");
var idsArray = ids.split(",");
return namesArray.map(function (name, key) {
return {
name: name,
value: valuesArray[key],
id: idsArray[key]
}
});
}
function createLi(item) {
var itemLi = document.createElement("li");
itemLi.textContent = item.name;
var propertiesUl = document.createElement("ul");
itemLi.appendChild(propertiesUl);
var valueLi = document.createElement("li");
valueLi.appendChild(document.createTextNode("value: "));
var b = document.createElement("b");
b.textContent = item.value;
valueLi.appendChild(b);
propertiesUl.appendChild(valueLi);
var idLi = document.createElement("li");
idLi.appendChild(document.createTextNode("id: "));
var b = document.createElement("b");
b.textContent = item.id;
idLi.appendChild(b);
propertiesUl.appendChild(idLi);
return itemLi;
}
function createListFragment(names, values, ids) {
var items = createItems(names, values, ids);
var fragment = document.createDocumentFragment();
var h3 = document.createElement("h3");
h3.textContent = "items";
fragment.appendChild(h3);
var ul = document.createElement("ul");
fragment.appendChild(ul);
items.forEach(function (item) {
var li = createLi(item);
ul.appendChild(li);
});
return fragment;
}
return createListFragment;
})();
You may need a DOM-shim and ES5-shim for cross browser compliance.
Using answers to this question, I have been able to populate a select box based on the selection of another select box. ( I posted my answer here) Pulling the data from an array structure built server-side, stored in a .js file and referenced in the html page.
Now I would like to add a third select box. If I had 3 sets of data (model, make, options) something like this (pseudo code):
cars : [Honda[Accord[Lx, Dx]], [Civic[2dr, Hatchback]],
[Toyota[Camry[Blk, Red]], [Prius[2dr,4dr]]
Ex: If Honda were selected, the next select box would have [Accord Civic] and if Accord were selected the next select box would have [Lx Dx]
How can I
1) create an array structure to hold the data? such that
2) I can use the value from one select box to reference the needed values for the next select box
Thanks
EDIT
I can create the following, but can't figure out the references in a way that would help populate a select box
var cars = [
{"makes" : "Honda",
"models" : [
{'Accord' : ["2dr","4dr"]} ,
{'CRV' : ["2dr","Hatchback"]} ,
{'Pilot': ["base","superDuper"] } ]
},
{"makes" :"Toyota",
"models" : [
{'Prius' : ["green","reallyGreen"]} ,
{'Camry' : ["sporty","square"]} ,
{'Corolla' : ["cheap","superFly"] } ]
} ] ;
alert(cars[0].models[0].Accord[0]); ---> 2dr
I prefer data structure like this:
var carMakers = [
{ name: 'Honda', models: [
{ name: 'Accord', features: ['2dr', '4dr'] },
{ name: 'CRV', features: ['2dr', 'Hatchback'] },
{ name: 'Pilot', features: ['base', 'superDuper'] }
]},
{ name: 'Toyota', models: [
{ name: 'Prius', features: ['green', 'superGreen'] },
{ name: 'Camry', features: ['sporty', 'square'] },
{ name: 'Corolla', features: ['cheap', 'superFly'] }
]}
];
Given the three select lists with id's: 'maker', 'model' and 'features' you can manipulate them with this (I believe this is pretty self explanatory):
// returns array of elements whose 'prop' property is 'value'
function filterByProperty(arr, prop, value) {
return $.grep(arr, function (item) { return item[prop] == value });
}
// populates select list from array of items given as objects: { name: 'text', value: 'value' }
function populateSelect(el, items) {
el.options.length = 0;
if (items.length > 0)
el.options[0] = new Option('please select', '');
$.each(items, function () {
el.options[el.options.length] = new Option(this.name, this.value);
});
}
// initialization
$(document).ready(function () {
// populating 1st select list
populateSelect($('#maker').get(0), $.map(carMakers, function(maker) { return { name: maker.name, value: maker.name} }));
// populating 2nd select list
$('#maker').bind('change', function() {
var makerName = this.value,
carMaker = filterByProperty(carMakers, 'name', makerName),
models = [];
if (carMaker.length > 0)
models = $.map(carMaker[0].models, function(model) { return { name: model.name, value: makerName + '.' + model.name} });
populateSelect($('#model').get(0), models);
$('#model').trigger('change');
});
// populating 3rd select list
$('#model').bind('change', function () {
var nameAndModel = this.value.split('.'),
features = [];
if (2 == nameAndModel.length) {
var makerName = nameAndModel[0],
carModel = nameAndModel[1],
carMaker = filterByProperty(carMakers, 'name', makerName);
if (carMaker.length > 0) {
var model = filterByProperty(carMaker[0].models, 'name', carModel)
if (model.length > 0)
features = $.map(model[0].features, function(feature) { return { name: feature, value: makerName + '.' + carModel + '.' + feature} })
}
}
populateSelect($('#feature').get(0), features);
})
// alerting value on 3rd select list change
$('#feature').bind('change', function () {
if (this.value.length > 0)
alert(this.value);
})
});
Thanks to the answer from #Marko Dunic, I was able to build an array (data) structure that can be referenced to populate 3 select boxes. I didn't use the implementation code only because I didn't completely understand it...it works as posted. I will come back to this code later as I learn jQuery.
My code is posted below (obviously, your reference to jQuery may be different)
<html><head>
<script language="Javascript" src="javascript/jquery-1.2.6.min.js"></script>
<script type="text/JavaScript">
var cars = [
{ name: 'Honda', models: [
{ name: 'Accord', features: ['2dr', '4dr'] },
{ name: 'CRV', features: ['2dr', 'Hatchback'] },
{ name: 'Pilot', features: ['base', 'superDuper'] }
]},
{ name: 'Toyota', models: [
{ name: 'Prius', features: ['green', 'superGreen'] },
{ name: 'Camry', features: ['sporty', 'square'] },
{ name: 'Corolla', features: ['cheap', 'superFly'] }
]
}
];
$(function() {
var options = '' ;
for (var i = 0; i < cars.length; i++) {
var opt = cars[i].name ;
if (i == 0){ options += '<option selected value="' + opt + '">' + opt + '</option>'; }
else {options += '<option value="' + opt + '">' + opt + '</option>'; }
}
$("#maker").html(options); // populate select box with array
var options = '' ;
for (var i=0; i < cars[0].models.length; i++) {
var opt = cars[0].models[0].name ;
if (i==0){options += '<option selected value="' + opt + '">' + opt + '</option>';}
else {options += '<option value="' + opt + '">' + opt + '</option>';}
}
$("#model").html(options); // populate select box with array
var options = '' ;
for (var i=0; i < cars[0].models[0].features.length; i++) {
var opt = cars[0].models[0].features[i] ;
if (i==0){options += '<option selected value="' + opt + '">' + opt + '</option>';}
else {options += '<option value="' + opt + '">' + opt + '</option>';}
}
$("#feature").html(options); // populate select box with array
$("#maker").bind("click",
function() {
$("#model").children().remove() ; // clear select box
for(var i=0; i<cars.length; i++) {
if (cars[i].name == this.value) {
var options = '' ;
for (var j=0; j < cars[i].models.length; j++) {
var opt= cars[i].models[j].name ;
if (j==0) {options += '<option selected value="' + opt + '">' + opt + '</option>';}
else {options += '<option value="' + opt + '">' + opt + '</option>';}
}
break;
}
}
$("#model").html(options); // populate select box with array
$("#feature").children().remove() ; // clear select box
for(var i=0; i<cars.length; i++) {
for(var j=0; j<cars[i].models.length; j++) {
if(cars[i].models[j].name == $("#model").val()) {
var options = '' ;
for (var k=0; k < cars[i].models[j].features.length; k++) {
var opt = cars[i].models[j].features[k] ;
if (k==0){options += '<option selected value="' + opt + '">' + opt + '</option>';}
else {options += '<option value="' + opt + '">' + opt + '</option>';}
}
break;
}
}
}
$("#feature").html(options); // populate select box with array
});
$("#model").bind("click",
function() {
$("#feature").children().remove() ; // clear select box
for(var i=0; i<cars.length; i++) {
for(var j=0; j<cars[i].models.length; j++) {
if(cars[i].models[j].name == this.value) {
var options = '' ;
for (var k=0; k < cars[i].models[j].features.length; k++) {
var opt = cars[i].models[j].features[k] ;
if (k==0){options += '<option selected value="' + opt + '">' + opt + '</option>';}
else {options += '<option value="' + opt + '">' + opt + '</option>';}
}
break ;
}
}
}
$("#feature").html(options); // populate select box with array
});
});
</script>
</head> <body>
<div id="selection">
<select id="maker"size="10" style="{width=75px}"></select>
<select id="model" size="10" style="{width=75px}"></select>
<select id="feature" size="10"style="{width=75px}"></select>
</div></body></html>
I really liked the solution by #Marko Dunic, but it didn't meet my needs for attaching IDs to the options. Once I attached the IDs, I realized that I could make the JS code even smaller and simpler. My solution is designed for when the data comes from a relational database, and the JSON input data retains the relational structure with Primary/Foreign Keys. Here is the JSON data:
<html lang="en">
<head>
<title>Populate a select dropdown list with jQuery - WebDev Ingredients</title>
<script type="text/javascript" src="js/jquery-1.4.2.js"></script>
<script type="text/javascript">
var types = [
{ typeID: 1, name: 'Domestic'},
{ typeID: 2, name: 'Import'},
{ typeID: 3, name: 'Boat'}
]
var makes = [
{ typeID: 1, makeID: 1, name: 'Chevy'},
{ typeID: 1, makeID: 2, name: 'Ford'},
{ typeID: 1, makeID: 3, name: 'Delorean'},
{ typeID: 2, makeID: 4, name: 'Honda'},
{ typeID: 2, makeID: 5, name: 'Toyota'},
{ typeID: 2, makeID: 6, name: 'Saab'}
]
var model = [
{ makeID: 1, modelID: 1, name: 'Camaro'},
{ makeID: 1, modelID: 2, name: 'Chevelle'},
{ makeID: 1, modelID: 3, name: 'Nova'},
{ makeID: 2, modelID: 4, name: 'Focus'},
{ makeID: 2, modelID: 5, name: 'Galaxie'},
{ makeID: 2, modelID: 6, name: 'Mustang'},
{ makeID: 4, modelID: 7, name: 'Accord'},
{ makeID: 4, modelID: 8, name: 'Civic'},
{ makeID: 4, modelID: 9, name: 'Odyssey'},
{ makeID: 5, modelID: 10, name: 'Camry'},
{ makeID: 5, modelID: 11, name: 'Corolla'}
]
//
// Put this in a stand alone .js file
//
// returns array of elements whose 'prop' property is 'value'
function filterByProperty(arr, prop, value) {
return $.grep(arr, function (item) { return item[prop] == value });
}
// populates select list from array of items given as objects: { name: 'text', value: 'value' }
function populateSelect(el, items) {
el.options.length = 0;
if (items.length > 0)
el.options[0] = new Option('please select', '');
$.each(items, function () {
el.options[el.options.length] = new Option(this.name, this.value);
});
}
// initialization
$(document).ready(function () {
// populating 1st select list
populateSelect($('#sType').get(0), $.map(types, function(type) { return { name: type.name, value: type.typeID} }));
// populating 2nd select list
$('#sType').bind('change', function() {
var theModels = filterByProperty(makes, 'typeID', this.value);
populateSelect($('#sMake').get(0), $.map(theModels, function(make) { return { name: make.name, value: make.makeID} }));
$('#sMake').trigger('change');
});
// populating 3nd select list
$('#sMake').bind('change', function() {
var theSeries = filterByProperty(model, 'makeID', this.value);
populateSelect($('#sModel').get(0), $.map(theSeries, function(model) { return { name: model.name, value: model.modelID} }));
});
});
</script>
</head>
<body>
Enter values, click submit, and look at the post parameters
<form method="get" action="index.php">
<div id="selection">
<select id="sType" name="type_id" style="{width=75px}"></select>
<select id="sMake" name="make_id" style="{width=75px}"></select>
<select id="sModel" name="model_id" style="{width=75px}"></select>
</div>
<input type="submit">
</form>
</body>
</html>
Notice that my solution shifts the functionality so that Make-Model are the 2nd and 3rd text boxes, and Type ( domestic, import, boat, etc. ) are the 1st level. I got the boilerplate JS down to 23 lines (less comments) while retaining good formatting.
The JSON data is very easy to render from SQL queries, which are cached in java Lists on init because the Type-Make-Model rarely change. I don't use any dynamic AJAX because that complicates the architecture, and I have a relatively small list of available values, so I just send it at the page request.
"Solutions should be a simple as possible, but no simpler" - A. Einstein
You should take a look here for select box manipulation.
For what you want, i think JSON will do the right job for you.
Anyhow, if i were you, i will do this way:
When I change first select, i do an ajax request. With ajax response, i will populate the second box. Same for second box and there you have the third box populated with right data.