Reputation: 1004
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ä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
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:
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);
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:
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();
});
}
});
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();
});
}
});
Upvotes: 7
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