views:

304

answers:

5

I am using jquery to add mulitple new "addTask" form elements to a "ul" on the page every time a link is clicked.

  $('span a').click(function(e){
    e.preventDefault();

     $('<li>\
     <ul>\
     <li class="sTitle"><input type="text" class="taskName"></li>\
     <li><input type="button" value="saveTask" class="saveTask button"></li>\
     </ul>\
     </l1>')
     .appendTo('#toDoList');
    saveTask();
});

These new nested ul elements all have an button with the same class "saveTask". I then have a function that allows you to save a task by clicking on an button with the class "saveTask".

// Save New Task Item 
function saveTask() {
    $('.saveTask').click(function() {
        $this = $(this);
        var thisParent = $this.parent().parent()

        // Get the value
        var task = thisParent.find('input.taskName').val();

        // Ajax Call 
        var data = {
            sTitle: task,
            iTaskListID: 29
        };

        $.post('http://localhost:8501/toDoLists/index.cfm/Tasks/save', 
            data, function(data) {

            var newTask = '<a>' + task + '</a>'
            thisParent.find('li.sTitle').html(newTask);
        });
        return false;
    });
}

This essentially allows the user to enter some text into a form input, hit save, and then the task gets saved into the database using ajax, and displayed on the page using jQuery.

This works fine when there is only one element on the page with the class "saveTask", but if I have more than 1 form element with the class "saveTask" it stops functioning correctly, as the variable "var task" shows as "undefined" rather than the actual value of the form input.

A: 

I think you're looking for the live event.

Also, your code is a little awkward, since the click event is only added when the saveTask() function is called. In fact, the saveTask() function, doesn't actually save anything, it just adds the click event to the elements with the .saveTask class.

Peter Di Cecco
Hi there, could you elaborate on how to make my code less akward? and how to use the live event to make the code function more efficiently? Would be appreciated...thanks
namtax
It's awkward because the function you have defined is only attached to the click event when saveTask() is called. Usually events are attached to elements in the document ready area, or simply `$(function(){ /* IN HERE */ });`. Then when clicked, your function will be called. Take a look at Michael Edwards' answer, it looks promising.
Peter Di Cecco
The live event is used to add a function to *future* elements. Since you dynamically create addTask elements, and you want these elements to have a click event that saves, it sounds like you need the live function.
Peter Di Cecco
Ok, I undestand what you are saying, the function should be attached to the elements already, not just when .saveTask button is clicked. Not heard of the live event, will have a look into this. Thanks
namtax
+3  A: 

Don't rely on the .parent() method. Use .closest('form') instead. So the following line:

var thisParent = $this.parent().parent()

should look something like this instead:

var thisParent = $this.closest('form');

EDIT: Based on the updated information you provided, it looks like when you're trying to register the click event handler it's failing out for some reason. Try this javascript instead as it will make use of the live event so that all the newly added items on the page will automatically have the click event autowired to them.:

$(function(){
    $('span a').click(function(e){
        e.preventDefault();

         $('<li>\
             <ul>\
             <li class="sTitle"><input type="text" class="taskName"></li>\
             <li><input type="button" value="saveTask" class="saveTask button"></li>\
             </ul>\
             </l1>')
             .appendTo('#toDoList');

    });

    $('.saveTask').live('click', function() {
        $this = $(this);
        var thisParent = $this.closest('ul');

        // Get the value
        var task = thisParent.find('input.taskName').val();

        // Ajax Call 
        var data = {
            sTitle: task,
            iTaskListID: 29
        };

        $.post('http://localhost:8501/toDoLists/index.cfm/Tasks/save', 
            data, function(data) {

            var newTask = '<a>' + task + '</a>'
            thisParent.find('li.sTitle').html(newTask);
        });
        return false;
    });
});
Agent_9191
Have tried this, but still having similar issues...i think its because there a mulitple elements with the saveTask class and they are all being submitted at the same time if I click on one
namtax
have you verified that `thisParent.find('input.taskName')` actually comes back defined? Based on the little bit of HTML you put in your comment above (which should really be edited into the question instead), it should come back with something, but it may be coming back `undefined` itself
Agent_9191
If I click once to add a new Task Item to the page ('input.taskName') comes back defined. If I click twice or more to add new Task Items to the page, ('input.taskName') only comes back defined for the first new Task Item added to the page, all the other task Items return nothing.
namtax
Take a look at the edit I made. It makes use of the `live` event handler so that the newly added items to the page are wired up successfully. It looked like when you were trying to register the click event after the 1st time it would fail silently for some reason. Does this help?
Agent_9191
That has worked like a beauty, made me happy...thanks very much mr agent!
namtax
A: 

