Yelotofu

Yelotofu ~ “In building standards compliant sites we are creating a better Web for the future.”

jQuery Inline Edit tutorial

Tags: , , , , ,

A friend recently asked me to review his edit-in-place code which turned out to be a modification of the one found at http://docs.jquery.com/Tutorials:Edit_in_Place_with_Ajax. Reading the tutorial on that page I asked myself how I would do this differently? Defining a global setClickable() function and then calling $('#editInPlace").click() is totally uncool, essentially limiting yourself to one edit-in-place area per page.

Since the concept of edit-in-place is so simple and the implementation should be likewise I want to try and tackle this as a tutorial by going through concepts and teaching to build from scratch.

The concept

There's a piece of text on a page, e.g. a heading or paragraph, looking plain and un-interesting. When you hover over it however a visual highlight, usually pale yellow, indicates that it's something special - an editable region. You then click on the text and it magically transforms into an editable box with save and cancel buttons tagged at the end. On clicking either save or cancel the editable box transforms back into its original form with text updated if saved.

The build

What we want to do is put these concepts into code whilst building something that's re-usable. A plugin will do the trick! Let's call our plugin inlineEdit.

We are only 3 steps away from achieving our goal! Follow along...

Step 1: Getting the basics right

The basic functionality consists of a static text element (span, div etc...) that can transforms into an input element on click. And on hover the text element will highlight to indicate special interactions.

Here's our first pass: Basic Interaction

Code explanation: Our plugin accepts a CSS class name defined in a stylesheet which is used to toggle the text element's classname - a class defining a background-color typically #ffC is sufficient for this. On clicking we replace the entire contents of the original text element with an input element and pass the original content into the input as its value. For now changes are saved when the input loses focus.

Step 2: Save, Cancel & Callbacks

As you may notice above we saved on blur and there is no way of canceling changes. Let's improve the interaction by adding a save button. This is important as you'd want to do something with the changes or allow a user to manually cancel. Every application does something different at the saving stage so we'll simply define a save callback and let the implementer decide what to do on save, be it Ajax post or what not. The save action is also cancelable by returning false inside the callback.

Here is our second pass: Save, cancel and callbacks

Step 3: Finishing Off

Our plugin is nearly there. Just a few nice finishing touches remaining. We want to give the user ability to change the button label and pass an initial value. Also to make the plugin more re-usable we want to move the core plugin code out to a separate file and reference it instead. To set the stage for further improvements in the future we also split the code into two distinct clauses.

Final result: Live Demo and source code is here

Usage

Now to use this brand spanking new plugin we simply apply it to any DOM element like so:

HTML

<span class="editable">Hello World!</span>

JavaScript - Basic

$(function() {
  $('.editable').inlineEdit();
});

JavaScript - Customised button label with save callback

$(function() {
  $('.editable').inlineEdit({
    buttonText: 'Add',
    save: function(e, data) {
      return confirm('Change name to '+ data.value +'?');
    }
  });
});

Feedback welcome - if you happen to improve upon this code base please share-a-like!

Enjoy!

Update 26-Aug-09: Added placeholder for cases when text is empty; changed label option to buttonText to minimise confusion.

Update 18-Oct-09 Fixed bug in setting initial value via script.

Update 17-Jan-10 Moved code to http://github.com/caphun/jquery.inlineedit. I will continue to improve this plugin from there. Feel free to fork it!

Similar Posts

