Tortooga
Tortooga

Reputation: 215

Use jQuery button to toggle back and forth between text and edit-form

I am trying to implement an edit feature, which allows me to toggle back and forth between a text element and a form element. The current functionality of the attached code is correct, except that the edit button becomes non-functional when it re-appears after clicking "cancel". I used event delegation to get the created "cancel" button to function, but now I need to somehow circle it back to the top.

$(document).ready(function() {
  $("#edit").click(function() {
    var fruit = $("p").html();
    var editbutton = $("#button").html();
    $("#button").html("<input id='submit' type='button' value='submit' /><input id='cancel' type='button' value='cancel' />");
    $("p").html("<input type='text' value='" + fruit + "' />");

    $("#button").on('click', '#cancel', function() {
      $("#button").html(editbutton);
      $("p").html(fruit);
    });
  });

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<p>Apple</p>
<span id='button'><input id='edit' type='button' value=edit /></span>

Upvotes: 2

Views: 2091

Answers (4)

A H Bensiali
A H Bensiali

Reputation: 875

Please use the jquery function .delegate

This has saved my skin on several occations.

$(document).delegate('.button', 'click', function(){ 
     //do what you need here
});

How is this function different from the others, not different at all except how it binds functionality to elements.

The above just says:

1) on document element bind the following

2) Every time you find .button or an element with class of button is created inside this document

  • attach a click event handler &
  • run the callback.

Even if you do this

 $('.container_element).append("<span class='button'><input id='edit' type='button' value=edit /></span>");

The span with class="button" will be attached with the click event in the delegate.

Happy coding

p.s. use class instead of id. id should be used as unique elements on the page however classes on element allows you more flexibility.

Upvotes: 0

Terry
Terry

Reputation: 66228

Even though using $("#button").on('click', '#edit', function() { } is a reasonable, quick and easy fix, it is not necessarily the best solution IMHO. The reason is that you are binding event handlers within event handlers. Better solutions could be (1) creating all the necessary action buttons and toggle their visibility based on the state of the content (static, or being edited); or (2) creating and destroying buttons on the fly (not as optimal due to extensive DOM manipulation).

My code might appear to be over complicated, but it is modularized—you can apply it to any .editable and .controls pair, and it stores the toggled states in the jQuery .data() object.

$(function() {
  
  // Assign value
  $('input.editable').val($('input.editable').prev('.editable').text());
  
  // Bind click event
  $('.controls').on('click', 'button', function() {
    var status = $(this).closest('.controls').data('status');
    if(status && status === 'static') {
      // Toggle controls
      $(this)
      .closest('.controls')
        .data('status', 'edit')
        .end()
      .siblings('button.'+status)
        .show()
        .end()
      .hide();
      
      // Toggle inputs
      $(this)
      .closest('.controls')
      .prevAll('p.editable')
        .hide()
        .end()
      .prevAll('input.editable')
        .show();
    } else {
      // Toggle controls
      $(this)
      .closest('.controls')
        .data('status', 'static')
        .end()
      .siblings('button.'+status)
        .show()
        .end()
      .hide();
      
      // Toggle inputs
      $(this)
      .closest('.controls')
      .siblings('p.editable')
        .show()
        .end()
      .siblings('input.editable')
        .hide();
    }
    
    // Update text if submit button is pressed
    if($(this).data('action') === 'submit') {
      $(this)
      .closest('.controls')
      .siblings('p.editable')
      .text($(this).closest('.controls').siblings('input.editable').val());
    }
  });
});
input.editable {
  display: none;
}
.controls button {
  display: inline-block;
}
  .controls button.static {
    display: none;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<p class="editable">Apple</p>
<input class="editable" type="text" />
<div class="controls" data-status="static">
  <button type="button" class="edit" data-action="edit">Edit</button>
  <button type="button" class="static" data-action="submit">Submit</button>
  <button type="button" class="static" data-action="cancel">Cancel</button>
</div>

Upvotes: 1

Henrique Barcelos
Henrique Barcelos

Reputation: 7900

You need to use event delegation in the first event handler too:

$("#button").on('click', '#edit', function() {
   // ...
});

Upvotes: 3

Hayden Schiff
Hayden Schiff

Reputation: 3330

The problem is that you are destroying the edit button when you create the cancel button, and as such, the event listener is not preserved when you recreate it. One solution to this is to always create the edit button dynamically, and use event delegation to attach a listener to it (like you already have for the cancel button).

Alternatively, instead of destroying it when you show the submit/cancel buttons, you could simply hide it, and unhide it when you want it to be visible again.

Upvotes: 2

Related Questions