What is your HTML structure?

It looks like your code can't find the input.taskName element.

Try setting thisParent to something like $this.closest('form'). (Depending on your HTML)

SLaks
A: 

You could try wrapping your click function in an each()

ie

function saveTask(){
$('.saveTask').each (function () {

     $this = $(this);

     $this.click(function() {

         var thisParent = $this.parent().parent()
                     //get the value
         var task = thisParent.find('input.taskName').val();
            // Ajax Call 
            var data = {
            sTitle: task,
            iTaskListID: 29
        };
        $.post('http://localhost:8501/toDoLists/index.cfm/Tasks/save', data, function(data){

             var newTask = '<a>' + task + '</a>'

            thisParent.find('li.sTitle').html(newTask);


        });
        return false;
      });
  })
}

I find this helps sometimes when you have issues with multiple elements having the same class

Addsy
How does that change *anything*?!
Shog9
Actually this is something I have found from experience that works. In fact I have a script open at the moment that was fixed in this exact way.I suspect it is because of the use of $(this).parent() in within the click handler. When you do $(".saveTask").click(...)presumably the reference to $(this) within the click handler contains all the matching elements and so ($this).parent() is not the same as when you do $(".saveTask").each(function() {$(this).click(...)}I understand why you think this shouldn't make any difference. I'm saying I've tried it and it does
Addsy
@Addsy: it doesn't though. The value of `this` in the click handler is a reference to the element that triggered the event - regardless of how many elements you had selected when you attached the event. That's why you have to wrap it in $() if you want to use jQuery functions on it - otherwise, you're just working with a raw DOM element. And... Look in the jQuery source, the definition for `bind()`: it's already doing the each() thing prior to attaching events... heck, it *has* to - elements and event handlers are 1-to-1 when you get down to the browser API. I think you're a bit confused...
Shog9
@Shog9 - Like I say, I see why this shouldn't make a difference, and it appears my reasoning for why it does make a difference is incorrect - it was just a guess, I hadn't tested it. All I'm saying is that I have had something similar to the problem described in the post and this is how I fixed it.
Addsy
This doesnt work either unfortunately, if there are multiple add new task forms on the page they all get submitted at the same time, when any of the forms are submitted
namtax
+1  A: 

First turn the save task into a function:

(function($){
  $.fn.saveTask= function(options){


   return this.each(function(){

     $this = $(this);
     $this.click(function(){

       var thisParent = $this.parent().parent()
                 //get the value
       var task = thisParent.find('input.taskName').val();
        // Ajax Call 
        var data = {
        sTitle: task,
        iTaskListID: 29
      };
      $.post('http://localhost:8501/toDoLists/index.cfm/Tasks/save', data, function(data){

        var newTask = '<a>' + task + '</a>'

        thisParent.find('li.sTitle').html(newTask);
      });
    });
  });
  return false;
})(jQuery)

When the app starts change the saveTask selector to this:

function saveTask(){
  $('.saveTask').saveTask();
}

Then on your add function:

function addTask(){
  $newTask = $("<div>Some Task stuff</div>");
  $newTask.saveTask();
}

This code is written very quickly and untested but essentially create a jQuery extension that handles for data submission then when ever a task is created apply the save task extension to it.

Michael Edwards
Getting an error here...$newTask.saveTask is not a function. Thanks
namtax
Sorry the end of the funciton diffinition has to end with })(jQuery)See the code change I have made
Michael Edwards
Still showing the error $newTask.saveTask is not a function unfortunately...Thanks
namtax