views:

241

answers:

5

As an easter egg for a part of one of my sites, I have in my footer #left tag a "Easter" egg that changes each time you click it, revealing funny messages:

$('#left').append(" <span><a href='#' id='dontclick1'>Don't click.</a></span>");

// time for some fun
$('#dontclick1').click(function() {
$('#left span').html("<a href='#' id='dontclick2'>I told you not to click.</a>");
return false;
});

$('#dontclick2').click(function() {
$('#left span').html('<a href="#" id="dontclick3">Now you will suffer.</a>');
return false;
});

$('#dontclick3').click(function() {
$('#left span').html('<a href="#" id="dontclick4">Shame!</a>');
return false;
});

$('#dontclick4').click(function() {
$('#left span').html('<a href="#" id="dontclick5">You shouldn't click.</a>');
return false;
});

$('#dontclick5').click(function() {
$('#left span').html('<a href="#" id="dontclick6">But you did. Sigh.</a>');
return false;
});

In my footer, the append adds the message dynamically so people without JavaScript won't see something unclickable. However, the first time it's clicked, it changes to the second message, but it won't work after that.

What am I doing wrong? Is it because of the return false inside there? Without it, it jumps to the top of the page. I'm confused.

+13  A: 

Err... that's a pretty horrible way of doing what you want, but you will fix it by replacing all the .click with .live('click' - at the time of you appending the events to all the elements, dontclick2, 3, etc. do not yet exist. Read up on live for more, or search around this site as this problem comes up quite often.

EDIT:

I couldn't resist. While John's answer provides a decent way for doing what you want, I am not a huge fan of mixing the Javascript with the markup like this. Whenever you want to add new funny messages and such you will have to be messing around with Javascript arrays and such which is kind of uncool.

The best way to do this (at least in my humble opinion) would be to have markup like this in your page:

<ul id="easter_messages" style="display: none;">
  <li>Don't click</li>
  <li>I told you not to click.</li>
  <li>Now you will suffer.</li>
  <li>Shame!</li>
  <li>You shouldn't click.</li>
  <li>But you did. Sigh.</li>
</ul>

This would be a semantically correct representation of what you have. You could use CSS like this on it:

#easter_messages ul, #easter_messages li { margin: 0; padding: 0; }
#easter_messages li { cursor: pointer; list-style-type: none; display: inline; }

And finally, use this jQuery code:

$('#easter_messages').show().find('li').hide().click(function() {
    var $li = $(this).next('li');
    if($li.length == 1) {
      $(this).hide();
      $li.show();
    }
}).filter(':first').show();

This code would essentially show the list, bind a click handler that finds the next list element relative to the one being clicked on, if it finds one it hides itself and shows that one, and then outside the click we show the first one to get things started.

And this is an example of it in action.

Paolo Bergantino
+1. I love .live()
Jonathan Sampson
+3  A: 

Because you're setting event hadlers to elements that don't yet exist.

Try

.live("click", function() {
});

instead .click(function() { ...

ppiotrowicz
Paolo already provided this solution 2 minutes ago. It would be courteous to upvote him rather than providing duplicate solutions.
Jonathan Sampson
Didn't see his answer, before I posted mine. But i've already upvoted his answer :).
ppiotrowicz
You can delete your answer if it's a duplicate (Keeps the site lean and mean, full of unique solutions). Thanks for upvoting him too.
Jonathan Sampson
+2  A: 

The elements are created after binding click event. You should bind after the element is created.

$('#dontclick2').click(function() {
   $('#left span').html('<a href="#" id="dontclick3">Now you will suffer.</a>');
   $('#dontclick3').click(function() {
   $('#left span').html('<a href="#" id="dontclick4">Shame!</a>');
   return false;
   });

   return false;
});
Daniel Moura
jQuery introduced the .live() method for things like this. Nesting multiple creates/handlers is an inefficient way of accomplishing this. That being said, it WAS the way to accomplish it up until recently - so don't feel bad ;)
Jonathan Sampson
When jQuery introduced live?
Daniel Moura
"Added in jQuery 1.3: Binds a handler to an event (like click) for all current - and future - matched element. Can also bind custom events."
Jonathan Sampson
If you're using 1.2.6 or earlier, see also Ariel Flesler's jQuery Listen plugin, it operates very similarly. http://flesler.blogspot.com/2007/10/jquerylisten.html
Funka
Live is the way to go. I used it a few times and it just a great feature.
Aduljr
+2  A: 

I'd leave a comment, but I don't have the rep yet, so I'll say it here. :)

.live() is a GREAT way to handle this sort of thing, but it requires jQuery 1.3. If you're stuck in jQuery 1.2.6, the answer as suggested by Daniel Moura is the proper approach. :)

Brian Arnold
Upvoting just to get you one step closer to commenting ;)
Jonathan Sampson
Appreciate it! :D
Brian Arnold
+7  A: 

You can use live() as others have mentioned. For an alternative let me suggest a little more elegant solution using an array of messages and a single click handler.

$('#left').append(" <a href='#' id='dontclick'>Don't click.</a>");

// time for some fun
var messageNumber = 0;

$("#dontclick").click(function() {
    var messages = [
        "I told you not to click.",
        "Now you will suffer.",
        "Shame!",
        "You shouldn't click.",
        "But you did. Sigh."
    ];

    $(this).text(messages[messageNumber]);

    // Cycle to the next message unless we're already there.
    if (messageNumber < messages.length - 1) {
        messageNumber += 1;
    }

    return false;
});

I've changed it so you just create the <a id='dontclick'> once, and it doesn't need a number any more. The click handler is only registered once and each time it's called it displays a different message in the #dontclick link. The $(this) in the $(this).text(...) line, by the way, refers to $("#dontclick"). It's just a little shorter to write $(this).

John Kugelman
+1 Much nicer as long as the link-text is all pre-loaded, etc.
Jonathan Sampson
+1 for showing him how it should be done. :)
Paolo Bergantino
Wow; thanks for pointing it out so explicitly!
Brandon Wang
It doesn't work, though, when I put it in. I tried to replace the "this.text" with "this.html" but it still doesn't work. Is it anything that I'm doing wrong?
Brandon Wang
Works now, thanks!
Brandon Wang