0

I'm trying to implement a simple jquery code, One function that will add new rows to a table:

function add_row(){
$('#giants_table tr:last').before('<tr><td><input type="text" name="giants_#" id="giants_#"><input type="button" id="removebtn" value="Remove row" onclick="remove_row()"</td></tr>')}

And the second function to remove that row:

function remove_row(){
$(this).parents('tr').remove()}

The first function works just fine, but it appears that in the second function the "this" selector is not set.

Any ideas?

shultz
  • 2,543
  • 3
  • 20
  • 23
  • Remove wrapping `''`, `this` is not a string, it's a pointer. – Johan Sep 24 '13 at 11:12
  • You've got jQuery - use that to register your event handlers instead of putting them inline and the answer should become obvious! Better yet, use a single delegated event handler on the entire table to handle the `remove_row` buttons. – Alnitak Sep 24 '13 at 11:13
  • @Alnitak How do you know that he's not referencing the functions from the jQuery event handlers? – Johan Sep 24 '13 at 11:14
  • @Johan he might be, but he's definitely referencing them inline. These days, that's just _wrong_... – Alnitak Sep 24 '13 at 11:15
  • @Alnitak, Why is it not recommended to call the function from inline? and why wont it refer to the proper element? – shultz Sep 24 '13 at 11:20
  • 1
    @shultz doing it inline - 1. doesn't pass `this` 2. doesn't always pass the `event` parameter 3. doesn't normalise the `event` object. – Alnitak Sep 24 '13 at 11:25
  • @Alnitak It's nice of you to summarize it in 3 simple reasons. It might be useful for others as well. – shultz Sep 24 '13 at 11:30
  • did you try passing onclick="myFunction(this)"? http://stackoverflow.com/questions/17060971/get-class-name-from-element-with-onclick – caramba Sep 24 '13 at 11:32
  • @caramba that'll work, but it's not the "right" answer. – Alnitak Sep 24 '13 at 11:33
  • 1
    p.s. don't give multiple buttons the same ID - give them a class instead! – Alnitak Sep 24 '13 at 11:34

3 Answers3

1

You can have this done using two ways

  1. Pass this in the function call.

    function add_row(){
        $('#giants_table tr:last').before('<tr><td><input type="text" name="giants_#" id="giants_#"><input type="button" id="removebtn" value="Remove row" onclick="remove_row(this)" /></td></tr>');
    }
    
    function remove_row(ele){
        $(ele).parents('tr').remove();
    }
    
  2. Bind the click, and use $(this)

    function add_row(){
        $('#giants_table tr:last').before('<tr><td><input type="text" name="giants_#" id="giants_#"><input type="button" id="removebtn" class="removebtn" value="Remove row" onclick="remove_row(this)"/></td></tr>');
        $('#giants_table tr td .removebtn').unbind('click').bind('click', function() {
              $(this).parents('tr').remove();
        });
    }
    

I would surely prefer to go for second option.

Ashis Kumar
  • 6,494
  • 2
  • 21
  • 36
1

When you register an event handler inline (i.e. within the HTML code) it doesn't set the this parameter - it'll be set to the global (window) object.

One option is to pass this as a parameter to remove_row, but you would be far better creating a one-off single delegated event handler, using jQuery:

$('#giants_table').on('click', 'button', remove_row);

You can then completely omit the onclick attribute in your HTML code. As this is a "delegated" handler, it'll automatically work on every row added to your table, even if they don't exist at the time the event is registered.

The principle advantages of using jQuery to register your events instead of doing them inline are:

  1. automatic setting of this
  2. guaranteed passing of the event object (unlike early MSIE versions)
  3. normalisation of the event object's properties to remove browser differences
Alnitak
  • 334,560
  • 70
  • 407
  • 495
0

You can delegate the click handler (probably the better solution), or simply assign it when you're creating the row. Examples below (using DOM Element creation, instead of HTML markup, because it's a better practice).

// delegate the function once in your DOM.ready
$('#giants_table tr').on('click', 'input[type="button"][value="Remove Row"]', function (e) {
    $(this).parents('tr').remove(); // this will point to the input
});

// or assign it when you create the row
function add_row(){
    var input = $('<input />'), //create new DOM elements
        row = $('<tr />'),
        cell = $('<td />'),
        rowCount = $('#giants_table tr').length;
    $('#giants_table tr:last').before(tr.append(td.append(input.clone().attr({
        "id": "giants_" + rowCount,
        "name": "giants_" + rowCount,
        "type": "text"
    })).append(input.clone().attr({
        "id": "removeRow" + rowCount,
        "type": "button",
        "value": "Remove Row"
    }).click(function (e) {
        $(this).parents('tr').remove(); // this now points to the input
    }))));
}
pete
  • 24,141
  • 4
  • 37
  • 51
  • there's no need for any of those `.clone()` calls as you're re-creating new instances of the required elements every time the function is invoked. Note also that MSIE doesn't permit the `type` attribute of an `input` to be changed after it has been created. – Alnitak Sep 24 '13 at 11:54