DannyThunder
DannyThunder

Reputation: 1004

Call function inside document.ready wont work

I want to be able to call a function from a link (this function will delete the corresponding row). If I place the function inside document ready it wont load upon call. But it works while outside. The function is delRow(id) located at the end of .

I tried making a 'copy' of "$("#participants").click(function() {" and calling it just like the "Lägg till deltagare" link (add user link).

How do I solve this in the best way? And Why cant you call functions inside document.ready and why wont my copy of the participants click.function work? (I dont have it in the code example, i named it like "$("#deleterow").click(function() and set the id of the button to deleterow)

URL: http://min.xn--spyp-toa.se/addrows.php (Some DNS servers have problem with this, run googles DNS instead! 8.8.8.8 )

<?php 
//Inkluderar fil för inläsning av databasinfo.
require("core.inc");       
?>

    <script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>

    <script type="text/javascript">

        $(document).ready(function () {

         //Denna kod ska hämta users i databasen till js-arrayen users
           var users = new Array();

            $.ajax({
                url: 'getuser.php',
                dataType: 'json',
                error: function(){alert("Kunde inte hämta data från databas");},
                success: function(data){
                    users = data;
                }
            });

        //Denna kod lägger till en ny rad
            var rowCount = 1; // Räknare som räknar hur många rader man lagt till
            var selectedUsers = new Array(); //Denna array innehåller användare man lagt till

            $("#participants").click(function() {

                var participantSelect = document.getElementById('participantSelect'); //Denna variabel håller värdet från selecten

                //En funktion som loopar fram användarna i databasen till <option> taggar
                function addOption(){

                    var option; //Denna variabel lagrar alla options som genereras i loopen

                    for (var i=0; i < users.length-1; i++)  {

                        var user = users[i].id;

                        if(jQuery.inArray(user, selectedUsers) > -1) {

                        }
                        else{
                           option += ('<option value="'+users[i].id+'">'+users[i].name+'</option>'); 
                        }
                    }
                    return option; //Skickar alla genererade <option>-taggar till append-scriptet
                }

                //Funktionen som uppdaterar <select>-satsen
                function updateSelect(){
                    $('#participantSelect').replaceWith('<select name="participantSelect" id="participantSelect">'+addOption()+'</select>');
                }

                //Function som matas in i apenden, den plockar ut namnet som hör till ID som kommer från selecten
                function name(){

                    //kör igenom arraeyn men avslutar när den hittar en träff
                    for(var i=0; users.length; i++){

                        if(users[i].id == participantSelect.value){ //Om användar ID är samma som ID från select

                            selectedUsers.push(users[i].id); //Denna lägger till valda användare i en array för filtrering av redan använda användare

                            //Denna funktion anropar koden som genererar om <select> och utelämnar redan valda användare
                            updateSelect();

                            return users[i].name; //Return i if-satsen för att avbryta vid träff.
                        } 
                    }
                }

                //Skriver in en input som är read-only med den valda användaren
                $('#container').append('<input type="text" name="participant_'+rowCount+'" id="'+participantSelect.value+'" value="'+name()+'" class="participant_1" readonly="readonly" /> <a href="#" name="participant_'+rowCount+'" id="'+rowCount+'" class="participant_1" onclick="bajs(this.name)">Ta bort deltagare</a><br>');

               rowCount++;                   
            });

            //Denna kod tar bort redan inmatad användare och lägger tillbaka denne i <select>
                function delRow(id){
                    alert(id);
                    alert("test");
                }
        });

    </script>

    <!-- Här väljer man vilka användare som deltagit, sedan trycker man på lägg till-knappen -->
    <select name="participantSelect" id="participantSelect">
                <?php

                $result = mysql_query("SELECT id, name FROM user");

                while ($db_field = mysql_fetch_assoc($result)) {
                    echo '<option value="'.$db_field['id'].'">'.$db_field['name'].'</option>';
                }

                mysql_free_result($result);

              ?>
    </select>

    <!-- Knapp för att lägga till ny rad inom container-diven -->
    <a href="#" id="participants" class="myButton">L&auml;gg till deltagare</a>

    <!-- Container diven hit vad alla användare kommer läggas till-->
    <h2>Deltagare:</h2>
    <div id="container" class="container">
   <!-- Här kommer allt läggas in -->
    </div>  

    <button name="participant_1" id="delete_participants" onclick="delRow()">Ta bort deltagare</button><br>

Upvotes: 0

Views: 4737

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074295

When you use old-style DOM0 handler hookup (onclick="..."), any functions you reference in that string must be globals. Your delRow function isn't a global, it's contained by the function you're passing into ready — which is a good thing, the global namespace is already too full.

Two choices for dealing with this:

1. Hook things up with jQuery

The best way (in my view) to deal with it is to hook up your button using jQuery from within your ready function. E.g.:

$("#delete_participants").click(delRow);

2. Export just that function

Alternatively, you could make a global that points to delRow by doing this within the ready function:

window.delRow = delRow;

That works because all global variables are properties of the window object in browsers, so by assigning a reference to delRow to the delRow property on window, you're explicitly making it available in the global namespace (without cluttering up the global namespace with any of your other functions). Then the onclick="..." handler will be able to see it. But I wouldn't advocate using globals if you can avoid it. It's a useful bridging technique, though, as you transition from the old style to the newer style.


Side note: You don't have to use the ready function, you can just put your script at the bottom of the page (just before or after the ending </body> tag) and do this:

(function() {
    // Your code here
})();

It will run slightly sooner, and still have access to all of the DOM elements on the page (ref | ref). But ready is mostly fine too.


Re your comment below:

I´ve given dynamic buttons the ID "delete_participants" and created the $("#delete_participants").click(delRow); but it aint getting called. It works for the static button tho.

That's because you can't have more than one element on a page with the same id. The id values must be unique.

If you're going to be adding and removing rows, and if you're using jQuery to hook things up, you have two additional choices:

A. Hook Them Up Individually

This is just a matter of calling click(function() { ... }) on the new button once you've created it, e.g.:

var tr = '<tr><td><input type="button" name="delete"></td></tr>';
tr.find('input').click(delRow);
tr.appendTo(/* ...presumably a table body or something...*/);

So for instance, here's a complete example:

HTML:

<table id="participants">
  <tfoot>
    <tr>
      <td colspan='2'>
        <input type="button" name="add" value="Add Participant">
      </td>
    </tr>
  </tfoot>
  <tbody>
    <tr>
      <td>
        Joe Bloggs
      </td>
      <td>
        <input type="button" name="delete" value="Delete">
      </td>
    </tr>
  </tbody>
</table>

JavaScript:

jQuery(function($) {

  $("#participants")
    .delegate('input[name="add"]', "click", addParticipant)
    .delegate('input[name="delete"]', "click", delParticipant);

  function addParticipant() {
    var td, tr, name;

    // Obviously you'd use something more modern than `prompt`
    name = prompt("Name for new participant: ", "");
    if (name) {
      // And this code is no thing of beauty, either, but you get the idea
      td = $('<td>').text(name);
      tr = $('<tr>');
      tr.append(td);
      tr.append('<td><input type="button" name="delete" value="Delete"></td>');
      tr.hide();
      $("#participants tbody").append(tr);
      tr.fadeIn();
      // Note we don't have to do anything to hook the new button;
      // delegation handles that
    }
  }

  function delParticipant() {
    var tr = $(this).closest('tr');
    tr.fadeOut(function() {
      tr.remove();
    });
  }

});

Live copy

B. Use Event Delegation

But in cases like this, another technique is frequently better: Event delegation. In event delegation, you hook the event ("click") on some container of all of the things that might receive that click, and then when the click happens check to see if the event has passed through any of the relevant elements (because most events, including "click", "bubble" from the element on which the click actually occurred up to its parent, grandparent, and other ancestors). jQuery makes delegation dead easy, using delegate:

$("selector_for_container").delegate("selector_for_element", "click", function() {
    // This will get called if the click matches the element selector
});

or, as of 1.7 or later, using one of the several variants of on:

$("selector_for_container").on("click", "selector_for_element", function() {
    // This will get called if the click matches the element selector
});

(Note that the order of arguments is slightly different.)

Complete example (using delegate, since a lot of people aren't on 1.7 yet):

HTML:

(Same as above)

JavaScript:

jQuery(function($) {

  $("#participants")
    .delegate('input[name="add"]', "click", addParticipant)
    .delegate('input[name="delete"]', "click", delParticipant);

  function addParticipant() {
    var td, tr, name;

    // Obviously you'd use something more modern than `prompt`
    name = prompt("Name for new participant: ", "");
    if (name) {
      // And this code is no thing of beauty, either, but you get the idea
      td = $('<td>').text(name);
      tr = $('<tr>');
      tr.append(td);
      tr.append('<td><input type="button" name="delete" value="Delete"></td>');
      tr.hide();
      $("#participants tbody").append(tr);
      tr.fadeIn();
      // Note we don't have to do anything to hook the new button;
      // delegation handles that
    }
  }

  function delParticipant() {
    var tr = $(this).closest('tr');
    tr.fadeOut(function() {
      tr.remove();
    });
  }

});

Live copy

Upvotes: 7

Luc Laverdure
Luc Laverdure

Reputation: 1480

"You've defined the function within a closure, so it's not accessible outside of that ready function"

http://forum.jquery.com/topic/call-functions-outside-document-ready

You should always put your functions outside the document ready closure for them to be accessible within the global scope.

Upvotes: 0

Related Questions