Getting the text of a dynamic element in JS - javascript

I am generating input fields dynamically, hoping to parse any text entered in a field and process in the keydownFunc later. No text avail in the DOM after the key event occurred.
Here is my code:
for (i = 1; i < 7; i++) {
var element = document.createElement("input");
element.setAttribute("style", "width:33px;");
element.setAttribute("name", i);
element.setAttribute("onkeydown", "keydownFunc(event);");
var tempID = document.getElementById(i)
tempID.appendChild(element);
}
function keydownFunc(event) {
var x = event.keyCode;
if (x == 13 | x == 9) {
path = event.path[1].id
console.log(path);
// process input
}
}
<table id="table">
<tbody>
<td id="1"></td>
<td id="2"></td>
<td id="3"></td>
<td id="4"></td>
<td id="5"></td>
<td id="6"></td>
</tbody>
</table>

You should avoid assigning event handlers using on... attributes. You should use addEventListener instead:
element.addEventListener("keydown", keydownFunc);
Also path is a quite obscure and not a standard property of the Event object. What are you trying to access? If it's the input itself, then use event.target (for example event.target.value to get its value), or if you want to get the table cell, then use event.target.parentNode.

I'm not entirely sure what your question is, but I think you want to access the text someone has entered in the textbox and do something with it. You can access this inside of your keydownFunc as
event.path[0].value
Here event.path[0] will be the input element.
Incidentally you don't need to name the input element, unless you're using the name for something else.

Related

Get cell Value of Table html

I have this code (opt1) to get the text of the cell, which is clicked, but this alerts all of the objects in the table as its in a loop. But when I place it outside the loop I just get a random text from the table and not the one which is clicked (opt2).
I need the textContent of the cell, which is clicked, once, and not any other.
FYI: the cell contains multiple <td> inside <tr>.
The current method gets me the name text either of all cells (opt1) or the last cell (opt2)
HTML:
<table id="table">
<tr class="cell" onclick="RowHandlers()">
<td class="name">Name</td>
<td class="date">Date</td>
<td class="subject">Subject</td>
</tr>
</table>
opt1
function RowHandlers() {
var table = document.getElementById("table");
var name;
for (var i=1;i<table.rows.length;i++) {
name = table.rows[i].cells[0].innerText;
alert(name);
}
}
opt2
function RowHandlers() {
var table = document.getElementById("table");
var name;
for (var i=1;i<table.rows.length;i++) {
name = table.rows[i].cells[0].innerText;
}
alert(name);
}
Your problem seems to be identifying the clicked row after a click has happened.
We can solve this in multiple ways:
In JS, use the event object's target property:
(Preferred) Add handler via addEventListener().
Use a single handler by making use of event propagation.
Add handlers to each row.
(Discouraged) Add handler via onevent property. Only one handler can be added this way.
(Deprecated) Use the global object window.event.
(Discouraged) In HTML, use inline event onclick on each row:
Pass this to the handler function (e.g. <tr onclick="RowHandler(this)">). this in inline events will refer to the clicked element.
Add IDs to each row (e.g. <tr id="row1">). For each row, pass its ID to the function (e.g. onclick="RowHandler('row1')"). Search for the element by the passed ID.
Using addEventListener()
To add a listener, pass the event type and the listener:
const input = document.querySelector("input");
const button = document.querySelector("button");
button.addEventListener("click", clickHandler);
function clickHandler(event) {
console.log(input.value);
input.value = "";
}
<input><button>Click me!</button>
Event propagation
Because events bubble up the DOM, <table>'s listener will also fire when its rows are clicked. We can use event.target to get the event's origin element and find the clicked row:
const table = document.querySelector("table");
table.addEventListener("click", tableClickHandler);
function tableClickHandler(event) {
const row = event.target.closest("tr.cell"); // event.target may be a <td>
const isInRow = Boolean(row);
const isFirstRow = row === table.rows[0];
if (!isInRow || isFirstRow) return;
const name = row.cells[0].textContent;
const date = row.cells[1].textContent;
const subject = row.cells[2].textContent;
console.log("Clicked:", { name, date, subject });
}
table,td{border:1px solid}
<table>
<tr>
<th>Name</th>
<th>Date</th>
<th>Subject</th>
</tr>
<tr class="cell">
<td class="name">Some name</td>
<td class="date">01-01-1999</td>
<td class="subject">Some subject</td>
</tr>
<tr class="cell">
<td class="name">Another name</td>
<td class="date">02-02-2020</td>
<td class="subject">Another subject</td>
</tr>
</table>
Regarding your solutions
You are missing some way of identifying the clicked element; see my suggestions above.
In opt2, you are looping through all rows, but constantly overriding the variable name before using it. Therefore it is the same as only reading the name of the last row.
You may want to use .textContent over .innerText, because it doesn't cause a reflow. If visibility of the content matters, choose .innerText, otherwise .textContent.
A table usually consists of sections (e.g. <thead>, <tbody>, <tfoot>, or an implicit section). Each section consists of rows; each row consists of cells. Therefore, your class name "cell" for a row may be confused with actual table cells (<td>/<th>).
Please check this.
const [row] = document.getElementsByClassName('cell');
row.addEventListener('click', function(event) {
console.log(event.target.innerText);
})
<table id="table">
<tr class="cell">
<td class="name">Name</td>
<td class="date">Date</td>
<td class="subject">Subject</td>
</tr>
</table>

