In jQuery, the .click()
function is a one time call. This means that, when you call it on a selector, like $('.my-class').click(function(e){ alert('click'); });
, it only applies that callback to the elements that match the selector at that exact moment in time. Putting the .click()
call into the $(document).ready()
function, makes it apply after the entire page loads. If it lives outside of the $(document).ready()
function, then it runs immediately when it is encountered by the browser. Here is an example to kind of demonstrate what I mean, since examples often help:
<html>
<head>
<script src="/path/to/jQuery.js"></script>
...
</head>
<body>
<div class="is-clickable click1">I change color and cause a popup</div>
<script type="text/javascript">
$('.is-clickable').click(function(e) {
alert('clickable div? blasphemy!');
});
</script>
<div class="is-clickable click2">I grow in size</div>
<script type="text/javascript">
$('.click1').click(function(e) {
$(this).css({ color:'#ff0000' });
});
$('.click2').click(function(e) {
$(this).css({ fontSize:'300%' });
});
</script>
...
</body>
</html>
Ok so in the first JavaScript block, at first glance, you might think that $('.is-clickable')
matches 2 elements, both of the divs. This is wrong. It only matches the first div. The reason is because, at the moment that the JavaScript block is executed, only one element with the is-clickable
class, exists. The second div, that also has the class is-clickable
is created after the first JavaScript block runs. The second JavaScript block runs as expected. Both selectors each find a single element, because at this point in time there is both one element with the click1
class and one with the click2
class.
If you were to wrap these bits of JavaScript in the $(document).ready()
block, then their execution would be deferred until after the entire page loads.
<html>
<head>
<script src="/path/to/jQuery.js"></script>
...
</head>
<body>
<div class="is-clickable click1">I change color and cause a popup</div>
<script type="text/javascript">
$(function() {
$('.is-clickable').click(function(e) {
alert('clickable div? blasphemy!');
});
});
</script>
<div class="is-clickable click2">I grow in size</div>
<script type="text/javascript">
$(function() {
$('.click1').click(function(e) {
$(this).css({ color:'#ff0000' });
});
$('.click2').click(function(e) {
$(this).css({ fontSize:'300%' });
});
});
</script>
...
</body>
</html>
This code now yields one div that pops an alert()
and changes color & one div that pops an alert()
and grows in text size. The first JavaScript bit is now executed after the entire page loads, thus the selector $('.is-clickable')
now actually finds 2 matches, and therefore adds the alert()
bit to both.
Your problem sounds like you suffer from this; however, your problem also sounds like you suffer from the exact same problem with different timing. It sounds like when you put the code in the $(document).ready()
block, it runs correctly, and your $('.is-clickable')
selector actually matches both divs (from my example). But after you manipulate the DOM, and after the $(document).ready()
block has already run, your click no longer works. This is because, again, your code has run before the elements (which you are trying to modify the click event of) exist. You can solve this easily. You can do one bit of code that will run your special click function on all the selected elements, regardless of when they are added to the page. Use the $('.is-clickable').on()
function (well technically the $(document).on('click', '.is-clickable', function(){})
function, lol).
.on()
can be used to apply an event listener to every element that matches the selecter, that ever enters the page, before or after your .on()
function call happens. This is how you would do it, with your code:
// still run your extra setup, after the page loads
$(function() {
$div = $('.period:first');
clearClones();
cloneDiv($div,$('#num_periods').val());
});
// change your old line to use the .on() function
// OLD LINE: $('a.overtime_add_button').click(function(event) {
$(document).on('click', 'a.overtime_add_button', function(event) {
event.preventDefault();
var id = $(this).attr('id');
var id_split = id.split("_");
var period_num = id_split[3];
$('#period_' +period_num).find('#overtime').append(ot_div);
});
Now your click
event is applied to all <a class="overtim_add_button">
that ever enter the page. It applies to ones that currently exist and ones that you will add in the future. You only ever have to define this code once. Now lets say that you only want your code to run on <a class="overtim_add_button">
that live inside of the <div id="special-click-links">
. You could do this, once:
// still run your extra setup, after the page loads
$(function() {
$div = $('.period:first');
clearClones();
cloneDiv($div,$('#num_periods').val());
});
// change your old line to use the .on() function
// OLD LINE: $('a.overtime_add_button').click(function(event) {
$(document).on('click', '#special-click-links a.overtime_add_button', function(event) {
event.preventDefault();
var id = $(this).attr('id');
var id_split = id.split("_");
var period_num = id_split[3];
$('#period_' +period_num).find('#overtime').append(ot_div);
});
The .on()
function, will almost always solve this type of problem. On top of that, .on can be used to defining any element based events you need to hook to an element. It is kinda an all in one function.
Anyways, hope this helps explain why you have the problem, as well as how you can easily solve this problem, now and in the future. Furthermore, I hope that others find it helpful too. Here is a jsfiddle of a working example of how .on()
can be used to solve your problem.