Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Jquery Help - Beginner

Hi,

I am learning JQuery and have been experimenting with some code. I am trying to click on a button, which displays some text, and then also have a cancel button which appears as a result of the first button click.

This cancel button removes the text added and reverts the html back to how it was before any button clicks. I have the first 'Book Now' button function click working but I am struggling on the 'Cancel' button click function.

Below is my code:

html:

<!doctype html>
<html>
<head>
<title>Our Funky HTML Page</title>
<meta name="description" content="Our first page">
<meta name="keywords" content="html tutorial template">
</head>

<body>
<div id="tours">
  <h1>Guided Tours</h1>
  <ul>
    <li class="usa tour">
      <h2>New York, New York</h2>
      <span class="details">$1,899 for 7 nights</span>
      <button class="book">Book Now</button>
    </li>
    <li class="europe tour">
      <h2>Paris, France</h2>
      <span class="details">$2,299 for 7 nights</span>
      <button class="book">Book Now</button>
    </li>
    <li class="asia tour">
      <h2>Tokyo, Japan</h2>
      <span class="details">$3,799 for 7 nights</span>
      <button class="book">Book Now</button>
    </li>
  </ul>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="application.js"></script>
</body>
</html>

Jquery:

$(document).ready(function() {
  $('.tour').on('click', function() {
    var message = $('<span class="details">Call 1-555-jquery-air to book this tour  </span>');
    var message2 = $('<span><button class="cancel">Cancel</button></span>');
    $(this).append(message);
    $(this).find('button').remove();
    $(this).append(message2);
    $(this).css('color','red');
  });
});


$(document).ready(function() {
    $('.cancel').on('click', function() {
    var message = $('<button class="book">Book Now</button>');
    $(this).append(message);
    $(this).find('.details').remove();

  });

});

Any help would be appreciated, Thanks

3 Answers

Hey Derek Vha, The click on cancel is currently not working because the click handler is binded to the element when the page first loads, at this time the element .cancel does not exist since it has not been appended to the page yet. Since it does not exist on the page yet we cannot attach a click event directly to it.

The solution here is what is called Event Delegation, I highly suggest reading this article that explains it well, and helped clarify things for me when I was getting started. Learn jQuery: Event Delegation.

What you need to do is bind the click handler to an element that you know will be on the page and then Delegate that click event to the .cancel class selector. While I was testing the code on Codepen I noticed some unexpected things were happening, I refactored the code a bit to get it to work in the manner I think you were aiming for.

Hope this all helps,

Cheers.

$(document).ready(function () {
        // Instead of attaching to the `.tour` lets just bind to the `.book` button.
    $(".book").on("click", function () {
        var callNow = $(
            '<span class="details-call-now">Call 1-555-jquery-air to book this tour</span>'
        );
        var cancelBtn = $('<button class="cancel">Cancel</button>');

        // Since we want to show details again, just hide them instead of remove.
        $(this).siblings('.details').hide();

        // Insert the Call Now Text right before the Book Button
        $(this).before(callNow);

        // Hide The Book Button
        $(this).hide();

        // Insert the Cancel button after the Book Button.
        $(this).after(cancelBtn);
    });

    // We attach to an element that we know is on the page at the time of load,
    // First arg is events
    // Second arg is a selector
    $("body").on("click", ".cancel", function () {
        // Get Sibling .book button and show it again
        $(this).siblings('.book').show();

        // Remove the sibling call now element
        $(this)
            .siblings(".details-call-now")
            .remove();

        // Get Sibling details and show
        $(this).siblings('.details').show()

        // Remove the Cancel Button that was clicked.
        $(this).remove();
    });
});
Steven Parker
Steven Parker
243,173 Points

Here's a few observations and suggestions:

  • your current code is not attaching handlers to the buttons but to the entire "tour" list item
  • you need delegated handlers to work on buttons yet to be created
  • otherwise, you'd need to attach a handler to each new button
  • putting the cancel button inside a span adds unneded complexity
  • the booking message needs a different class to identify it from the original details
  • you forgot to revert the color in the "cancel" handler
  • you forgot to remove the "cancel" button itself

Here's what the code might look like with all the suggestions applied:

$(document).ready(function() {
  $("#tours").on("click", ".book", function() {
    var message = $('<span class="booking">Call 1-555-jquery-air to book this tour </span>');
    var message2 = $('<button class="cancel">Cancel</button>');
    var $li = $(this).parent();
    $li.append(message);
    $li.append(message2);
    $li.css("color", "red");
    $(this).remove();
  });

  $("#tours").on("click", ".cancel", function() {
    var message = $('<button class="book">Book Now</button>');
    var $li = $(this).parent();
    $li.append(message);
    $li.find(".booking").remove();
    $li.css("color", "");
    $(this).remove();
  });
});
Steven Parker
Steven Parker
243,173 Points

Vince's show/hide approach was actually my first thought, but I used the double delegation to stay closer to your original concept.

thank you both, i have learnt a few things from your comments. I am still getting to grips with Jquery.