I have a table of students and in each row are their names, a select list to select their attendance for their lesson and then a "Message" link when clicked will popup a a dialog to send a message to the student.
The table is dynamically driven by a select list of courses. For example, a teacher selects a course and then the table is repopulated with all the students within that course. This is done through AJAX. The table body is basically getting written every time a course is selected. My problem is this, when a new course is selected, the div for the dialog becomes visible inside the cell of the Message link. I suspect the problem is to do with AJAX and not being able to rebind the link and click event. How do I therefore overcome this?
This is my table generated in PHP (http://pastebin.com/CTD3WfL6):
public function createTable($cid)
{
$userModel = new Users();
$attendanceModel = new Attendance();
$students = $userModel->getStudents($cid);
$table2 = '<table id="tutorTable">';
$tableHeaders =
'<thead>
<th>Student Name</th>
<th>Attendance</th>
<th>Message</th>
<th>Mobile</th>
<th>Parent Name</th>
<th>Message</th>
</thead>
<tbody>';
$table2 .= $tableHeaders;
foreach($students as $student)
{
$table2 .=
'<tr><td id="studentName">'.$student['firstname'].' '.$student['lastname'].'</td>
<td>
<select class="attendSelect" id="studentSelect"'.$student['id'].'>
<option value="Attended">Attended</option>
<option value="Absent">Did not Attend</option>
<option value="Excused Absent">Excused</option>
<option value="Late">Excused</option>
<option value="Excused Late">Did not Attend</option>
</select>
</td>
<td>
<a href="#MessageStudent" class="popUpLink">Message</a>
<div class="popUpDialog" id="'.$student['id'].'" title="Message '.$student['firstname'].' '.$student['lastname'].'">
<form id="studentForm" action="" method="POST">
<fieldset>
<input type="hidden" value="message_send" name="action"/>
<input type="hidden" value="'.$student['id'].'" name="studentId"/>
<textarea rows="3" cols=35" name="message"></textarea>
<input type="submit" value="Send Message"/>
</fieldset>
</form>
</div>
</td>
<td>'.$student['phone1'].'</td>
<td>Parent name goes here</td>
<td>
<a href="mailto:ParentsEmail@email.com" id="parentEmail">Message</a>
</td>
</tr>';
}
$table2 .= '</tbody></table>';
return $table2;
}
This is the jQuery to handle the dialog and the table:
/** Dialog Handler **/
$('.popUpLink').each(function()
{
$divDialog = $(this).next('.popUpDialog');
$.data(this, 'dialog', $divDialog.dialog(
{
autoOpen: false,
modal: true,
title: $divDialog.attr('title')
}));
}).on('click',function()
{
$.data(this, 'dialog').dialog('open');
return false;
});
/**AJAX to handle table **/
$('#courseSelect').on('change', function()
{
var cid = $('#courseSelect').val();
$.getJSON('?ajax=true&cid=' + cid, function(data)
{
var lessonSelect = "";
var count = 1;
/** populate select list of lessons **/
for(var i in data.lessons)
{
lessonSelect += '<option id="' + count + '" value="' + data.lessons[i].id+ '">' + data.lessons[i].name + '</option>'
count++;
};
var lessonDialog = '<p>' + data.lessons[0].name + '</p>';
var launchLessonDiv = '<a href=" ' + data.launchLesson.reference + ' ">Launch Lesson</a>';
var courseDialog = '<p>' + data.course.fullname + '</p>';
$('#lessonSelect').html(lessonSelect);
$('#lessonDialog').html(lessonDialog);//insert lesson information into lesson dialog
$('#launchLessonDiv').html(launchLessonDiv);//insert link to launch lesson
$('#courseDialog').html(courseDialog);
/**Repopulate table **/
//var lessonCount = 1;
//var table = createTutorTable(data, cid, lessonCount);
//$('table#tutorTable>tbody').html(table);
$('form#tutorTableForm').html(data.table);
});//getJSON
});//Course select
Everything works fine until a new course is selected and the textarea becomes visible inside the cell. I've only just started jQuery last month so bear with me!
I have a simplify example here
Basically, you need a delegate function or most people recommend live. Delegate is basically, similar function to live but instead of bind to the top document, delegate only bind to specific dom that you specified. Therefore, it is not possible to have stopPropagation in live. Also, live is deprecated in jquery 1.7
if you lazy to see the fiddle, here is the code
I hope it helps
As I understands, each row has its own markup for dialogs and the code that creates those dialogs is executed only once, during page load:
But this code should be called on startup AND every time you repopulate your table, because markup for dialogs is located inside row cells. I suggest you to put this code in a function:
Call it right after page load, and each time you repopulate table:
Also, I noticed how you create your table rows inside foreach loop. Every row will have the same id's, like this one
<td id="studentName">
. Having many id's repeated on the page is not OK, it can leads to problems that hard to debug.I hope it helps.
EDIT: Just noticed, it is almost the same approach that @Lazerblade proposed.
You should not be using
click()
with elements which are dynamically added to a page because this jQuery method has no way of binding future events to these elements. It's fine for static pages and documents, but not for functionality which inserts, deletes or modifies objects on the page.Instead, you need to use
on()
because it allows you to bind future events. You need to implement it like this:At the moment, from the sound of things, you're only using
live()
as a potential substitute. Don't use this because as of jQuery 1.7 it is deprecated. Refactoring all out-of-date code like this is an absolute must in any web project -- you can't just refuse to change things because of the range and depth of existing implementation. In fact, that should only motivate you more: because if you leave the site with deprecated software something's going to go wrong.If you're unsure about how you can change from
live()
toon()
, see jQuery's documentation which provides a succinct and easy way to update existing functionality.If I got your question right you should use
or
instead of
I had a similar issue when building with AJAX a dialog for "Upload complete" and such, just destroy the dialog before you create it like:
Here's your javascript, rewritten to include modified syntax for recalling the .each() for the popUpLink after reloading. I've also bound the popUpLink to the outer form wrapper. Also, verify that your jQuery has been updated to the latest version, 1.7.2, in order to use the .on() function: