3

I have a table and I am adding rows to it dynamically, every one of the rows will have 2 cells, one of them will contain an input field where I plan to use the DatePicker UI to, obviously, pick a date. The issue comes when I add the new row, cells and everything add just fine but the input field somehow does not show the DatePicker when I click on it. Reading around I learned I'm supposed to use "Event Delegation" since the new elements were not loaded from the start. But for the life of me I have not been able to understand how to do it, if someone could point me in the right direction that would be great. Thanks

The code I've got so far:

<html>
<head>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-    ui.css">
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script>
$(document).ready(function() {
  $( ".datepicker" ).datepicker();
  $( "#TheTable" ).on("change",".datepicker",function(){
    $( this ).datepicker( "option", "dateFormat", "yy-mm-dd" );
  });
});

function addRow() {   
    var table = document.getElementById("TheTable");
    var row = table.insertRow(-1);
    var cell1 = row.insertCell(0);
    var cell2 = row.insertCell(1);
    cell1.innerHTML='Date';
    cell2.innerHTML='<input type="text" class="datepicker"/>';
}
</script>
</head>
<body>
<table name="MyTable" id="TheTable">
  <tr>
    <td>Date</td>
    <td><input type="text" class="datepicker" /></td>
  </tr>
  <tr>
  <td>Date</td>
    <td><input type="text" class="datepicker" /></td>
  </tr>
</table>
<input type="button" onClick="return addRow()" value ="Add Another Date">   
</body>
</html>
Annatar
  • 87
  • 6

3 Answers3

4
$( "#TheTable" ).on("change",".datepicker",function() {

This should work.

.on() works by listening to a parent element for events that bubble up (see here for explanation of bubbling - first paragraph), so that events fired by new elements bubble up to the parent that is being listened to.

As a side note, I'd like to mention that it is a good idea to listen to the the lowest common parent of the desired elements, otherwise jQuery will evaluate unnecessary events.

See jQuery docs for .on()

Here are the key points of the .on() docs:

.on( events [, selector ] [, data ], handler(eventObject) )

The majority of browser events bubble, or propagate, from the deepest, innermost element (the event target) in the document where they occur all the way up to the body and the document element. In Internet Explorer 8 and lower, a few events such as change and submit do not natively bubble but jQuery patches these to bubble and create consistent cross-browser behavior.

If selector is omitted or is null, the event handler is referred to as direct or directly-bound. The handler is called every time an event occurs on the selected elements, whether it occurs directly on the element or bubbles from a descendant (inner) element.

When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.

Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on()

Bill
  • 3,478
  • 23
  • 42
  • If I understand correctly when I provide a `selector` the event is delegated, which means the event occurs for the descendants of the element that share the selector. According to the Documentation I've got two options; either I select the elements and attach event handlers after the new HTML or use delegated events. The code that you provided does that, it uses a delegated event to attach an event handler. Am I correct? I implemeted your solution but I can't get it to work. I updated the code to what I have now. I don't even know what I'm doing wrong... I appreciate your help. – Annatar Jan 15 '14 at 18:15
  • Yes you understand correctly. You could attach a listener to the element when you add it - inside your `addRow()` function but by doing so you are adding extra event listeners; just the one sounds better, no? ;P If you were going to do it that way, remember to remove the listener when you remove the row! Your code looks spot on now, I couldn't make any more suggestions without seeing more. Can you link me to a live version? – Bill Jan 16 '14 at 02:56
  • Ah - I found your bug! You have to initialize the date picker on the new element before it can work. I pasted it all into a [jsFiddle](http://jsfiddle.net/FAcF2/) so that I could debug it, look at the last line I added to `addRow()`. http://jsfiddle.net/FAcF2/ – Bill Jan 16 '14 at 03:03
  • Thanks a lot, it works perfectly now. I sort of figured I needed to initialize the datepicker on the new element to get it to work, thus my previous comment asking how to do it... but I just could not figure it by myself. Again, thanks a lot. Happy coding! – Annatar Jan 16 '14 at 04:55
  • Please, correct me if I'm wrong but. are you are using `.not()` to select the elements that do not belong to the .datepicker class and then you are adding them to it? – Annatar Jan 16 '14 at 05:04
  • When you initialize the date picker on an element, it gets given the class `.hasDatepicker`. I'm using `.not('.hasDatepicker')` so that we don't try to initialize the date picker on elements that already have it – Bill Jan 16 '14 at 21:42
1

You need to be listening to the parent to be able to target dynamically drawn content. Replace:

$(document).ready(function() {
  $( ".datepicker" ).datepicker();
  $( ".datepicker" ).on("change",function() {
    $( ".datepicker" ).datepicker( "option", "dateFormat", "yy-mm-dd" );
  });
});

with:

$(document).ready(function() {
  $( ".datepicker" ).datepicker();
  $( '#TheTable' ).on("change",'.datepicker',function() {
    $( this ).datepicker( "option", "dateFormat", "yy-mm-dd" );
  });
});
Lawren
  • 223
  • 2
  • 8
  • This is the same as my answer, except you don't need to listen to the entire document - this will slow your your script alot, especially with large pages. You need only delegate the closest common parent. – Bill Jan 13 '14 at 20:10
  • 1
    I just caught that. I edited my answer and am up-voting yours. – Lawren Jan 13 '14 at 20:12
  • This doesn't initialize the datepicker on the new datepicker elements. – Kevin B Jan 13 '14 at 20:56
  • It is still not working for me, how should I initialize the datepicker on the new datepickers elements? – Annatar Jan 14 '14 at 19:18
  • @Annatar did you look at my answer? – Bill Jan 14 '14 at 23:44
0

You don't need event delegation; just set your options at the beginning. Then, in your function, trigger the datepicker method (with the desired options) on the new element as you're adding it.

$(".datepicker").datepicker("option", "dateFormat", "yy-mm-dd");

function addRow() {   
    var table = document.getElementById("TheTable");
    var row = table.insertRow(-1);
    var cell1 = row.insertCell(0);
    var cell2 = row.insertCell(1);
    cell1.innerHTML='Date';
    $('<input type="text" class="datepicker"/>')
        .datepicker("option", "dateFormat", "yy-mm-dd")
        .appendTo(cell2);
}
Blazemonger
  • 90,923
  • 26
  • 142
  • 180
  • How are you going to set datepicker options on an input using the datepicker option method before making it a datepicker? – Kevin B Jan 13 '14 at 20:14