Adding a click function to cells in a dynamically created table - JavaScript - javascript

var tableData = document.getElementsByTagName("td");
for(var i = 0; i < tableData.length; i++){
var x = tableData[i];
x.addEventListener("click", fun1(x.textContent));
}
function fun1(y){
document.getElementById("testB").textContent = y;
}
I am trying to add a click function to every cell in a dynamically created table (i have no way of knowing how many cells are in the table or what their id would be but i only need the text contained in the cell)
I want to update a button with the content of the cell and as of right now it does not update on click and instead updates the button to the last value of the table.
I am fairly new to JavaScript and would appreciate any help you could offer.
here is the solution i used thanks to the answer provided below
var tables = document.getElementsByTagName("table");
for(var i = 0; i < tables.length; i++){
tables[i].addEventListener("click",
function(e) {
if(e.target && e.target.nodeName == "TD") {
fun1(e.target.textContent);
}
}
);
}
function fun1(y){
document.getElementById("testB").textContent = y;
}
i went with this solution because the script generating the tables is not mine and as such i do not know what the id of the table i want is and do not know how many tables it actually creates.

You almost certainly do not want to add an event handler individually to each table cell if all (or even many) of them should do the same thing when clicked.
The DOM event model was designed to support delegation, which is a much better solution in cases like this. In brief: individual cells do not respond to clicks, but some ancestor element that is considered a permanent fixture (say, the <table> they are in) detects the clicks on cells and responds to them appropriately.
Here's how to set up delegation:
document.getElementById("#mytable").addEventListener("click",
function(e) {
if(e.target && e.target.nodeName == "TD") {
fun1(e.target.textContent);
}
}
);
Of course you can choose any ancestor element you want, even <body> if that's what is required. Also note that this way of doing things means your code behaves as expected even while cells are added to or removed from the table -- you just do this once on page initialization.

x.addEventListener("click", fun1(x.textContent));
This line is your issue -- addEventListener looks for a function reference, you're just calling the function (which does not return a function). Wrap it in an anonymous function, or bind the value to the reference.
x.addEventListener("click", function() {
fun1(x.textContent)
}, false); //also a good idea to add the event bubbling boolean param
Alternatively, ES6 arrow syntax:
x.addEventListener("click", () => fun1(x.textContent), false);

Related

click event with each loop on dynamicly added elements javascript / jquery

my problem is that the $('input[name=isDefault]').length equal 2, but the each runs ony ones.
$(saveProduct).click(function (e) {
$('input[name=isDefault]').each(function (index) {
var that = this;
console.log(index, 'index');
console.log($('input[name=isDefault]').length);
// do something...
})
})
$('input[name=isDefault]') is dynamicly addes obj during another logic.
I know there is a solution like $(upper container).on("event", dynamic obj inside upper container) - but this causes the click event to be almost everywhere, because .on is attaching event on big area container
EDIT:
I made some changes for testing purpose:
var imgs = imgs = $('div[name=fileContainer]');
console.log('before loop', imgs.length); // = 1
for (var i = 0; i < imgs.length; i++) {
console.log('inside loop', imgs.length); // = 1
//do something - imgs is dynamicly added html element by jquery, when I load page there is 1 element with that name
}
,but when I write $('div[name=fileContainer]').length; in console it's equal 2 (of course when I dynamicly added + 1 fileContainer element)
EDIT:
this isn't the answer, but I omitted the need to use the above loop.
Elsewhere in the code, I already have $(something).on("change", dynamiclyAddedElements function() {}) with difrent main purpouse..., but! there I add addintional function where I created javascipt obj with needed data. Then I can use them in above click event.
$(upper container).on("event", dynamic obj inside upper container) - but this causes the click event to be almost everywhere, because .on is attaching event on big area container
thats not right, you can use the second parameter to specify the selector
e.g
$('#upper_container').on('click', '.saveProduct', function(){...})
will add the click handler to (also dynamically added) .saveProduct and not #upper_container as you claim

Tool to count the bindings on a web page?

