How can I bind a click event to dynamically created TDs? - javascript

I have a function that takes some json data and puts it into a table. I'm trying to bind a click event to certain table elements. In this case the table has two columns 'Drink Name' and 'Drink Type'. I want the two columns to have different events so I'm trying to give them class tags so I can bind the event to a given class.
The lines wrapped in ** are pseudo code for what I'm trying to do. I've tried looking at a bunch of stuff and can't quite figure this out...thanks!!
function totable(data){
var d = document.getElementById("drinkList");
d.setAttribute("class","panel panel-default");
var dd = document.createElement("div");
dd.setAttribute("class","panel-heading");
dd.appendChild(document.createTextNode("Drink list"));
d.appendChild(dd);
var mytable = document.createElement("table");
mytable.setAttribute("class","table");
var thr = document.createElement("tr");
for(var key in data[0]){
var th = document.createElement("th");
th.appendChild(document.createTextNode(key));
thr.appendChild(th);
}
mytable.appendChild(thr);
for(var i=0;i<data.length;i++){
var r = document.createElement("tr");
for(var key in data[i]){
var td = document.createElement("td");
**td.setClassName("drinkEntry");**
td.appendChild(document.createTextNode(data[i][key]));
r.appendChild(td);
}
mytable.appendChild(r);
}
d.appendChild(mytable);
**$("#drinkEntry").on("click", viewDrink);**
}

Replace **td.setClassName("drinkEntry");** with $(td).click(viewDrink);.

you can use method .on() with document to bind the event instead directly to the class (not id) because the instance doesn't exist already
try this:
$(document).on("click",".drinkEntry", function(){
});

Try this
$('body').on('click','#drinkEntry',function(){
//Your Codes
});

To bind event to dynamically created items you must use .on() jQuery function.
In your case it should looks like:
$('#drinkEntry').on("click", function () {
//logic
});

try out
$('body').on('click', 'td.drinkEntry', function() {
alert("td clicked");
});
JsFiddler Demo
you can also read help full aticle for this : Bind events to dynamically created elements using jQuery

Related

Decide when to load html file

The problem I am encountering is that I want to be able to view the row variable without the waiting for event click.
Works: JQuery event waiting for click, row is view-able:
$('[data-row]').on('click', function() {
var row = $(this).attr('data-row');
alert(row); //
});
Does not work: This is what I want to work...?:
$('[data-row]').(function() {
var row = $(this).attr('data-row');
alert(row);
});
You are missing each
$('[data-row]').each(function() {
var row = $(this).attr('data-row');
alert(row);
});
Unclear. What does alert($row) have to do with deciding to load an HTML file?
You could use setTimeout function to avoid clicking.
Delete the line 1 and 4 of your 2nd code.
This must be inside of jquery document.ready
Your code must be like this
var row = $(this).attr('data-row');
alert(row);

Adding a custom attribute in ASP.NET. accessing it in JQuery

