jQuery selector doesn't update after dynamically adding new elements
My selector is:
$('section#attendance input:last')
However, I append another input to section#attendance. I want the selector to now select that element (as it should, because of the :last
. However, for some reason, it doesn't, and I'm not too sure why?
Here's my full code:
$('section#attendance input:last').keydown(function(e) {
if (e.which == 9)
{
var $this = $(this);
var num = parseInt($this.attr('name').match(/\d+(,\d+)?/)[0]) + 1;
$this.parent().append('<input type="text" name="attendance[' + num +']" value="Name and Position" class="medium" />');
}
});
New Code:
$("section#attendance").on("keydown", "input:last", function(e) {
if (e.which == 9) {
var $this = $(this);
var num = parseInt($this.attr('name').match(/\d+(,\d+)?/)[0]) + 1;
$this.after('<input type="text" name="attendance[' + num +']" value="Name and Position" class="medium" />');
}
});
EDIT: The issue seems to be on the 'input:last', as it works if I just use input. Also, if I add class 'last' to the last element, it works when I use 'input.last' as a selector.
Depending upon what jQuery version you are using, you should use either .delegate()
(before jQuery 1.7) or .on()
(jQuery 1.7+) to have delegated event handling that will handle events for elements that are added dynamically. Note .live()
has been deprecated from all versions of jQuery so it should no longer be used.
Using .on()
, you can use this:
$("#attendance").on("keydown", "input:last", function(e) {
if (e.which == 9) {
var $this = $(this);
var num = parseInt($this.attr('name').match(/\d+(,\d+)?/)[0]) + 1;
$this.parent().append('<input type="text" name="attendance[' + num +']" value="Name and Position" class="medium" />');
}
});
Using .delegate()
would be similar (except the arguments are in a different order). You can see both of them in the jQuery doc .delegate()
and .on()
.
Delegated event handling works using the "bubbling" capability of some javascript events. This allows you to attach an event handler to a parent object (that does not come and go) and have it look at each event that "bubbles" up to it from a child, check to see if the event comes from an object with an appropriate selector, and if so, execute your event handling code. This way, as objects come and go, their events are still handled appropriately.
As you were originally doing things, you would bind the event handler directly to the object that matched the selector 'section#attendance input:last'
at the time the event binding code ran. From then on, it was bound directly to that specific object regardless of whether that object still matches the selector or if new objects are added to the DOM that would match the selector. Delegated event handling using .delegate()
or .on()
allows the event handling behavior to be more dynamic - automatically adjusting to changes in the DOM. You just have to bind the event to a common parent object that will not be changing and it can automatically monitor all it's children.
To react to dynamically inserted content, you will have to use delegated events. The currently recommended function to do this is .on()
(live
is deprecated, bind
and delegate
are superseded).
Don't use live! Use on
The live method has been deprecated due to bad performance, use the on method now instead.
$('section#attendance').on('keydown', 'input:last', function(e) {
/* ... code ... */
});
Also there's no reason to have the section
tag in front of your id selector.
The problem most likely is because you are using the keydown event. That actually uses bind against the selector. Bind only effects items that exist when it is first attached. The JQuery docs says:
bind() attacheds events to elements that exist or match the selector at the time the call is made. Any elements created afterwards or that match going forward because the class was changed, will not fire the bound event.
You should look to replace keydown with something like the following:
$('section#attendance).on("keydown", "input:last", function(e) {...});