My function below need to update a certain row value (Fruit Name) when the first Select has been changed. How can I update the last <"input"> element? I don't know what is the correct DOM property to use.
I need to use DOM so I don't need to set id on the tags.
HTML
<table>
<tbody>
<tr>
<td>Option:
<select onChange="updateValue(this);">
<option>A</option>
<option>B</option>
</select>
</td>
<td>Quantity:
<select>
<option>1</option>
<option>2</option>
</select>
</td>
<td>Shipping Address:
<input type="text"/>
</td>
<td>Fruit Name:
<input type="text"/>
</td>
</tr>
</tbody>
</table>
Javascript:
function updateValue(obj){
var _input = obj.parentNode.nextSibling.firstChild;
_input.innerText = 'Apple';
}
firstElementChild & lastElementChild is to get html element only not a text(html string).So it is more better to get specific element!
function updateValue(obj){
var _input = obj.parentNode.parentNode.lastElementChild.firstElementChild;
_input.value = 'Apple';
}
Make a minor change in your HTML code.Add a class which will help to recognize which input to update. Use parentNode twice to move up to tr. Then use the className to find the input which you want to update
<td>Fruit Name:
<input type="text" class="fruitClass"/>
</td>
JS Change
function updateValue(obj){
var _input = obj.parentNode.parentNode.getElementsByClassName("fruitClass")[0]
_input.value = 'Apple';
}
Check this jsFiddle
https://jsfiddle.net/kavxatro/6/
function updateValue(obj){
// get the last TD of this TR
var _input = obj.parentNode.parentNode.lastChild;
// make sure we are on the TD. If not, get the previous object.
if (typeof _input.tagName == 'undefined') _input = _input.previousSibling;
// get the input inside the TD
_input = _input.lastChild;
// make sure we are on the input. If not, get the previous object.
if (typeof _input.tagName == 'undefined') _input = _input.previousSibling;
// change the input value
_input.value = 'Apple';
}
The first time you use parentNode you get the parent of the SELECT, that is the TD.
The second parentNode gets the parent of this TD, that is the TR.
You can use lastChild to get the last element inside this TR, that is the last TD.
And since you only have one element inside that TD, then either firstChild or lastChild should get you the input object.
To change the value of an input field you use value, not innerText.
Also there is no HTML form type textbox. If you want a one-line input form then text is the type you should use.
<td>Fruit Name:
<input type="text"/>
</td>
Related
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 have a bit of HTML here:
<tr taskId="(#=obj.task.id#)" assigId="(#=obj.assig.id#)" class="assigEditRow" >
<td><select name="resourceId" class="get-resources formElements"></select></td>
<td><span class="resources-units"></span></td>
<td><span class="resources-quantity"></span></td>
<td><input type="text" placeholder="Required Q"></td>
<td align="center"><span class="teamworkIcon delAssig" style="cursor: pointer">d</span></td>
</tr>
And a bit of JS here:
'use strict';
function addResourceFunction(){
let ResourcesJSON = (json) => {
let Resources = json;
console.log(Resources);
let contactsLength = json.length;
let arrayCounter = -1;
let resID;
let resName;
let resUnit;
let resQuantity;
let Option = $('<option />');
let assignedID = $('tr.assigEditRow:last').attr("assigId");
while(arrayCounter <= contactsLength) {
arrayCounter++;
resID = Resources[arrayCounter].ID;
resName = Resources[arrayCounter].name;
resUnit = Resources[arrayCounter].unit;
resQuantity = Resources[arrayCounter].quantity;
$('.assigEditRow').last().find('select').append($('<option>', {
value: resName.toString(),
text: resName.toString(),
resourceID: resID.toString(),
resourceUnit: resUnit.toString(),
resourceQuantity: resQuantity.toString()
}));
}
}
$.getJSON("MY JSON URL IS HERE", function(json) {
ResourcesJSON(json);
});
};
So what's actually going on here: I get my data from the URL (JSON array), trigger the addResourceFunction() on click to create a new table row and to add a new select with options passed from the array. As you see from my HTML markup, the select input is placed in td.get-resources, and all that works good. I get my date set, I populate the select field and all works good. I can add as many rows/select dropdowns as I want.
Also, every option has a few custom attributes (you can see it in my JS code above), and I want to add the values of those attributes to the second and third column of the row (in HTML those are span.resources-units and span.resources-quantity). The thing is, I have no clue how to make it work 1:1, meaning that one select dropdown "alters" only units and quantity of its own row. Below is the code for that:
let idCounter = 1;
$(document).on('change', '.get-resources', function() {
$('.assigEditRow').last().find('.resources-units').attr('id', 'units-' + idCounter);
$('.assigEditRow').last().find('.resources-quantity').attr('id', 'quantity-' + idCounter);
this.resourceUn = $( ".get-resources option:selected" ).attr( "resourceUnit" );
this.resourceQuant = $( ".get-resources option:selected" ).attr( "resourceQuantity" );
$('#units-' + idCounter).append(this.resourceUn);
$('#quantity-' + idCounter).append(this.resourceQuant);
idCounter++;
});
What happens is that if I add one select input, and change options, the thing works. When I add another one and change its options, it gets attributes of the first one. Adding more - same thing. Whatever I change, it takes the attribute value of the first item added.
Try getting the id from the element instead of from the variable, since you always update the element with the id of the counter, instead of the element with the id of the row that was clicked.
Hmm, what does the counter do exactly? The more I look at it, the less I understand. What I do know is that you're not selecting the correct elements by using the idCounter to reference the correct row.
You want to do something like
$(document).on('change', '.get-resources', function() {
//var row = this;
this.find(/* Some path to the second column */).att(/* some att to change */);
this.find(/* Some path to the third column */).att(/* some att to change */);
});
where you always use the row as the root again, instead of finding a certain id, so you only update that row.
Native:
<table>
<tr>
<td>
<select>
<option data-text="resName1" data-resourceID="resID1" data-resourceUnit="resUnit1" data-resourceQuantity="resQuantity1">1</option>
<option data-text="resName2" data-resourceID="resID2" data-resourceUnit="resUnit2" data-resourceQuantity="resQuantity2">2</option>
<option data-text="resName3" data-resourceID="resID3" data-resourceUnit="resUnit3" data-resourceQuantity="resQuantity3">3</option>
</select>
</td>
<td>
<div class="column2"></div>
</td>
<td>
<div class="column3"></div>
</td>
</tr>
</table>
<script>
document.addEventListener('change', function ( event ) {
var select = event.target,
option = select.options[select.selectedIndex],
values = {
'text' : option.getAttribute('data-text'),
'resourceID' : option.getAttribute('data-resourceID'),
'resourceUnit' : option.getAttribute('data-resourceUnit'),
'resourceQuantity' : option.getAttribute('data-resourceQuantity')
},
row = select.parentNode.parentNode,/* depending on how deep the select is nested into the tr element */
column2 = row.querySelector('.column2'),
column3 = row.querySelector('.column3');
column2.textContent = 'some string with the values you want';
column3.textContent = 'some string with the other values you want';
});
</script>
Basically you start with the select that was changed, from there you get the option node that was clicked. Then you get the attributes you need from that option. Then you go up a few nodes to the row parent and find the two columns inside that row. Then you can set the content of these two columns.
I want the value of last textbox to be grabbed by the varialble on multiple textbox with same ID.
HTML
<input type="text" id="get"><br>
<input type="text" id="get"><br>
<button id="grab">Click</button><br>
SCRIPT
$("#grab").click(function(){
var value = $("#get").val();
});
Or, a way to delete the first textbox might also work. Working Example
Your HTML is invalid: HTML elements can't have the same id attribute.
Use the class attribute, instead.
You can then use .last() to get the last element that matches the .get selector:
$("#grab").click(function(){
var value = $(".get").last().val();
alert(value);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" class="get" value="foo"><br>
<input type="text" class="get" value="bar"><br>
<button id="grab">Click</button><br>
(I added the value attributes for demonstrative purposes. Obviously, they can be removed.)
If you want to get the first element's value if the second one is empty, you could do this:
$("#grab").click(function(){
var firstValue = $(".get").val(); // `.val()` gets the first element's value by default
var secondValue = $(".get").last().val();
var result = secondValue || firstValue;
alert(result);
});
If you don't have any control on ids you should use following solution. If you can change the ids you should change them.
You approach will not work because the id is not unique. It will always get the first input.
$("#grab").click(function() {
// var value = $(this).prev("input").val(); // Will work when there is no `<br>`
alert($('input[id="get"]').last().val());
});
Here $('input[id="get"]') will get all the elements having id get and last() will get the last element from it.
Demo: https://jsfiddle.net/orghoLzg/1/
I have a dynamically generated tables the foot of the table contain some text fields when click on save i want to add the value of text fields to the body of that table .
here is the table
<table border="1" class="entity_table">
<tfoot>
<tr>
<td>
<div class="pane1"></div>
<div class="pane2">
<input type="text" id="name"><br>
<select id="data">
<option value="1">int</option>
<option value="2">tinyint</option>
</select>
<br><span id="save">save</span>
</div>
</td>
</tr>
</tfoot>
<tbody class="table-body" id='myid'></tbody>
</table>
i did this but this is id specific ..i want to update that specific table on which it is clicked and edited .
var myName = document.getElementById("name");
var data = document.getElementById("data");
var Mtable = document.getElementById("myid");
var rowCount = Mtable.rows.length;
var mrow = Mtable.insertRow(rowCount);
var mcell = mrow.insertCell(0);
mcell.innerHTML = myName.value;
var mcell1 = mrow.insertCell(1);
mcell1.innerHTML = size.value;
i want to update each dynamically generated table with values that is entered in its table's foot section
You can use below jQuery :
$(function(){
$('#save').click(function(){
$(this).closest('table').find('tbody').append('<tr><td>'+$('#name').val()+' and '+$('#data').val()+'</td></tr>');
});
});
Demo
EDIT - to eliminate input and select box id dependency use below code :
$(function(){
$('#save').click(function(){
var name = $(this).closest('tr').find('input[type=text]').val();
var data = $(this).closest('tr').find('select').val();
$(this).closest('table').find('tbody').append('<tr><td>'+name+' and '+data+'</td></tr>');
});
});
Demo
So if I understood this right, you dont want to use element's ID to select it.
You have some else options if you dont want to work with elements IDs:
1) You can add them some data- attribute, for example: data-id. And based on this you select your element like this:
myElement.querySelector("[data-id='X']") where myElement is some parent element of your tables and X is their ID which you generated before (lets say it will start from 0 and will increment with every next table).
2) If possible, work with objects. When you create your tables, you either create them with raw text with defining html elements or you create new elements with calling createElement("table") on document keyword. If second option is your option, you can save this elements to some array (myTables in this case) and then approach this elements in a standard way - lets say:
myTables[0].getElementsByTagName("input")
Hope it helps your issue. Hope I understood issue you were asking about.
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()
};
});