I have been tasked with optimizing a web site recently. One thing I suspect is that there are way too many events bound to different objects on the web site. Like each item in a grid having an event bound to it's double click event. This slows down the rendering as it has to set up a binding for each item as things are added. I would expect we could do something like a doubleclick on a grid binding and then determine the item clicked.
Anyway, the client is asking for metrics on things. I need to be able to give them some kind of report that says there are X events bound on the page currently. My change takes it down to Y number of bindings.
Is there any tool out there that you know of that can give me that kind of count? I thought about the event listeners tab on the elements view of chrome developer tools, but I wasn't sure if that listed all the events and if so, how to export the list out to get a count.
Visual Event gives you an overview of all events bound on a particular page.
If you are running chrome you can also get bound events on a DOM node using the command line API:
getEventListeners(el); // {click: [..], mouseover: [..], ...}
This feature can be wrapped in something like:
// Run this in your console:
var count = 0;
// forEach on all elements in the DOM:
[].slice.call(document.getElementsByTagName('*')).forEach(function(el) {
// count all bound events using `getEventListeners`
var events = getEventListeners(el);
for ( var prop in events ) count += events[prop].length;
});
console.log('Total events bound: %d', count);
Hope you get the general idea.
To count bindings on an element:
var count = 0;
$('#myTable *').each(function () {
$.each(getEventListeners(this), function () {
count += this.length;
});
});
console.log(count + ' bindings in #myTable');
To optimize, you will be able to do this:
With raw JS:
document.getElementById('myTable').onclick = function (event) {
console.log(event.target); // Contains the clicked TD tag element
};
With jQuery:
$('#mayTable').on('click', 'td', function() {
console.log(this); // Contains the clicked TD tag element
});
This will decrease bindings from columns * lines of your grid to 1.

Writing this jQuery click function in JavaScript

I am slowly making my way from jQuery to vanilla JS, but I am struggling with wanting to write the following in JavaScript, would I have to setup a 'for' loop for each element? How would I target the child elements an an ID?
Here's what I would want to understand:
$('#id li').click(function(){
$(this).addClass('active');
});
I understand document getElementById to grab the '#id' but how would I achieve getting the child <li> elements? And how would I get the 'onclick' function to work for ANY list element clicked? Would I have to setup a 'for' loop for each element? Any help much appreciated!
Here is a JSFiddle that does what you want:
http://jsfiddle.net/digitalzebra/ZMXGC/10/
(function() {
var wrapper = document.getElementById("id");
var clickFunc = function(event) {
var target = event.originalTarget || event.target;
target.className = "active";
};
wrapper.addEventListener("click",clickFunc);
})();
A little bit of an explanation is in order...
First, I'm using a self executing function to fetch the wrapper div, using getElementById(). This is equivalent to using an id selector in jQuery: $('#id')
Next, I'm attaching a click handler to the element using the function addEventListener() and passing in an event type of click.
This binds the click handler function to the div element, the same as jQuery's click() would do. I'm using something called event bubbling, where a click event on any of the children of the wrapper will call the click handler attached to the wrapper.
Once the user clicks on the element, our function is called. originalTarget or target is the element that the user actually clicked in to, which in this case will always be an li. I'm then setting the class on that element to active.
Here is example for above question
http://jsfiddle.net/G3ADG/2/
(function() {
var id = document.getElementById("id");
var liele = id.getElementsByTagName("li");
for (var i = 0; i < liele.length; i++) {
liele[i].addEventListener("click", function () {
this.className = "active";
})
}
})();
Well I really liked Polaris878 solution as he is not using any loop inside it. In my solution first get HTML node information using document.getElementById this works similarly to $("#id"). than in next step I am fetching all tag type of "li" which are children of "id", than on this li tag array adding event listener to listen click functionality
className is one of attribute that allow to add class on that Node
I have tested above code in mozilla and chrome
This will work (IE >= 10), not want to search classList.add() replacement for IE<10,
var elems = document.querySelectorAll('#id li');
for (var i = 0; i < elems.length; i++) {
var elem=elems[i];
elem.addEventListener("click", function (e) {
this.classList.add('active');
});
}
fiddle

