This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 2 years ago.
I'm trying to pass arguments to onclick handler. The table rows are constructed dynamically in a loop and each row contains a tag with different args. Suppose there are two rows, and when clicking the first img, the argument is always the argument corresponding to the second(last) row.
JS code
for(...){
id = get_id(); // every row's id is different
img = '<img src="/img/icon-view.png" height="20" width="20" style="cursor:pointer"
onclick="view_detail(id)">'
row = ('<tr> ' + '<td>' + img + '</td>' + '</tr>')
$("#table_1").append(row)
}
function view_detail(id){
...
// the first row's id is always the second id's row
}
Comment:
Jquery passing ID from <img> to function gives a solution, but the trick here is id is a variable.
<img onclick="Myfunction(this.id)" src="files/pic/Website.png" id="Website">
Finally I found a workaround which is very simple - create img element and converts to string, and then put the string in row tag.
Sample code:
var img = document.createElement("img")
img.setAttribute("id","local_variable")
img.setAttribute("onclick","view_detail(this.id)")
var img_str = img.outerHTML(img)
row = '<tr>' + '<td>' + img_str + '</td>' + '</tr>'
Code in onXXX attributes is executed in the global scope, so it can't use captured local variables in a closure.
Nurul Huda's answer is ideal. But a simple fix to your code is to substitute the actual value of id rather than referencing the variable.
id = get_id(); // every row's id is different
img = `<img src="/img/icon-view.png" height="20" width="20" style="cursor:pointer"
onclick="view_detail(${id})">`
Dont use string to build DOM, but use document.createElement() instead. Then you can get instance of each target, add any event listeners, you can also pass those instances as arguments to other function as you want.
Here is a sample snippet you can try (I used button instead of img).
const tbody = document.querySelector('table tbody')
const btnAddRow = document.querySelector('button#btn-add-row')
btnAddRow.addEventListener('click', e => {
const row = document.createElement('tr')
const col = document.createElement('td')
const content = document.createElement('button')
content.innerHTML = `Row ${tbody.childNodes.length}`
content.addEventListener('click', e => {
alert(`${content.innerHTML} clicked!`)
})
col.append(content)
row.append(col)
tbody.append(row)
})
<button id="btn-add-row">Add row</button>
<hr>
<table border="1">
<thead>
<tr>
<th>Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Another advantage is, your code becomes more readable and modern.
Related
I am trying to use code from html into javascript using code from https://www.geeksforgeeks.org/how-to-get-the-id-of-the-clicked-button-using-javascript-jquery/.
I am creating a memory game I have the images stored in an array and am able to click on it and it flips. I want to take the ID of 2 images and compare them but I need to pass the clicked ID variable from html into a javascript variable to store them for processing:
Here is my code for the detectClick function
**function detectClick(clicked) {
htmlID = clicked;
return htmlID;}**
Here is my HTML sample of my table of the first 4 array elements:
***<tr class ="success" >
<td onClick="changeimage('tblArr0',tblArr[0]);"><img src='memImages/CardBack.png' id="tblArr0"
onClick="document.getElementById('tblArr0').src='tblArr[0];detectClick(this.id);';"draggable="true" ondragstart="dragstart_handler(event)"></td>
<td onClick="changeimage('tblArr1',tblArr[1]);detectClick(this.id);"><img src='memImages/CardBack.png' id="tblArr1"
onClick="document.getElementById('tblArr1').src='tblArr[1]';"draggable="true" ondragstart="dragstart_handler(event)"></td>
<td onClick="changeimage('tblArr2',tblArr[2]);detectClick(this.id);"><img src='memImages/CardBack.png' id="tblArr2"
onClick="document.getElementById('tblArr2').src='tblArr[2]';"draggable="true" ondragstart="dragstart_handler(event)"></td>
<td onClick="changeimage('tblArr3',tblArr[3]);detectClick(this.id);"><img src='memImages/CardBack.png' id="tblArr3"
onClick="document.getElementById('tblArr3').src='tblArr[3]';"draggable="true" ondragstart="dragstart_handler(event)"></td>
</tr>***
I need to pass the detectClick(this.id) into the variable htmlID.
Any other ways of doing this or suggestions would help.
I have the the tblArr[] which contains another array called imgArr which stores the source name of the files for the images. I am able to have my array shuffled, now I need to be able to process the array objects to compare them. I just need to know how to get the ID from the image back into a javascript variable.
Thanks for suggestions and help in Advance!!
This should create your <td>s for as many as are available in the array:
function detectClick(clicked) {
htmlID = clicked;
return htmlID;
}
function loadSuccess() {
let container = document.getElementById("success");
for (let i = 0; i < tblArr.length; i++) {
let new_td = document.createElement("td");
new_td.setAttribute("onClick", "changeimage('tblArr" + i + "',tblArr[" + i + "]);");
container.appendChild(new_td);
let new_img = document.createElement("img");
new_img.src = "memImages/CardBack.png";
new_img.id = "tblArr" + i;
new_img.setAttribute("onClick", "this.src='tblArr[" + i + "];detectClick(this.id);';");
new_img.setAttribute("draggable", "true");
new_img.setAttribute("ondragstart", "dragstart_handler(event)");
new_el.appendChild(new_img);
}
}
<tr class ="success" id="success" onload="loadSuccess()"></tr>
I currently have a table that has a list of email template names being echoed using php. Below is part of the php code. I'm trying to grab the table value and pass it to my JS file where a future AJAX command will pass it to a different file (that I won't have any issues with). My first attempt to alert out the value stated that the value was undefined. My second attempt showed the type of element it was inside (at the time it was a span). Now it's not showing anything. Suggestions?
PHP code:
<table class="departments">
<tr>
<th scope="col" style="width: 175px;">Email Name</th>
';
$get_depts = mysql_query("SELECT dept_name FROM depts where bus_id = '{$_SESSION['bus_id']}'");
while(($department = mysql_fetch_assoc($get_depts)))
{
echo '
<th scope="col" style="width: 175px;">'.$department['dept_name'].'</th>
';
}
echo '
</tr>
';
$get_emails = mysql_query("SELECT id, email_name from emails where bus_id = '{$_SESSION['bus_id']}' ORDER BY email_name ASC");
while(($email = mysql_fetch_assoc($get_emails)))
{
echo '
<tr>
<td id="test" onclick="moveValue()">'.$email['email_name'].'</td>
';
Current JS code:
function moveValue()
{
var x = document.getElementById(test);
var y = x.innerHTML;
alert(y);
}
Javascript:
var y = document.getElementById("test").innerText;
jQuery:
$("#test").text();
To get the HTML:
var html = document.getElementById("test" ).innerHTML;
jQuery:
$("#test").html();
You id attribute would be the same for every td inside the loop. So JS would not know which element you want.
You could try passing this into the onclick method
HTML
<td onclick="moveValue(this);">
JS
function moveValue( elem )
{
alert(elem.innerHtml);
}
I would take a look at jQuery if I were you. It makes all this stuff much easier to achieve.
I don't want to get into all the problems with your code as there are rather a lot. However, getting the value of a <td> element by clicking is trivial to achieve.
You first need to assign a click handler to each cell in your table. The easiest way to do this is to loop through each cell and assign the handler like so:-
var cells = document.getElementsByTagName('td');
for(var i = 0; i <= cells.length; i++){
cells[i].addEventListener('click', clickHandler);
}
function clickHandler()
{
alert(this.textContent);
}
Then every time you click on a cell the clickHandler() will be called and you can run whatever code you wish.
You can see it working in this fiddle
Lots of information here https://developer.mozilla.org/en-US/docs/Web/API
With javascript:
To get raw text without any elements or:
somevar=document.getElementById ( "test" ).innerText;
To get full html code of tag. Contents will be stored in 'somevar' variable.
somevar=document.getElementById ( "test" ).innerHTML;
You can do it either by
function moveValue()
{
var x = document.getElementById('test');
var y = x.innerHTML;
alert(y);
}
or by:
function moveValue(element) {
var y = element.innerHTML;
alert(y);
}
//with the following html code:
<td onclick="moveValue(this)">'.$email['email_name'].'</td>
its work.
function clickValue(elem) {
var x = document.getElementById(elem).innerHTML;
alert(x);
}
<table>
<th>Coba</th>
<tr>
<td id="1" onclick="clickValue('1')">value</td>
</tr>
<tr>
<td id="2" onclick="clickValue('2')">value yg ke 2</td>
</tr>
</table>
Change id="*anyvalue*" and clickValue('*anyvalue*')
Initially adding the element statically like below:
<td valign="top" id="description_div">
*<table class="des_box" id="comment_div">
<tr><td class="head" id=file_comments> The function comments </td></tr>
<tr><td class="comment" id="test_point_comment_info"></td></tr>
</table>*
</td>
Dynamically adding the element as below :
$("#description_div").append(
'<table class="des_box1" id=comment_div><tr><td class="head" id=file_comments> The function comments </td></tr><tr><td class="comment" id=test_point_comment_info_' + id + '></td></tr> </table>')
Now, when I try to fetch the element by its id (that is by "comment_div") ... I am not able to retrieve the dynamically created element. But able to fetch the static element by using $("#comment_div")
I am trying to do following on the element :
$("#comment_div").show();
tried .live() ....but was not able to fetch the dynamic element.
$("#comment_div").live().show();
check box code :
<li><input type="checkbox" name="comment" />Comment</li>
actual functions where am trying to fetch the element:
$("#checkbox_div input:checkbox").click(function() {
var division = "#" + $(this).attr('name') + "_div";
$(division).show();
}
function SetCheckboxes(checkbox_data) {
//SetCookie('pre_checkbox', "1111111111111111")
var checkbox_data = GetCookie('pre_checkbox');
if (checkbox_data == null) {
SetCookie('pre_checkbox', "1111111111111111")
checkbox_data = GetCookie('pre_checkbox');
}
checkbox_array = new Array("owner", "test_time", "bp", "single_use", "num_of_test", "pause", "clearall", "clearclass", "clearmax", "closeall", "qeinbat", "m_lint","geck","header","comment","feature");
for ( i = 0; i < checkbox_data.length; i++) {
var checkbox_name = checkbox_array[i];
var value = checkbox_data[i];
var division = "#" + checkbox_name + "_div";
if (checkbox_name=="geck" || checkbox_name=="header" || checkbox_name== "comment" || checkbox_name=="feature"){
console.log("entering_loop_as_expected")
if (value == "1") {
//alert("1");
$("#checkbox_div input[name='" + checkbox_name + "']").attr("checked", "checked");
$(division).show();
} else {
$(division).hide();
}
continue;
}
Please help me out on this.
.live() is what you wanted but it has been depreciated, you now need to use .on()
$(document).on("click", "#checkbox_div input:checkbox", function(){
//Your code here.
});
Using document for your selector with .on will allow you to bind events to dynamically created elements. This is the only way I've found to do it when the DOM elements don't exist prior to execution.
I do this in a dynamically created table that is sort-able and works great.
EDIT:
Here is an example. Click the button to add a div then click the div to get it's contents.
http://jsfiddle.net/FEzcC/1/
You missed the quotes, also ensure you try to access them before they are added to DOM, e.g they will not be available on DOM ready if they are added on some button click. I think you forgot to give value of id, I have made a live demo.
Live Demo
$("#checkbox_div input:checkbox").click(function() {
var division = "#" + $(this).attr('name') + "_div";
$(division).show();
});
id=1;
$("#description_div").append('<table class="des_box1" id="comment_div"><tr><td class="head" id="file_comments"> The function comments </td></tr><tr><td class="comment" id="test_point_comment_info_' + id + '"></td></tr> </table>');
Edit based on comments and fiddle being provided.
You have few problems with html in the demo
Your html starts with td instead of table
You do not enclose the ids with quotes
You are assigning same id to more then one element, instead assign a
common class and use that.
Live Demo, Problem here are not dynamic element but the wrong HTML / Script
Html
<table>
<tr>
<td valign="top" id="description_div">
<table class="des_box" id="comment_div1">
<tr>
<td class="head" id="file_comments">The function commentszz</td>
</tr>
<tr>
<td class="comment" id="test_point_comment_info"></td>
</tr>
</table>
</td>
</tr>
</table>
<div id="checkbox_div">
<input type="checkbox" name="des" />Comment
</div>
Javascript
$("#description_div").append(
'<table class="des_box" id="comment_div2"><tr><td class="head" id=file_comments> The function comments </td></tr><tr><td class="comment" id=test_point_comment_info_' + 'id' + '></td></tr> </table>');
$(document).on("click", "#checkbox_div input:checkbox", function () {
var division = "." + $(this).attr('name') + "_box";
$(division).show();
});
If you have to use same id for more than one element with is wrong you can use attribute selector. First correct the html by enclosing td within tr and table tag.
Live Demo
$(document).on("click", "#checkbox_div input:checkbox", function(){
var division = "[id=" + $(this).attr('name') + "_div]";
$(division).show();
});
What is id value in + id +, id is undefined in current context. I have put it in single quote and its working fine.
Update: You are using same id comment_div for static and dynamic content, id should be unique in DOM. Use class instead of id for multiple elements
Updated jsFiddle
I'm not sure if I'm doing this the right way. I have table which I fill with rows that each represent a song in a playlist. Right now, I assign a unique ID per row, and also assign som jQuery.data() to each ID.
html += '\
<tr id="track-' + i + '" class="tracks-row"> \
<td class="track"><a id="play-'+ i +'" class="play"></a><a id="play2-' + i + '">' + song.song_track + '<span class="mix-style">' + song_mix + '</span></a></td> \
<td class="artist">' + song.song_artist + '</td> \
<td class="favourites-holder"><a id="favourite-' + i + '" class="favourites"></a></td> \
' + delete_holder + ' \
</tr> \
';
So as you can see, each row has an ID like track-1, track-2 etc.
Is there another way to populate a playlist like this without assigning unique ID's to each track, or is this how it's supposed to be done? Each track has some properties like this:
$("#track-" + i).data("song_id", song.song_id);
$("#track-" + i).data("song_artist", song.song_artist);
$("#track-" + i).data("song_track", song.song_track);
$("#track-" + i).data("song_mix", song.song_mix);
$("#track-" + i).data("ps_id", song.ps_id);
... and also .click events for each track, which allows the user to play, sort, drag etc... It just feels like I'm doing it wrong :)?
You could store a reference to each generated row in your loop (assuming html only contains the HTML for a single row):
var row = $(html).appendTo("#table");
var data = row.data();
data["song_id"] = song.song_id;
data["song_artist"] = song.song_artist;
data["song_track"] = song.song_track;
data["song_mix"] = song.song_mix;
data["ps_id"] = song.ps_id;
row.click(function(){...});
It is not bad to have an ID for an element. But is definitely faster to make use of a reference if you have one and not use jQuery's selector engine over and over again.
Also if you attach the same click handler to every row, it is probably better to just attach one to the table and delegate it, e.g.
$('#table').delegate('tr', 'click', function(){...});
Unique id's makes sense or use the Metadata plugin to store all the extra data related to each row. http://plugins.jquery.com/project/metadata
It will store the data like:
<li class='someclass' data="{some:'random', json: 'data'}">...</li>
And you can query this like this:
var data = $('li.someclass').metadata();
if ( data.some && data.some == 'data' )
alert('It Worked!');
Whether the tr needs a unique id or simply a generic class is going to depend a lot on what you intend to do with either javascript (for targeting the rows) or css (also for targeting the rows). If you need to specifically target one row for styling or script effects, a unique id can be an effective way of doing it. If not, then it may be extra "mark-up" that is unnecessary.
In any case, if you do use id's, do make sure they are unique :-)
May be something like this with jquery:
var songs = function(songList){
this.songs = songlist;
this.init();
}
$.extend(songs.prototype, {
init: function(){
var self = this;
var table = $('#myTableID');
for(var i = 0; i < this.songs.length; i++){
var song = this.songs[i];
var songData = $('<tr><td class="track">'+song.track+'</td><td class="artist">'+song.artist + '</td></tr>');
table.append(songData);
songData.find('td.track:first')click(function){
self.SongClick(song.track);
});
//add other events here
}
}
},
SongClick : function(track){
//add here you click event
},
otherEvent : function(track){
//add otherEvent code
}
});
Like this you can create your javascript object and attach events to the dom elements directly as you parse them. You will not need any id's.
This approach will create a js object with its constructor
so you can say
var so = new songs(mySongList)
when it initializes it will retrieve a table with an id of myTableID and foreach song in the list, it will attach elements to it, and will attach the events directly to those elements.
I have a html table that I reorder based on a CSV list of custom attribute values that I have for each table row. I am using the following function to do it:
for (var i = 0; i < arrCSV.length; i++)
{
$('#' + tableId)
.find('[fname = ' + arrCSV[i] + ']')
.eq(0)
.parents('tr')
.eq(0)
.appendTo('#' + tableId);
}
The table structure is:
<table>
<tr>
<td fname='f1'>something here</td>
</tr>
<tr>
<td fname='f2'>something here</td>
</tr>
</table>
The CSV could be something like this "f2, f1"
I find this is very very slow performing function. Any help in optimizing it is really appreciated.
EDIT:
Based on the article at http://www.learningjquery.com/2009/03/43439-reasons-to-use-append-correctly, one can achieve the greatest boost in performance by calling append only once with the html concatenated string. Can someone help in using this technique to my problem? I am not sure how to go about getting the s HTML in the for loop and appending it once.
I would suggest finding the elements as few times as possible. Store all the matching rows into a "hash" using the attribute value of interest as the key. Go through your CSV, pick the corresponding row out of the hash, push it into an array, then finally append the elements of the array to the table using the jQuery object previously found.
var table = $('#' + tableId);
var rowHash = {};
table.find('[fname]').each( function() {
rowHash[$(this).attr('fname')] = $(this).closest('tr');
});
var rows = [];
for (var i = 0; i < arrCSV.length; ++i)
{
var row = rowHash[arrCSV[i]];
if (row) {
rows.push(row);
}
}
$(rows).appendTo(table);
EDIT: This seems like a slight improvement to my previous code where I was appending each row to the table as it was found. I tested on a table with 1000 rows and it seems to take about 1sec to sort a table that needs to be completely inverted.
If you want to append html only once (like that learningjquery.com article), try following:
$(document).ready(
function()
{
var arrCSV = ['f2', 'f1'];
var tableId = 'mainTable';
var newTable = [];
for (var i = 0; i < arrCSV.length; i++)
{
var row = $('#' + tableId)
.find('[fname = ' + arrCSV[i] + ']')
.eq(0)
.parents('tr')
.eq(0);
newTable.push(row.html());
}
$('#' + tableId).html(newTable.join(''));
};
});
Live version: http://jsbin.com/uwipo
Code: http://jsbin.com/uwipo/edit
Though I personally feel that you should profile your code first and see if it's append which is slow OR that 'find' method call. I am thinking that for a huge table, using 'find method' to find a custom attribute could be slow. But again, there is no point in any guesswork, profile the code and find it out.
If the 'find' method is slow, will it be possible to use id attribute on td instead of giving custom attribute.
e.g.
<table>
<tr>
<td id='f1'>something here</td>
</tr>
<tr>
<td id='f2'>something here</td>
</tr>
</table>
Then your code to find the parent row could be as simple as:
('#' + arrCsv[i]).parent('tr')
EDIT: As pointed out by tvanfosson, this code assumes that arrCSV contains attribute for all the rows. The final table will only contain those rows which are present in arrCSV. Also, this code does not copy 'thead', 'tfoot' section from the original table, though it should be easy to write code which does.
You may have to rethink your algorithm.
Without changing the algorithm, a slight optimization would be:
var $table = $("#" + tableId);
for (var i = 0; i < arrCSV.length; i++)
{
$('[fname = ' + arrCSV[i] + ']:first',$table).closest('tr').appendTo($table);
}
Wait a second...
$('#' + tableId)
Get #myTable
.find('[fname = ' + arrCSV[i] + ']')
Find anything with an attribute of fname equal to i
.eq(0)
Give me the first item of the previous expression
.parents('tr')
Find the parents of type TR
.eq(0)
Give me the first in the previous expression
.appendTo('#' + tableId);
Add that TR to #myTable
Okay. Now that I've broken it down - are you duplicating a Table Row only? If so, the .append() isn't your problem, your choices of selectors is. To further compound my confusion here, the only tags with an attribute of fname are your TR's, so why are you going to their parents() and seeking out the first TR? You're basically asking for TR tags to be placed within TR tags - but that isn't what your Markup example shows.