I have a table that is created in ASP.NET C# code behind. The table has several levels of groupings, and when I create the rows for the outer most grouping, I add an custom attribute as follows:
foreach (Table2Row row in Table2Data)
{
// skipping a bunch of irrelevent stuff
...
tr_group.Attributes.Add("RowsToToggle", String.Format(".InnerRowGroupId_{0}", row.GroupHeaderId));
...
}
The attribute is the CSS class name of the inner level rows that I would like to toggle. When the user clicks on the outer level row, I would like to call JQuery Toggle function for all inner level rows that match the custom attribute.
To achieve that effect, I have attached an onclick event to the header rows with the following script in the aspx file:
var tableId = '<%= Table2MainTable.ClientID %>';
$(document).ready(function () {
var table = document.getElementById(tableId);
var groupRows = table.getElementsByClassName("Table2GroupHeaderRow");
for (i = 0; i < groupRows.length; i++) {
table.groupRows[i].onclick = function () { ToggleOnRowClick(table.rows[i]); }
}
});
function ToggleOnRowClick(row) {
var r = $('#' + row.id);
var innerRows = r.attr('RowsToToggle');
$(innerRows ).toggle();
}
So, clicking anywhere on the header row should call the function ToggleOnRowClick, which should then toggle the set of rows below it via the custom attribute RowsToToggle.
When I set a (FireBug) break point in the ToggleOnRow function, the variable r appears to be pointing to the correct object. However, innerRows is not getting set but instead remains null. So am I setting the custom attribute incorrectly in ASP.NET or reading in incorrectly in JQuery?
You did not post the code to generate inner level rows, I am assuming you sat proper classes to them.
There are few issues with the jquery you posted. This line wouldn't work:
table.groupRows[i].onclick = function () { ToggleOnRowClick(table.rows[i]); }
You don't have any groupRows property defined for table object.
We don't care about table row anymore, we care about groupRows[i] and want to pass it to ToggleOnRowClick function.
This line in next function is also wrong:var r = $('#' + row.id);
Solution: Change your script to this:
var tableId = '<%= Table2MainTable.ClientID %>';
$(document).ready(function () {
var table = document.getElementById(tableId);
var groupRows = table.getElementsByClassName("Table2GroupHeaderRow");
for (i = 0; i < groupRows.length; i++) {
groupRows[i].onclick = function () { ToggleOnRowClick(this); }
}
});
function ToggleOnRowClick(row) {
//var r = $('#' + row.id);
var innerRows = $(row).attr('RowsToToggle');
$("." + innerRows).toggle();
}
I have tested the code with dummy data. So if you have any issue, PM me.
This line is your culprit:
table.groupRows[i].onclick = function () { ToggleOnRowClick(table.rows[i])
By the time the event handler runs, table.rows might still exist, but i will be set to groupRows.length+1, which is out of bounds for the array. The handler will get called with an argument of undefined.
Remember, Javascript is an interpreted language! The expression "table.rows[i]" will get interpeted when the handler runs. It will use the last value of i (which will still be set to the value that caused your for loop to end, groupRows.length+1).
Just use
table.groupRows[i].onclick = function () { ToggleOnRowClick(this) }
So, First you shouldn't use custom attributes... they are a sin!
Please use data attributes instead, so that is what I'm going to use in the code, should be an easy fix regardless.
If this doesn't work then I'd be very very interested in seeing a dumbed down HTML snippet of the actual output.
$(document).ready(function () {
$('#MYTABLE').on('click', '.Table2GroupHeader', function() {
var attr_if_you_insist_on_sinning = $(this).attr("RowsToToggle");
var data_if_you_like_not_sinning = $(this).data("RowsToToggle");
//if the row is like <tr data-RowsToToggle=".BLAH" or th etc
//asumming you set the attribute to .BLAH then:
var rows_to_toggle = $(data_if_you_like_not_sinning);
rows_to_toggle.toggle();
//assuming you set it to BLAH then:
var rows_to_toggle = $("."+ data_if_you_like_not_sinning);
rows_to_toggle.toggle();
});
});
$(document).ready(function () {
$('#<%= Table2MainTable.ClientID %> .Table2GroupHeader').each(function(){
$(this).click(function(){
$(this).toggle();
});
});
});

Delete current parent node in recursion

I created multiple divs which class="extra". Then I add delete buttons to each div in order to remove each div respectively. So my code is:
var exr = document.getElementsByClassName("extra");
for(var i = 0 ;i<exr.length;i++){
var delbt = document.createElement("button");
delbt.className="floatbutton_3 font_b"
delbt.innerHTML="delete";
exr[i].appendChild(delbt);
delbt.onclick= function(i){ return function(){ exr[i].parentNode.removeChild(exr[i]) } }(i);
}
The problem is, the button can't removethe button it should remove. It seems that after last delete, the index is changed. How to avoid this from happening?
Thanks!
Use this within the onclick function to reference the button that was clicked — so by replacing excr[i] with this.parentNode, your code should execute as intended.
Do this...
var exr = document.getElementsByClassName("extra");
for(var i = 0 ;i<inserter.length;i++){
var delbt = document.createElement("button");
delbt.className="floatbutton_3 font_b"
delbt.innerHTML="delete";
exr[i].appendChild(delbt);
delbt.index = i;
delbt.onclick= function(i){ var me = this; return function(){ exr[me.index].parentNode.removeChild(exr[i]) } }(i);
}
Since getElementsByClassName() returns a live set, you could make a shallow copy first:
var exr = [].slice.call(document.getElementsByClassName("extra"), 0);
You could use currentTarget on the click event.
This will only remove the parent div of the button clicked.
var exr = document.getElementsByClassName("extra");
for(var i = 0 ;i<exr.length;i++){
var delbt = document.createElement("button");
delbt.className="floatbutton_3 font_b"
delbt.innerHTML="delete";
exr[i].appendChild(delbt);
delbt.onclick= function( event ){
event.currentTarget.parentElement.remove();
}
}
The code looks like it should work, but only as long as you don't add or remove elements. And that's what you are doing.
There is a simpler and more reliable solution: You can use this inside the event handler to refer to the current element.
delbt.onclick = function() {
var div = this.parentNode; // you want to remove the div, not the button
div.parentNode.removeChild(div);
};
I recommend to read the excellent articles about event handling on quirksmode.org, especially about traditional event handling (since that's what you are using).
getElementsByClassName() returns an HTMLCollection. This is a live list that changes when the underlying structure changes. So even if you don't activly delete the elements from the list, they still will be removed from it automatically when you remove the corresponding HTML Element.
By passing i to the anonymous function, you kind of fixed the index. So whenever you click on the third delete button it will try to delete the third element in the list. But if you have already deleted the first and the second element, the list will be modified too. So your third button will be represented by the first item in your list but it will try to find the third one.
To avoid this problem you can pass the complete element to the anonymous function and not just the index:
for(var i = 0 ;i<exr.length;i++){
var delbt = document.createElement("button");
delbt.className="floatbutton_3 font_b"
delbt.innerHTML="delete";
exr[i].appendChild(delbt);
delbt.onclick = function(el){ return function(){ el.parentNode.removeChild(el) } }(exr[i]);
}
I'll add a jQuery solution:
$('.extra').append(function() {
return $('<button />', {'class':'floatbutton_3 font_b', html:'delete'});
});
$('.floatbutton_3').on('click', function() {
$(this).closest('div').remove();
});
FIDDLE

Using variable from .closest() into a selector

I am sure this is something very easy to do, but I'm stuck.
I have multiple forms in a page each one with a "duplicate" button.
The button duplicates a row and put it at the end of a table that is inside of each form. The class of the tables is '.table-add'. So I'm trying to tell the button to find the closest form element and add it to the path on my selector, but it is not working. I know it can work if I put an ID in each form and call for them, but that's not what I want. Thanks for help.
This is my code:
i=0;
$(".duplicate").on("click", function(e) {
i++;
$newID = 'clon'+i;
selectedObj = $($( e.target ).closest("form"));
alert(selectedObj.html()); //**THIS WORKS**
$cloned = '<tr class="'+$newID+'">'+$(selectedObj+' .table-add tr:first').next().html()+'</tr>';
$(selectedObj+' .table-add > tbody:last').append($cloned);
});
Just use this:
var i=0;
$(".duplicate").on("click", function(e) {
i++;
var $newID = 'clon'+i;
var selectedObj = $(e.target).closest("form");
alert(selectedObj.html()); //**THIS WORKS**
var $cloned = '<tr class="'+$newID+'">'+selectedObj.find('.table-add tr:first').next().html()+'</tr>';
selectedObj.find('.table-add > tbody:last').append($cloned);
});
selectedObj is a jQuery object so you can use the .find() function to select .table-add tr:first and '.table-add > tbody:last'.

Attaching change() even listeners to select/dropdown dynamically

I'm creating 3 dropdowns/select boxes on the fly and insert them in the DOM through .innerHTML.
I don't know the ID's of the dropdowns until I created them in Javascript.
To know which dropdowns have been created, I create an array where I store the ID's of the dropdowns I have created.
for(var i=0; i<course.books.length; i++)
{
output+="<label for='book_"+course.books[i].id+"'>"+ course.books[i].name +"</label>";
output+="<select id='variant"+course.books[i].id+"' name='book_"+course.books[i].id+"'>";
output+="<option value='-'>-- Select one --</option>";
for(var j=0; j<course.books[i].options.length; j++)
{
output+="<option value='"+course.books[i].options[j].id+"'>"+course.books[i].options[j].name+"</option>";
}
output+="</select>";
}
Now I have an array with 3 id's like:
dropdown1
dropdown2
dropdown3
What I want to accomplish with Javascript (without using jQuery or another framework) is to loop over these 3 dropdowns and attach a change event listener to them.
When a user changes the selection in one of these dropdown, I want to call a function called updatePrice for example.
I'm a bit stuck on the dynamic adding of event listeners here.
Now you have added your code its straight forward and you can ignore my verbose answer !!!
output+="<select id='variant"+course.books[i].id+"' name='book_"+course.books[i].id+"'>";
could become :
output+="<select onchange="updatePrice(this)" id='variant"+course.books[i].id+"' name='book_"+course.books[i].id+"'>";
This will call the updatePrice function, passing the select list that changed
However
IMO its far better (from a performance point of view for a start) to create elements in the DOM using the DOM.
var newSelect = document.createElement("select");
newSelect.id = "selectlistid"; //add some attributes
newSelect.onchange = somethingChanged; // call the somethingChanged function when a change is made
newSelect[newSelect.length] = new Option("One", "1", false, false); // add new option
document.getElementById('myDiv').appendChild(newSelect); // myDiv is the container to hold the select list
Working example here -> http://jsfiddle.net/MStgq/2/
You got the array already? Then you can do this:
function updatePrice()
{
alert(this.id + " - " + this.selectedIndex);
}
var list = ["dropdown1", "dropdown2"];
for(var i=0;i<list.length;i++)
{
document.getElementById(list[i]).onchange = updatePrice;
}
Fiddle: http://jsfiddle.net/QkLMT/3/
That won't work across browsers.
You'll want something like
for(var i = 0; i < list.length; i++)
{
$("#"+list[i]).change(updatePrice);
}
in jquery.

Categories