48 Comments

  1. By Javier at

    I usually use Jeditable for this kind of things. I find it quite useful.

  2. By caphun at

    @javier – Jeditable looks very good! Thanks for the link!

  3. By dalin at

    I’m still getting a handle on jQuery syntax. Why
    $(function() { …
    and not
    $(document).ready(function() {…
    ?

  4. By caphun at

    @dalin – One’s a short form of the other, I’m just being lazy. ;)

  5. By Bram at

    Hi CaPhun, I really like your inlineEdit plugin ;-) . It’s much smaller and easier to use than jeditable! I do have one little problem tough, it’s when the content to be edited is empty or cleared by the user. I’d like to have an alternative text which is not used in the input field when clicked. Although I think it can be solved with css, do you have a suggestion how to do this in inlineEdit?
    Thanks, Bram

  6. By caphun at

    @bram – glad you like the plugin. :)

    Having a default text label is a good idea. There are several alternatives. If the editable region is blank we could replace it with a label element that disappears on click or make the input box sticky if no content is present. The latter being one less click.

  7. By Bram at

    I’ve added the following lines near the bottom of the script:

    if (self.text().length == 0) {
    self.text(options.emptylabel);
    }

    And added an extra (empty) default option on top. It only sets the ‘emptylabel’ when the text is empty initially and doesn’t cover the case when the text is cleared. It would be nice to add a css class whenever the ‘emptylabel’ is used, so that it can be styled accordingly.
    Cheers,

  8. By caphun at

    @bram – I just added a placeholder option to deal with your situation. Now if the editable region is empty initially or made empty a placeholder will be added. The placeholder defaults to “Click to edit” but you could change it to something else via the placeholder option. There is also a class tagged onto this generated placeholder. Source code and demo updated.

  9. By Bram at

    Thanks a million! I’ll checkout your jquery-ui rewrite later.

  10. By calin at

    how can be this combine with php to use a database?

  11. By caphun at

    @calin: use the save callback. Here’s an example:

    http://yelotofu.com/labs/jquery/snippets/inlineEdit/save/demo.html

  12. By calin at

    do you have an example of the php file?

  13. By caphun at

    sure:

    http://yelotofu.com/labs/jquery/snippets/inlineEdit/save/backend.php.txt

    I’m using sessions to generate a unique experience – not recommended for production. You should replace with db read/write.

  14. By calin at

    I played around with it but I want to make a change,
    by saving a number in that field
    if it starts with 1234 and I want to enter 4000 but I type by mistake 40a0 I receive an alert and then the field becomes again 1234 and does not remain open for input; can you give me a solution?

  15. By caphun at

    @calin: I think that’s by design. To save your change you will have to click on the “save” button. What alert are you getting? Could you post up a demo?

  16. By calin at

    maybe I didn’t explain very well
    I want to put in js a check for the input field to accept just numbers;
    function check_edit(myval) {
    var tn = myval;
    var tnPat = /^(0|([1-9](\d{1,4})?))(\.(\d{1,2}))?$/;
    var tnArray = tn.match(tnPat);
    if (tnArray == null) {
    alert(”the value is not a number !\n(99999.99)”);
    return false;
    }
    }

    end then I have
    return check_edit(data.value);

    If I enter by mistake a letter, it shows the alert, but then the input field because didn’t passed the check receives the initial value; I want to say that the field does not remain open for input, I must click again on the span to edit it again, and re-enter the value

  17. By caphun at

    right, I see what you are saying. As return false is equivalent to moving focus away from the input it will cancel the edit. I will give this some thought. Maybe it’s more intuitive for return false to simply cancel the save operation and maintain the state of the input box? That makes sense actually.

  18. By calin at

    yes it makes sense;
    to complicate a little bit there are 2 cancels:
    - if you need a confirm and you abort the change, then you send a cancel, after that the input disappear
    - if you check the field for a desired value or type, and you receive an alert then you receive another cancel, which keeps the input

  19. By caphun at

    @calin: Actually I would argue that it’s just one. Even with the confirm dialog it should just go back to the input box if false is returned.

  20. By James at

    Heya,

    Great plugin, I have a question tho – I want to use the jquery live event on it (http://docs.jquery.com/Events/live) so I can add elements e.g. list elements with editable fields as at the moment only the original elements are editable.

    I have tried replacing instances of bind with live however I just keep breaking it whatever I try. Any advice as to what I can do?

    Thanks

  21. By caphun at

    @james: Thanks :) Could I see an example of what you’re trying to do?

  22. By James at

    This code is what i’m doing, just cut down somewhat. Basically select an ‘example page’ from the drop down list and it will add a list item to the test list. I have used the jquery .live so I can delete and move the added list items, but I can’t seem to do this with the inline edit.

    Example Page 1
    Example Page 2

    Choose Page
    Example Page 3
    Example Page 4

    function buildString(elementID, count){
    var li_string = ‘

       ‘ + elementID + ‘

    function sortable(){
    $(”#test-list”).sortable({
    handle : ‘.handle’,
    update : function () {
    var order = $(’#test-list’).sortable(’serialize’);
    counter = order.size();
    $(”#info”).load(”process-sortable.php?”+order);
    }
    });

    $(’#test-list li img.delete’).live(”click”,function(){
    // Delete element
    });

    $(’.editable’).inlineEdit({
    buttonText: ‘Add’,
    save: function(e, data) {
    return confirm(’Change name to ‘+ data.value +’?');
    }
    });

    $(’#menu_select option.page’).click(function(){
    $(this).remove();
    var counter = $(’#test-list li’).size() + 1;
    var select_val = this.value;
    $(buildString(select_val, counter)).appendTo(’#test-list’).hide().css(’opacity’, 0).slideDown(’slow’, function(){
    $(this).fadeTo(”slow”, 1);
    });
    i++;
    });
    }
    sortable();

    Thanks
    James

  23. By James at

    Ooops the code got formatted weird, I’ll try putting it in pre tags

    Example Page 1
    Example Page 2

    Choose Page
    Example Page 3
    Example Page 4

    function buildString(elementID, count){
    var li_string = ‘

       ‘ + elementID + ‘

    function sortable(){
    $(”#test-list”).sortable({
    handle : ‘.handle’,
    update : function () {
    var order = $(’#test-list’).sortable(’serialize’);
    counter = order.size();
    $(”#info”).load(”process-sortable.php?”+order);
    }
    });

    $(’#test-list li img.delete’).live(”click”,function(){
    // Delete element
    });

    $(’.editable’).inlineEdit({
    buttonText: ‘Add’,
    save: function(e, data) {
    return confirm(’Change name to ‘+ data.value +’?');
    }
    });

    $(’#menu_select option.page’).click(function(){
    $(this).remove();
    var counter = $(’#test-list li’).size() + 1;
    var select_val = this.value;
    $(buildString(select_val, counter)).appendTo(’#test-list’).hide().css(’opacity’, 0).slideDown(’slow’, function(){
    $(this).fadeTo(”slow”, 1);
    });
    i++;
    });
    }
    sortable();

  24. By James at

    Obviously not working, I hope you get the idea, I can email it if you want?

  25. By caphun at

    @james: I should have mentioned jsbin.com eariler. Could you put it there? It would be easier for me to see what the code is doing.

  26. By James Lang at

    http://jsbin.com/ukiwa
    http://jsbin.com/ukiwa/edit

  27. By caphun at

    Here’s a possible solution:

    http://jsbin.com/ohuzo/edit

  28. By James at

    That works perfectly, thanks so much.

    Sorry to bother you again, but I have another page with a similar issue i.e. html created after dom initiated and the inline edit not working. I tried what you did to get the last one to work but it doesnt seem to have sorted it.

    Any chance you could have a look? I am so sorry to trouble you again!

    http://jsbin.com/ifumi/edit

  29. By caphun at

    All I did was move the inlineEdit call into the blur callback:

    http://jsbin.com/ufaju/edit

  30. By indialike at

    Very nice and useful tutorials to web designers,
    Thanks for posting.

  31. By tom at

    Heloo caphun, i’ve tried your plugin and its work very well, but there is a problem, i wants to modified your plugin, i want to change the input style into a text area, but i got some error, i dont get a value of the new data

    else if ($this.is(self[0].tagName) || $this.hasClass(’inlineEditText-placeholder’)) {
    self
    .html(”+ self.value() +’ ‘+ options.buttonText +”)
    .find(’textarea’)

    if i run this, there will appears a confirm box

    ” Change name to undefined? ”

    can you fix this ??? Thanks before (^_^)

  32. By caphun at

    @tom: thanks :)

    Actually I have a version of this plugin that supports textarea. It might be slightly buggy but will put it up on github.com very soon.

  33. By caphun at

    As promised I have moved this code to github.com

    http://github.com/caphun/jquery.inlineedit

    Feel free to fork it!

  34. By tom at

    Wow, thanks a lot for your help, its work properly now. Many thanks from me.. (^_^)

  35. By kalyan at

    Caphun,Great plugin and thank you for sharing. Your plugin is working for me except for one scenario where I was adding new row to the table through jquery. Inline edit is only working for old rows but not the new rows that were added through jquery. Can you please help me on this..
    I placed my code here..

    http://jsbin.com/anumi3/edit

  36. By kalyan at

    Caphun,You already gave the solution for this.. http://jsbin.com/ohuzo/edit
    It worked for me too..Thank you

  37. By kalyan at

    Caphun,Would I be able to capture/know the currently edited and saved id or name of the or or . at the function $(’.editable’).inlineEdit /
    Thanks

  38. By caphun at

    Hello Kalyan. Thanks for the compliments. Yes, it’s possible to know the currently edited element within the save callback by simply using “this”. to get the id of the element use this[0].id, this[0] is the span element.

    As for your other questions here’s a code snippet to demonstrate it working via re-binding:

    http://jsbin.com/okese3/edit

    Since jQuery 1.4 supports focus/blur through the focus(in|out) events I will switch to the .live() method soon.

  39. By amarushakur at

    I really liked your example but i will like to apply some validation to the editable contents before they are saved. i want both a client side and php server side validation. if the validation fails, the edited contents should not be saved.

  40. By amarushakur at

    my page uses a php script to display a table of records from a databse. i need help on inline editing such that when a user clicks on a row, the fields become editable and the edited contents are validated both at the client side and php server side before it is finally saved. Please help me. its my final year project. the server side script is jquery

  41. By caphun at

    @amarushakur: It is certainly possible to validate the contents before submission. Take a look at my save example within demos here:

    http://github.com/caphun/jquery.inlineedit

    You can place client-side validation inside the save callback function.

    Hope that helps.

  42. By Chris at

    Very nice. Is it possible to send a hidden ID field along with the updated text using ajax so that the correct database field is updated. I’m sure this is trivial but I am not getting it.

  43. By caphun at

    @chris – yes just add another value to the ajax data object when saving, e.g.


    $.ajax({
    data: {
    'action': 'save',
    'value': data.value,
    'id': 'my-hidden-id-value'
    }
    });

  44. By Chris at

    I guess while I’m at it, I need to be able to do this on a single row at a time in a table. The idea is to be able to take a numeric text field and modify it while submitting the results to the database with ajax. I can do this (sort of) but the id is returning the same for each row. It is just using the first id from the first row.

  45. By Chris at

    This is what I have done in the js.

    var id = document.getElementById(’holidayID’);
    var html = $.ajax({
    data: { ‘action’: ’save’, ‘value’: data.value, ‘id’: id.value }

  46. By caphun at

    @chris – that won’t work since the ID attribute is supposed to be unique. The entire HTML page should only have one holidayID. A better approach would be to look-up the closest container and grab the corresponding. If you put up a very minimal example script on http://jsbin.com I could give more specific advise.

  47. By jeffery at

    hai, this seems to be a very good replacement for jeditable plugin…..but i need to apply validations for the editable fields…..is it posiible to do this here….

    cuz am not just getting it right to work with validations and edit together…

    can just show any sample codes of how to implement it…. it would be a great help….

  48. By Michael at

    Hi,

    First I would like to thank you for a great script.

    I am having some difficulties with certain characters.
    You should be able to replicate the bug if you go to your demo (http://yelotofu.com/labs/jquery/snippets/inlineEdit/demo_final.html) and type a smiley, for example :) , and then click on save.
    When I do that I get:

    uncaught exception: Syntax error, unrecognized expression: :)

    Any ideas?

    Thanks!

Leave a Reply

* marks a required field.



(this will not be published)