Jquery get id of an element on click of a common class

I am programmatically populating options to increase and decrease the value of an element and store the same in the DB table. To get a better idea, consider the following example:
<tr>
<td id="name_1">Element 1</td>
<td>increase icon</td>
<td>decrease icon</td>
</tr>
<tr>
<td id="name_2">Element 2</td>
<td>increase icon</td>
<td>decrease icon</td>
</tr>
<tr>
<td id="name_n">Element n</td>
<td>increase icon</td>
<td>decrease icon</td>
</tr>
Whenever I click any among the n increase / decrease icon, I need to access the value of #name_n. For which, I wrote the following function:
$(".increase").click(function(){
var id = $(this).attr('id'); //get the id of the element that was clicked.
console.log(id);
var arr = id.split("_"); //split to get the number
var no = arr[1]; //get the number
var name = $("#name_"+no).text(); //get the required value in name_n
console.log(name);
});
//replica for decrease class as well.
Problem :
Every time I click any increase icon, in my console, I'm getting id as inc_1 only! So, the value of name is Element 1 always. Same happens with click function for .decrease.
I have tried with the following ways to get the id:
var id = this.id;
var id = $(this).get(0).id;
var id = $(this)[0].id;
But nothing changed. The same problem persists. What's wrong and how do I resolve this?
Your code looks correct at first glance. Maybe there is some issue with rendering, perhaps the elements really get the same ID.
However, I would recommend a different approach for this task, without using ID's. You can achieve your goal by referencing the TD element relatively to the clicked increase/decrease button. I mean something like this.
$(".increase").click(function(){
var $td = $(this).closest("tr").children(":eq(0)"); //get the TR's first TD
var name = $td.text(); //get the required value in name_n td
console.log(name);
});
You could add a more generic class on the elements you wish tou target after the click(currently #name_n) and use the .closest and .siblings methods.
html
<tr>
<td id="name_n" class="target">Element n</td>
<td>increase icon</td>
<td>decrease icon</td>
</tr>
js
$(".increase").click(function(){
var name = $(this).closest('td').siblings('.target').text();
console.log(name);
});
Here is a working demo https://jsfiddle.net/0hru2jtx/

What is a FORM's equivalent to DOM's getNextSibling?

I have a form, as follows:
<FORM name="form1">
<TABLE onkeypress="focusNextFormElement(event)">
<TR>
<TD><INPUT name="SomeObscureName"></TD><TD>Some data</TD>
</TR>
<TR>
<TD><INPUT name="ThisName"><span>Here's some obscure text.</span></TD><TD>Some data</TD>
</TR>
<TR>
<TD><INPUT name="someNameIdontKnow"></TD><TD>Some data</TD>
</TR>
</TABLE>
</FORM>
Is there a better, more efficient way to write this function? (Of course, this is a simplified example)
function focusNextFormElement(ev){
var el = ev.target;
var f = el.form;
var xx = f.elements.length - 1;
for(var x = 0; x < xx; x++){
if(f.elements[x] === el){
f.elements[x+1].focus();
}
}
}
There may be thousands of elements on this form, and I don't see it as particularly efficient to loop through them all each time the function runs.
Also, I could arbitrarily select an input with my mouse, so a tracking array may not be of much use.
I have no control of the generation of the page, as it's a greasemonkey script to help with automation of another page.
EDIT
Thanks to #Trincot and, from a broader point of view, #Oriol, the answer is found.
function nextFormSibling(el){
var elems = el.form.elements;
var idx = [].indexOf.call(elems, el);
return elems[idx+1] || el;
}
function previousFormSibling(el){
var elems = el.form.elements;
var idx = [].indexOf.call(elems, el);
return elems[idx-1] || el;
}
You could get the element's index calling indexOf() on the form's elements collection. Although this collection is not a native JavaScript array, you can use indexOf.call like this:
function nextFormElement(el) {
var f = el.form;
if (f) return f.elements[1+[].indexOf.call(f.elements, el)];
}
Here is a demo snippet using this function to move focus to the next element when a form element is clicked. This is obviously quite useless, but it shows that it works:
function nextFormElement(el) {
var f = el.form;
if (f) return f.elements[1+[].indexOf.call(f.elements, el)];
}
function focusNextFormElement(ev){
var el = nextFormElement(ev.target);
if (el) el.focus();
}
document.querySelector('form').onclick = focusNextFormElement;
<FORM name="form1">
<TABLE onkeypress="focusNextFormElement(event)">
<TR>
<TD><INPUT name="SomeObscureName"></TD><TD>Some data</TD>
</TR>
<TR>
<TD><INPUT name="ThisName"><span>Here's some obscure text.</span></TD><TD>Some data</TD>
</TR>
<TR>
<TD><INPUT name="someNameIdontKnow"></TD><TD>Some data</TD>
</TR>
</TABLE>
</FORM>
Basically, no, without knowing the strusture of the DOM a priory, you need the index of the element in the elements collection in order to access the next one. And iterating it seems the only way, but consider using indexOf instead of doing it manually.
However, if you want to access the next element multiple times, you can do it only once and cache the result:
var nextFormElement = (function() {
var cache = new WeakMap();
return function(el) {
if(!cache.has(el)) {
var els = el.form.elements,
idx = [].indexOf.call(els, el);
cache.set(el, els[idx+1] || null);
}
return cache.get(el);
};
})();
If ES6 is not supported you can use a plain object instead of a weakmap, where the values are the next elements, and the keys are some identifiers which you can store in the elements as data-* attributes.
Then, use it like
var next = nextFormElement(event.target);
if(next) next.focus();
var nextFormElement = (function() {
var cache = new WeakMap();
return function(el) {
if(!cache.has(el)) {
var els = el.form.elements,
idx = [].indexOf.call(els, el);
cache.set(el, els[idx+1] || null);
}
return cache.get(el);
};
})();
document.forms.form1.addEventListener('keypress', function(event) {
var next = nextFormElement(event.target);
if(next) next.focus();
});
<form name="form1">
<div>
<input name="SomeObscureName" />
<span>Some data</span>
</div>
<div>
<input name="ThisName" />
<span>Here's some obscure text.</span>
<span>Some data</span>
</div>
<div>
<input name="someNameIdontKnow" />
<span>Some data</span>
</div>
</form>
That said, this code is completely obtrusive, and I don't recommend it. If the user wants to focus the next text focusable field, he can use the tab key. If he doesn't want to change focus and you do it for him, it will be so annoying.
Assuming that the HTML strictily adeheres to your example you can try :
function focusNextFormElement (ev) {
// First find the tr element of this input...
var tr = ev.target.parentNode.parentNode; // find the tr node
// Advance to the next table row ...
tr = tr.nextElementSibling
// Then focus the input element in that row...
tr.querySelector ('input').focus ();
}
Could you elaborate as what are you planning to achieve? Do you want to keep shifting your Input Focus to Input Elements? If so, it's better you should use the 'tabindex' attribute.
Rer: http://www.wufoo.com/html5/attributes/27-tabindex.html
If not, then would like to know more about the problem.
Note: Your 'onkeypress' on 'TABLE' element has more problem because, for every key pressed in the input field, it immediately switches to next input field.

How to loop through group of check boxes that have same class name?

I have dynamic table that has table cell with checkbox field. This fields are populated from DB so my table is dynamic. I would like to loop through checkboxes based on their class name. In that loop I want to check value for each check box. For example if value is equal 1 I want checkbox to be checked, if not unchecked. Alo I'm not sure if possible but I would like to set unique ID for each of these check boxes. Since my table is dynamic my fields need to have unique ID's. Here is example of my code:
<table>
<thead>
<tr>
<th>#</th>
<th>Time Slots</th>
<th>Block</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>~(SLOT_LABEL)</td>
<td>
<span>
<input type="checkbox" name="CF-[Events]" class="block" id="block_"+Value that will make this ID unique. value="~(SLOT_ID)"/>
</span>
</td>
</tr>
</tbody>
</table>
Also in current language that I work with to read values from DB I have to use NAME tag. If anyone can help please let me know. Thank you.
You can use the attribute selector to retrieve elements by both their name and value. Try this:
$('input[name="CF-[Events]"][value="1"]').prop("checked", true);
Working example
If you don't want a jQuery solution, it is also possible to fetch these elements with the querySelector:
var inputs = document.querySelectorAll("input.block");
for(var i = 0; i < inputs.length; i++) {
inputs[i].checked = true;
}
you can do it in jquery by each loop
$('.ClassName').each(function(i, o) {
// o is object in your case checkbox
// i is index
// your code
});
$("input[name='CF-[Events]']").each(function() {
if($(this).val() === "1")
{
$(this).prop("checked", true);
}
});

Extracting data from HTML table using jquery

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()
};
});

Categories