jqGrid: inline-edit whole row, but focus on cell clicked to enter edit mode

Our web application uses jqGrid for data entry, and data is edited inline and in rows.
A customer wishes a more "Excel-like" experience so that when switching a row to inline editing by clicking on a cell, that cell receives the focus (currently, the first cell in the row gets the focus).
I ran into the same issue. Very frustrating. I modified the jquery.jqGrid.js file to accommodate setting focus on the cell clicked on.
In my editRow function I added the following code:
function editRow(id, row, col, z)
{
grid.jqGrid("editRow", id, true, function ()
{
var f = $("input, select", z.target).focus();
return f;
});
}
This will create a variable f that will ultimately be passed to the "oneditfunc" in $.jgrid.extend
The problem is in the setTimeout function.
$("td:eq("+focus+") input",ind).focus();
as this gets executed with focus set to the first editable field from the .each function above. This would be a great place to pass in a focus index, but not possible.
// End compatible
return this.each(function ()
{
var $t = this, nm, tmp, editable, cnt = 0, focus = null, svr = {}, ind, cm, bfer;
...
I then added the following code. (The line with >>> is unchanged. It is only there to help you find the insertion point in the code.)
>>> $($t).triggerHandler("jqGridInlineEditRow", [rowid, o]);
if ($.isFunction(o.oneditfunc))
{
// modified to allow for setting focus on the
// field clicked on
// sets a return value. this was added to the original code. if using
// legacy code, should see no change as r will be undefined
var r = o.oneditfunc.call($t, rowid);
// if get a return value, this indicates you want to set focus
if (r && r[0])
{
var focusIndex = focus;
var i = 0;
// look for the field name that was clicked on
// cm, which is built above, has no index value associated
// with it, so we must keep track of
var focusField = $.grep(cm, function(c)
{
// find the field name which was clicked on
if (c.name == r[0].name)
focusIndex = i;
i++;
return c.name == r[0].name;
});
// if the field is editable, then update focus index
// which is defined above
if (focusField[0].editable)
{
focus = focusIndex;
}
}
}
The most elegant solution? Probably not, but it does allow all legacy code to work and figure out which field was clicked on so can set focus
Unfortunately the grid can only set focus to the first editable field in the row. You can see this in the source code of grid.inlinedit.js. Here it figures out the index of the row to set focus to, by iterating over them and finding the first one:
if(cm[i].editable===true) {
if(focus===null) { focus = i; }
and later on it sets focus:
$("td:eq("+focus+") input",ind).focus();
Which selects the element at the index stored in the focus variable - for reference, see the :eq selector docs.
That said, if you are so inclined you could edit this section of code to add your own custom logic to control which element receives focus...
I agree with Justin that jqGrid has no direct support of the behavior which you need. The way ich which you can receive which is need can be more complex as just one option of jqGrid. Look at the code from the answer. I hope it will help you.
This is serious deficiency in jqGrid.
If grid is wider than window, you also need to scroll right to make clicked cell visible. For this you can use code from
http://www.trirand.com/blog/?page_id=393/help/inline-edit-problem-with-scrolling/
if (rowID && rowID !== lastSelectedRow) {
var scrollPosition = 0;
scrollPosition = grid2.closest(".ui-jqgrid-bdiv").scrollLeft();
grid2.jqGrid('restoreRow', lastSelectedRow);
grid.jqGrid('editRow', rowID, true);
lastSelectedRow = rowID;
setTimeout(function(){
grid2.closest(".ui-jqgrid-bdiv").scrollLeft(scrollPosition);
},100);
For my part, i used both Justin Ethier and Steve's answers to come up with my own solution (for jqGrid 4.4.3):
In the source of jqGrid, i just comment out the following line (this is to prevent the first input of the edited row to receive the focus):
// commented out
// $("td:eq("+focus+") input",ind).focus();
Then, I create a global js variable that will hold the source of the click on the grid:
var clickedCell;
And finally, set that variable when clicking on the cell.
$('#myJqGrid td').on('click', function(e){ clickedCell = this; });
In order to be able to attach the event to the table cell though, we need to be sure the grid has been created, so it has to be done in the "gridComplete" function of the jqGrid, e.g.:
$('#myJqGrid').jqGrid('setGridParam', {
gridComplete: function(id){
$('#myJqGrid td').on('click', function(e){ clickedCell = this; });
}
});
And finally, when editing the row, i retrieve the clicked cell (saved in the variable "clickedCell"), and give the focus to the input or textarea inside it. This has to be done in the "onSelectRow" function of the jqGrid:
$('#myJqGrid').jqGrid('setGridParam',
{
onSelectRow : function(id) {
...
//switching the selected row to editmode
$('#myJqGrid').jqGrid(
'editRow',
id,
{
keys: true,
// when editing the row, give the focus to the input
//or textarea within the last clicked cell
oneditfunc: function() {
$('input, textarea', clickedCell).focus();
}
);
...
}
}
});
The only problem is there really isn't a solution without touching even slightly the source code of jqGrid. If you don't, it will still work, but the first input will still get the focus at first, shifting the horizontal scroll of your page, even after the correct focus has been assigned.
I posted this answer in another stackoverflow related question. Hope this will apply to you.
I somehow succeeded to accomplish this by attaching a dblclick event to every td of the table, I know this is not the best method, but you are free to optimize it how you like, you can also ignore the setTimeout which was used only for testing.
$("#jqGrid").on("dblclick", "td", function (event) {
// setTimeout(function () {
console.log(this);
$(event.target).find("input,select").focus();
// }, 0);
});
Hope this will help you.

Storing IDs of table rows

sorry to bother some of you again. But I still haven't found a working answer to my problem.
I have a good old fashioned HTML table. Each reach has its unique ID. What I want to achieve with JavaScript is that this ID gets stored in a variable once I cklick a link on each row.
Might sound easy to you, I am new to JS and I can't figure it out to the end. Could someone help me please?
Thanks!
The Onclick event of what you are clicking on can call a function like
function RecordClick( elem ) {
mylocalVar = elem.id;
}
by saying
onclick="RecordClick(this);"
When you create your table on the server-side you put this in your HTML (as I understand your scenario):
...
<tr>
<td>
Some link name
</td>
</tr>
...
So why not do this:
<script>
var rowID = -1;
</script>
...
<tr>
<td>
Some link name
</td>
</tr>
...
This is not the best way of doing it, but it gets the job done. If you're using some library like jQuery, you could do it in a completely different way and using closures as well.
you have to take a global variable for this. U can take it into the head section like-
var idHolder;
Now in the table onclick handler u call a function like-
table onclick="extractId(obj)">
The function definition may be like this-
function extractId(var obj)
{
idHolder=obj.Id;
}
This will do ur work, in case of any problem reply.
Since the click event bubbles up the DOM, you can do this by hooking the click event of the table, and then figuring out which row was clicked. This is usually called "event delegation." Something like this (live copy):
// Hook click on the table
document.getElementById('theTable').onclick = function(e) {
var element;
// Handle IE vs. standards
e = e || window.event;
// Find out what was clicked (more IE vs. standards)
element = e.target || e.srcElement;
// Find the row
while (element && element.tagName !== "TR") {
element = element.parentNode;
}
// Found it?
if (element) {
// Yes, remember the ID and show it
targetVariable = element.id;
display("You clicked row " + targetVariable);
}
};
Off-topic: This stuff is made a lot easier if you use a library like jQuery, Prototype, YUI, Closure, or any of several others. For instance, the above code using jQuery looks like this:
$('#theTable').click(function(event) {
var target = $(event.target).closest('tr');
if (target.length > 0 ) {
targetVariable = target[0].id;
display("You clicked row " + targetVariable);
}
});
Live copy
Or using one of the new features:
$('#theTable').delegate('tr', 'click', function(event) {
targetVariable = this.id;
display("You clicked row " + targetVariable);
});
Live copy

Categories

Resources