I am attempting to take a generated table and create an object out of it using jquery. I have looked up examples but am getting some odd behavior when I try to implement. Given this simplified version of my table (generated via Spring MVC):
<table id="notices">
<thead>
<tr>
<td class="columnheader">Order</td>
<td class="columnheader" style="display: none;">ID</td>
<td class="columnheader">Title</td>
</tr>
</thead>
<tbody>
<tr>
<td class="formlabel"><input class="fields" size="2" type="text" value="3"></td>
<td class="formlabel" style="display: none;">JP-L2913666442781178567X</td>
<td class="formlabel">*Notice1</td>
</tr>
<tr>
<td class="formlabel"><input class="fields" size="2" type="text" value="2"></td>
<td class="formlabel" style="display: none;">JP-L2913666442760937100X</td>
<td class="formlabel">Quiz Notice - Formative</td>
</tr>
</tbody>
</table>
And snippet of my current script:
var noticeMap = $('#notices tbody tr').map(function() {
var $row = $(this);
return {
sequence: $row.find(':nth-child(1)').text(),
noticeUID: $row.find(':nth-child(2)').text()
};
});
When I de[fire]bug, noticeMap looks like this:
Object { sequence="*Notice1", noticeUID="JP-L2913666442781178567X"},
Object { sequence="Quiz Notice - Formative", noticeUID="JP-L2913666442760937100X"}
Somehow :nth-child(1) is retrieving the title, the third td. I believe it has to do with retrieving the value of the input, but am not sure where to go from here. Maybe because the input field is within the td child I am specifying, it is not considered a direct descendant, so the proper text is not retrieved? Just seems odd to me that it would then skip to the 3rd td. Alas, I am still learning with jquery, and humbly request any ideas and guidance.
Thanks!
You're right about the input being the issue, you have to get the value of the input inside then td, which is not defined as a text node, but as its own element, therefore you have to specify the child element within the jQuery selector. Also .text() won't work for input elements, you can read its value with .val().
This will work for you to get the right value into your object:
$row.find(':nth-child(1) input').val();
Or using .eq()
var noticeMap = $('#notices tbody tr').map(function() {
var $cells = $(this).children();
return {
sequence: $cells.eq(0).children('input').val(),
noticeUID: $cells.eq(1).text()
};
});
Or into a single object with key/value pairs:
var noticeMap = {};
$('#notices tbody tr').each(function() {
var $cells = $(this).children();
noticeMap[$cells.eq(0).children('input').val()] = $cells.eq(1).text();
});
I'm not too sure tho why your original attempt returns the text inside the 3rd td. That is really odd. I'll have a tinker with it.
Edit
It seems to me that .find() is somehow being smart about what it returns, it seems to realise that calling .text() does not return anything on the first match it finds (the first td), it therefore travels down the DOM to find the next element which does have a :first-child, which matches the a tag inside the 3rd td, then it returns the text of that a tag. When I removed the a around the title, .find() started returning "" again, I think that is because it couldn't find another match after the first one didn't return anything useful.
Using .children() would be safer in this case, as it only finds direct descendants and doesn't travel down the DOM.
For better performance, use .eq() on the matched set:
var noticeMap = $('#notices tbody tr').map(function() {
var $cells = $(this).children();
return {
sequence: $cells.eq(0).find('input').val(),
noticeUID: $cells.eq(1).text()
};
});
Related
Look at my code, I want to make it like that when a user click the table tr, I will get the first td text, so jquery I use first() and then text(), but it echo out both td 1 and td apple. I only want to get the first td text, what did I do wrong? Appreciate.
var gridtable = $('.gridtable');
gridtable.click(function(){
var name = $(this).first().text();
alert('name');
})
<tr class="gridtable">
<td>1</td>
<td >apple</td>
</tr>
<tr class="gridtable">
<td>2</td>
<td >banana</td>
</tr>
Inside handler this refers to the clicked tr applying first() method will retrieve the same tr itself . Instead, you need to filter out the nested td element. You can use the :first pseudo-class selector to get the first element.
var gridtable = $('.gridtable:first');
gridtable.click(function(){
// get the first element within the context
var name = $('td:first', this).text();
alert(name);
});
var gridtable = $('.gridtable');
gridtable.click(function() {
var name = $('td:first',this).text();
alert(name);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr class="gridtable">
<td>1</td>
<td>apple</td>
</tr>
<tr class="gridtable">
<td>2</td>
<td>banana</td>
</tr>
</table>
Since you are attaching your click handler at the tr level, you'll need to use the find function to select all tds within the tr and then select the first td:
var name = $(this).find('td').first().text();
var gridtable = $('.gridtable');
gridtable.click(function(){
var name = $(this).find('td:first').text();
alert(name);
});
var gridtable is referring to your tr elements, so when $element.click is raised, gridtable.first() is locating the first element with class .gridtable
You could use .gridtable.children() to get the children of gridtable and use the .first() after that
Please have a look to this example: https://jsfiddle.net/gnc9t1p2/1/
Edit 1 -
As #JaromandaX said, you should use var name = $(this)... to get the clicked row.
I think below code will help out you, it's working for me.
var gridtable = $('.gridtable');
gridtable.click(function(){
var name = $(this).find("td:first").text();// for only first td
or
var name = $(this).find("td:eq(0)").text();// you can change index number like eq(0) ,eq(1) for nect td
alert(name);
})
There is a table with some input and select fields in a row. I want to check if all input and select fields of an row have a value. This is how I would think to do that, but do I have to use closest and find? I think this is not optimal.
HTML
<table>
<tr>
<td><select><option></option><option>Select anything</option></td>
<td><input type="text" name="field1"></td>
<td><input type="text" name="field2"></td>
</tr>
<tr>
<td><select><option></option><option>Select something</option></td>
<td><input type="text" name="field3"></td>
<td><input type="text" name="field4"></td>
</tr>
</table>
JS
'change #table input, change #table select': function(event) {
var $this = $(event.currentTarget),
$row = $this.closest('tr'),
$elements = $row.find('input, select');
var empty = false;
$elements.each(function(index) {
if (!$(this).val()) empty = true;
});
if (empty)
console.log('some value is missing')
else {
console.log('valide');
// do something with values
}
}
There are really two questions here:
Most optimal method to select all inputs in a table row
Ensure all the inputs have a value
For the first question there is a subliminal side to that. Ensure that it IS an input and then select it within the context of the current row of the changed input.
First off, jQuery uses the Sizzle (https://sizzlejs.com/) engine under the covers for selection. One thing to be aware of is the "right to left" processing of the selector string by that engine.
Thus the most optimal selection is somewhat browser specific but the fastest way to select is an ID followed in modern browsers by a class. Some older browsers do not select by class as well but let's leave that for your research.
Selection: Bad way to do stuff
So given that, let's look at a complex selector that you might use:
'div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td select, div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td input'
First off DO NOT USE THAT. Now to explore why not: Remember we talked about the "right to left" selector processing? For discussion let us narrow down out selector to the last part:
"div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td input"
What this does then in starting on the right:
"find all the inputs in the DOM",
use that list of those inputs, "find all the inputs in a td element
use those td elements, find all those in a tr
find all those tr in a .mytableclass element
find all those in an element with an id of mytable (remember this ID MUST be unique)
Now keep going, find that single element id that is a table element
That is an immediate child of an element with classmytablecontainer
That is a DIV element div
That is a child of an element with class mycontainer
That is a DIV element div
Whew that's a lot of work there. BUT we are NOT DONE! We have to do the same thing for the OTHER selector in there.
Selection: Better way to do stuff
NOW let's do this better; first off let's leverage the modern browser class selector by adding a class to all our "scoped" inputs - things we want to check for entry.
<input class="myinput" />
It does really need a type="" attribute but ignore that for now. Let's use that.
$('#mytable').find('.myinput');
What this does is:
Select the element with ID of 'mytable' which is the FASTEST selector in all browsers; we have already eliminated those 47 other tables in our DOM.
Find all the elements with a class of class="myinput"; within that table; in modern browsers this is also very fast
DONE. WOW! that was SO much less work.
Side note on the .find() instead of "#mytable input"
Remember our right to left once again? Find all inputs in the DOM, then narrow to those inputs we found that are in that table NO STOP THAT right now.
Or (better likely) "#mytable .myinput"
SO our "rules" of selecting a group of elements are:
Use an ID to limit scope to some container if at all possible
Use the ID by itself NOT part of a more complex selector
FIND elements within that limited scope (by class if we can)
Use classes as modern browsers have great selection optimization on that.
When you start to put a space " " or ">" in a selector be smart, would a .find() or .children() be better? In a small DOM perhaps maintenance might be easier, but also which is easier to understand in 4 years?
Second question: not specific but still there
You cannot simply globally use !$(this).val() for inputs.
For a check box that is invalid. What about radio buttons? What about that <input type="button" > someone adds to the row later? UGH.
SO simply add a class to all "inputs" you DO wish to validate and select by those:
<input type="text" class="validateMe" />
<select class="validateMe" >...
Side note you MIGHT want to sniff the TYPE of the input and validate based upon that: How to get input type using jquery?
EDIT: Keep in mind your validation input MIGHT have a "true/false" value so then this might fail: !$(this).val() (radio buttons, checkbox come to mind here)
Some code and markup:
<table id="mytable">
<tr>
<td>
<select class="myinput">
<option></option>
<option>Select anything</option>
</select>
</td>
<td>
<input class="myinput" type="text" name="field1" />
</td>
<td>
<input class="myinput" type="text" name="field2" />
</td>
</tr>
<tr>
<td>
<select class="myinput">
<option></option>
<option>Select something</option>
</select>
</td>
<td>
<input class="myinput" type="text" name="field3" />
</td>
<td>
<input class="myinput" type="text" name="field4" />
</td>
</tr>
</table>
<div id="results">
</div>
probably NOT want a global (namespace the "selectors")
var selectors = '.myinput';
$('#mytable').on('change', selectors, function(event) {
var $this = $(event.currentTarget),
$row = $this.closest('tr'),
$elements = $row.find(selectors);
var $filledElements = $elements.filter(function(index) {
return $(this).val() || this.checked;
});
var hasEmpty = $filledElements.length !== $elements.length
var rowIndex = $row.index();
$('#results').append("Row:" + rowIndex + " has " + $filledElements.length + ' of ' + $elements.length + ' and shows ' + hasEmpty + '<br />');
if (hasEmpty)
console.log('some value is missing');
else {
console.log('valide');
// do something with values
}
});
AND something to play with: https://jsfiddle.net/MarkSchultheiss/fqadx7c0/
If you're only selecting on particular element with knowing which parent to select with, you should try using .filter() to filter out only element that did't have a value like following :
$('button').click(function() {
var h = $('table :input').filter(function() {
return $(this).val() == "" && $(this);
}).length;
alert(h);
});
DEMO
I did this plunk
https://plnkr.co/edit/q3iXSbvVWEQdLSR57nEi
$(document).ready(function() {
$('button').click(function() {
var table = $('table');
var rows = table.find('tr');
var error = 0;
for (i = 0; i < rows.length; i++) {
var cell = rows.eq(i).find('td');
for (a = 0; a < cell.length; a++) {
var input = cell.eq(a).find(':input');
if (input.val() === "") {
input.css("border", "solid 1px red");
error++;
} else {
input.css("border", "solid 1px rgb(169, 169, 169)");
}
}
}
if (error > 0){
alert('Errors in the form!')
return false;
} else {
alert('Form Ok!')
return true;
}
})
})
Simple Jquery validation, searching all the inputs (including selects), if it's null, increment the error counter and change class. If the error counter is > 0, alert error and return false;
Maybe isn't the best solution, but it sure can help get started.
I'm trying to implement a quick search/filter function to my table using jquery. In essence I want to hide all the rows that don't have the string I'm looking for from a searchbox to be hidden.
I have a dynamically created table and a text field used as the filter for the list.
Table:
<table id="report-table" class="table">
<thead>
<tr>
<th class="">client</th>
<th class="">coach</th>
<th class="">groups</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name">John</td>
<td class="coach">Peter </td>
<td class="groups"> Skiers </td>
</tr>
</tbody>
Ihave a function tied to the change event of the search text box. In this function I essentially want to choose all tr that do not contain the text string in name or coach column and add a class to them. I have tried many things but have not gotten the syntax right, how should it be written?
hideSearch: function(e){
console.log("hideSearch called");
var searchValue = this.$el.find('.search-text').val();
if(!searchValue ){
console.log("hideSearch: empty search param");
this.$el.find('tr').removeClass('hidden');
}
else{
console.log("hideSearch: searched for: " + searchValue);
//$('(#name, #groups):contains:not("'+searchValue+'")').parent().addClass('hidden');
var selection =$('#name, #groups').('*:contains("'+searchValue+'")');
console.log(selection);
//console.log($('#name, #groups').('*:contains("'+searchValue+'")'));
//$('(#name, #groups):contains("'+searchValue+'")').parent().addClass('hidden');
//$('#name, #groups').('*:contains:not("'+searchValue+'")').parent().addClass('hidden');
}
$('#name, #groups').('*:contains("'+searchValue+'")'); would basically try to access the property *:contains("foo") (assuming searchValue is "foo") of the object returned by $('#name, #groups'). I believe I don't have to say that jQuery objects don't have properties with such strange names.
First of all you have to give all the cells a common class instead of an ID. Then you should select all rows and see if either .name or .coach contain the search value. Use .filter to get those for which neither cell matches:
$('#report-table > tbody > tr').filter(function() {
return $(this).children('.name').text().indexOf(searchValue) === -1 &&
$(this).children('.coach').text().indexOf(searchValue) === -1;
}).addClass('hidden');
The filter callback returns true if neither the .name cell nor the .coach cell contain the search value. Those rows for which the callback returns true are kept in the selection and are getting the class hidden added to them.
I am trying to create a prepopulated form that pulls values from any selected row in a HTML table . The HTML page is populated by a JSP .
my table looks like this
<table id="data-table" id="test">
<thead>
<tr>
<th>value1</th>
<th>value2</th>
<th>value3</th>
<th>value4</th>
</tr>
</thead>
<tbody>
<tr>
<td id="class1"><%= value.valueOne() %></td>
<td id="class2"><%= value.valueTwo() %></td>
<td id="class3"><%= value.valueThree() %></td>
<td id="class4"><%= value.valueFour() %></td>
</tr>
<%
}
%>
</tbody>
</table>
I want to obtain a prepopulated form with the row values on click of a particular row . I have some js code that does this .
$(document).on("click", ".data-table .class1", function(e) {
var value = $(this).closest('tr').val();
console.log(value);
// Just to check if I get the correct value
});
unfortunately I cannot understand how to get the values for that particular row from the DOM and populate it in a form , That I want to overlay over the table . I would appreciate any pointers . I really would have written more code but I dnt know Jquery and am stuck
Your general strategy should be this:
Populate the table on the server side: done
Have the form pre-existing in the page, but hidden with css (display:none)
Register a click listener on all tr elements to:
find the values inside each td within the tr
select the corresponding form inputs
populate the inputs using jQuery's val(value) function.
unhide the form if it's hidden
With this in mind, I would change your click listener from document to something like this. (Note: I'm assuming value.valueOne() are just numbers or strings, and don't contain any html.
//target just TR elements
$('tr').click(function(){
values = [];
$(this).children().each(function(){
//add contents to the value array.
values.push($(this).html())
});
//fill in the form values
populateForm(values);
});
Populate form would completely depend on your form's HTML, but to get you started here's an idea of what it might look like:
function populateForm(values){
//set the value of the input with id of name to the value in the first td.
$('#name').val(values[0]);
//show the form (id inputForm) now that it's populated
$('#inputForm').show();
}
A couple things are wrong with your html markup and your JQuery selector. You'll never be able to execute the code you've provided...
You have two 'id' parameters in this element, <table id="data-table" id="test">... This will work with the JQuery I've fixed below, but it's malformed html either way.
In your selector, you are using the syntax for finding an element based on it's css class attribute, however your elements in your HTML have those values set as 'id' attributes. Thus, this, $(document).on("click", ".data-table .class1", function(e) {... should be written as follows, $(document).on("click", "#data-table #class1", function(e) {
Now, if you are attempting to get the values within all of the 'td' elements within a row, then all you really need to do is get the parent element of the 'td' that was clicked, and then get it's children. Then, for each child, get their values.
Like this...
$(document).on("click", "#data-table #class1", function(e) {
var elements = $(this).parent().children();
$.each(elements, function(index, el){
alert($(el).html());
});
});
I've saved a JSFiddle for you to see this in action... http://jsfiddle.net/2LjQM/
val() is used to return value of form inputs. You are using it to try to get the value of a row and row has no value.
Without seeing what your output into the TD as html, I assume it is a form control
Try
$(document).on("click", ".data-table .class1", function(e) {
var value = $(this).find(':input').val(); // `:input pseudo selector wull access any form control input,select,textarea
console.log(value);
// Just to check if I get the correct value
});
EDIT: if the TD contains text
var value = $(this).text();
Instead of scraping the DOM, you could invert the logic, and build the rows using javascript instead. Check out this jsBin to see the solution in action: http://jsbin.com/aligaZi/2/edit?js,output
Start with an empty table:
<table class="data-table" id="test">
<thead>
<tr>
<th>value1</th>
<th>value2</th>
<th>value3</th>
<th>value4</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Since you need to fill a form with the data, I'll be using a simple one as an example:
<form class="data-form">
<label>Value1<input class="value1" /></label>
<label>Value2<input class="value2" /></label>
<label>Value3<input class="value3" /></label>
<label>Value4<input class="value4" /></label>
</form>
Then, on the javascript side:
$(function() {
// Interpolate the values somehow.
// I'm not familiar with JSP syntax, but it shouldn't be too hard.
// I will use dummy data instead.
var tableData = [
{
value1: "row1-v1",
value2: "row1-v2",
value3: "row1-v3",
value4: "row1-v4"
}, {
value1: "row2-v1",
value2: "row2-v2",
value3: "row2-v3",
value4: "row2-v4"
}
];
// For each object, create an HTML row
var rows = $.map(tableData, function(rowData) {
var row = $("<tr></tr>");
row.append($('<td class="class1"></td>').html(rowData.value1));
row.append($('<td class="class2"></td>').html(rowData.value2));
row.append($('<td class="class3"></td>').html(rowData.value3));
row.append($('<td class="class4"></td>').html(rowData.value4));
// When this row is clicked, the form must be filled with this object's data
row.on("click", function() {
fillForm(rowData);
});
return row;
});
$(".data-table").append(rows);
function fillForm(rowData) {
var form = $(".data-form");
form.find("input.value1").val(rowData.value1);
form.find("input.value2").val(rowData.value2);
form.find("input.value3").val(rowData.value3);
form.find("input.value4").val(rowData.value4);
}
});
So the short version of this is: Can I traverse only the elements within the matched element of the selectors before the each()? Or is there a simpler way of getting what I want without an each() loop?
I thought this would be much easier, which makes me think I'm just missing some fundamental principle of element traversing with jquery.
So here's the scenario:
I have a table (and it is appropriate in this case), where each cell has a text input. The last input is read-only and is supposed to be the total sum of the other values entered on that row. I have a really messy js script for finding both the totals of each row and then the grand total of each row total.
Here's the basic HTML:
<table>
<thead>
<tr><th>Col 1</th><th>Col 2</th><th>Col 3</th><th>Total</th></tr>
</thead>
<tbody>
<tr id="row1"><td><input type="text" /></td><td><input type="text" /></td><td><input type="text" /></td><td class="total"><input type="text" readonly="readonly" /></td></tr>
<tr id="row2"><td><input type="text" /></td><td><input type="text" /></td><td><input type="text" /></td><td class="total"><input type="text" readonly="readonly" /></td></tr>
<tr id="row3"><td><input type="text" /></td><td><input type="text" /></td><td><input type="text" /></td><td class="total"><input type="text" readonly="readonly" /></td></tr>
</tbody>
</table>
The javascript will validate that the data entered is numerical, just to be clear.
So I have a event listener for each input for onchange that updates the total when the user enters data and moves to the next cell/input. Then I have a function called updateTotal that currently uses for loops to loop through each row and within that loop, each cell, and finally sets the input in the total cell to sum.
Quick note: I have included the code below to show that I'm not just looking for a hand out and to demonstrate the basic logic of what I have in mind. Please feel free to skim or skip this part. It works and doesn't need any debugging or critique.
This is what that looks like:
function updateTotal() {
table = document.getElementsByTagName("tbody")[0];
allrows = table.getElementsByTagName("tr");
grandtotal = document.getElementById("grand");
grandtotal.value = "";
for (i = 0; i < allrows.length; i++) {
row_cells = allrows[i].getElementsByTagName("input");
row_total = allrows[i].getElementsByTagName("input")[allrows.length - 2];
row_total.value = "";
for (ii = 0; ii < row_cells.length - 1; ii++) {
row_total.value = Number(row_total.value) + Number(row_cells[i][ii].value);
grandtotal.value = Number(grandtotal.value) + Number(row_cells[i][ii].value);
}
}
}
Now I am trying to re-write the above with jquery syntax, but I'm getting stuck. I thought the best way to go would be to use each() loops along the lines of:
function findTotals() {
$("tbody tr").each(function() {
row_total = 0;
$($(this) + " td:not(.total) input:text").each(function() {
row_total += Number($(this).val());
});
$($(this) + " .total :input:text").val(row_total);
});
}
But using $(this) doesn't seem to work in the way I thought. I read up and saw that the use of $(this) in an each loop points to each matched element, which is what I expected, but I don't get how I can traverse through that element in the each() function. The above also leaves out the grand_total bit, because I was having even less luck with getting the grand total variable to work. I tried the following just to get the row_totals:
$($(this).attr("id") + " td:not(.total) input:text").each(function() {
with some success, but then managed to break it when I tried adding on to it. I wouldn't think I'd need each row to have an id to make this work, since the each part should point to the row I have in mind.
So the short version of this is: Can I use the each loop to traverse only the elements within the matches, and if so, what is the correct syntax? Or is there a simpler way of getting what I want without an each loop?
Oh, one last thought...
Is it possible to get the numerical sum (as opposed to one long string) of all matched elements with jquery? I'll research this more myself, but if anyone knows, it would make some of this much easier.
You are trying to set your context incorrectly try this:
function findTotals() {
$("tbody tr").each(function() {
row_total = 0;
$("td:not(.total) input:text",this).each(function() {
row_total += Number($(this).val());
});
$(".total :input:text",this).val(row_total);
});
}
For more information about the context check out the jquery docs: http://docs.jquery.com/Core/jQuery#expressioncontext
There can be two parameters for a selector. The second parameter is the context htat that the search is to take place in. Try something like the following:
$('#tableID tbody tr).each(function(){
//now this is a table row, so just search for all textboxes in that row, possibly with a css class called sum or by some other attribute
$('input[type=text]',this).each(function(){
//selects all textbosxes in the row. $(this).val() gets value of textbox, etc.
});
//now outside this function you would have the total
//add it to a hidden field or global variable to get row totals, etc.
});