Uncaught ReferenceError: Function is not defined using external js file - javascript

I have a table of rows with contact details and a call button in every row, which when clicked should call the customer.
I am using onclick on call the function defined in external js file (I know not the best practice and potentially due to outside scope, but I am passing the phone number as well)
I am getting error Uncaught Referenceerror: function is not defined
https://jsfiddle.net/e1z25y3w/3/
<table>
<th>
<td>#</td>
<td>Name</td>
<td>City</td>
<td>Phone Number</td>
<td>Call</td>
</th>
<tr>
<td>1</td>
<td>John</td>
<td>Melbourne</td>
<td>+61123456789</td>
<td><a role="button" onclick="callPhone('+61123456789')">Call</a></td>
</tr>
<tr>
<td>2</td>
<td>Tanya</td>
<td>Sydney</td>
<td>+61987654321</td>
<td><a role="button" onclick="callPhone('+61987654321')">Call</a></td>
</tr>
</table>
Jquery 3.4.1 included at the bottom of the page
javascript file also included after jquery
$(function () {
//const phoneNumberInput = document.getElementById("phone-number");
function callPhone(phonenumber) {
alert(here);
log("calling phone number " + phonenumber.value);
//e.preventDefault();
phoneNumberInput = phonenumber;
makeOutgoingCall();
}
});
What is the best way to execute this?

because the "callPhone" function isn't in the global scope, so when you to try call it, will give "callPhone is not defined".
1- first solution is to write it on the global scope.
function callPhone(phonenumber) {
console.log('running')
}
https://jsfiddle.net/mena234/rakeopg2/9
2- The second solution is to store it in a global variable.
let referanceStore = null
$(function () {
//const phoneNumberInput = document.getElementById("phone-number");
function callPhone(phonenumber) {
// any code here...
console.log('running')
}
referanceStore = callPhone
});
and use referancecStore to call your function
ex:
referanceStore('+61987654321')
https://jsfiddle.net/mena234/z391euhm/7
3- Third solution is to use the javascript click event and dataset instead.
https://jsfiddle.net/mena234/z391euhm/22/

That is one of the reasons why you shouldn't use an inline event handler (onclick="callPhone('+61123456789')")
Your inline event handler can't find the function callPhone(phonenumber) because it is defined within the anonymous callback function passed to your $( ... ) so it is only visible in it.
So the first idea of how to solve it would be to make the callPhone globally visible. This however is a bad idea as it pollutes the global namespace.
You instead should get rid of your inline event handlers and attach the event handler within the scope where callPhone is defined, using e.g. event delegation and data attributes:
$(function() {
function callPhone(phonenumber) {
console.log("calling phone number " + phonenumber);
}
// use event delegation to listen a click on elements with the data-action attribute beeing callPhone
$(document).on('click', '[data-action="callPhone"]', function(evt) {
// get the contents of the data attribute phone-number
// and pass it to the callPhone function
callPhone($(evt.target).data().phoneNumber)
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr>
<td>#</td>
<td>Name</td>
<td>City</td>
<td>Phone Number</td>
<td>Call</td>
</tr>
<tr>
<td>1</td>
<td>John</td>
<td>Melbourne</td>
<td>+61123456789</td>
<td><a role="button" data-phone-number="+61123456789" data-action="callPhone">Call</a></td>
</tr>
<tr>
<td>2</td>
<td>Tanya</td>
<td>Sydney</td>
<td>+61987654321</td>
<td><a role="button" data-phone-number="+61987654321" data-action="callPhone">Call</a></td>
</tr>
</table>

Related

How do I pass the jQuery selection object into a function being called by the selection?

New to jQuery and I don't know how else to say it.
When the function below is within the jquery selection, it works fine. When I try to break out the function I'm not sure what object/variable needs to be passed into the function in order to make the function work.
//function included in selection works great
$('#id1 tbody tr').find('td:eq(2)').text(
function(i,t){
return Math.round((parseInt(t,10) / 60)*100)/100;
});
//function broken out returns NaN in HTML
$('#id2 tbody tr').find('td:eq(2)').text(minutesToHours());
function minutesToHours(i,t){
return Math.round((parseInt(t,10) / 60)*100)/100;
}
It's the same function but returns 'NaN' in the HTML. I know I need to pass the object into the function minutesToHours(), but nothing I've tried works-- this or $(this). minutesToHours('whatGoes','here')
JSFiddle
I'm assuming this has something to do with context but I can't figure it out.
Example HTML
<table id="id1">
<tbody>
<tr>
<td>John</td>
<td>2</td>
<td>800</td>
<td>4</td>
</tr>
<tr>
<td>Sally</td>
<td>5</td>
<td>610</td>
<td>9</td>
</tr>
<tr>
<td>Bob</td>
<td>7</td>
<td>249</td>
<td>3</td>
</tr>
</tbody>
</table>
<table id="id2" style="margin-top:1em;">
<tbody>
<tr>
<td>John</td>
<td>2</td>
<td>800</td>
<td>4</td>
</tr>
<tr>
<td>Sally</td>
<td>5</td>
<td>610</td>
<td>9</td>
</tr>
<tr>
<td>Bob</td>
<td>7</td>
<td>249</td>
<td>3</td>
</tr>
</tbody>
</table>
Result
Included function
John 2 13.33 4
Sally 5 10.17 9
Bob 7 4.15 3
Broken out function
John 2 NaN 4
Sally 5 NaN 9
Bob 7 NaN 3
Thanks for your help!
To further explain Adeno's answer in the comments:
From the docs:
.text( function )
function
Type: Function( Integer index, String text ) => String
A function returning the text content to set. Receives the index position of the element in the set and the old text value as arguments.
This means that when you call the below, t is filled with the old text value:
$('#id1 tbody tr').find('td:eq(2)').text( function(i,t){
return Math.round((parseInt(t,10) / 60)*100)/100;
});
However, when you use minutesToHours() in the below, you are actually calling minutesToHours() with no parameters and passing the returned value into .text() Since you didnt pass any variables to the function,t is undefined and the function produces NaN:
$('#id2 tbody tr').find('td:eq(2)').text(minutesToHours());
function minutesToHours(i,t){
return Math.round((parseInt(t,10) / 60)*100)/100;
}
And a different signature for .text() is used:
.text( text )
text
Type: String or Number or Boolean
The text to set as the content of each matched element. When Number or Boolean is supplied, it will be converted to a String representation.
With Adeno's suggestion, you drop the () from minutesToHours() like the below. Now you are passing a reference to the minutesToHours function into .text() not calling the function . When, .text() sees the parameter is a function, it will call the function in that context, passing in the two values (index, text ) and all is right with the world:
$('#id2 tbody tr').find('td:eq(2)').text(minutesToHours);
function minutesToHours(i,t){
return Math.round((parseInt(t,10) / 60)*100)/100;
}

jQuery Click Event for Dynamically Created Links Not Working

I am displaying a list of users in a table. Then, I have a button to allow a user to view more details about a specific user. However, I am having a hard time getting the ID for the user that is clicked on. I am not sure why it's not working. What am I doing wrong? I tried several options, and none of them work.
Partial code of how data the links are generated in my view.
if (Model != null) {
if (Model.Count() > 0) {
foreach (var item in Model) {
<tr>
<td><div class="centerText">#item.UserID</div></td>
<td><div class="centerText">#item.FirstName</div></td>
<td><div class="centerText">#item.LastName</div></td>
<td><div class="centerText">Details</div></td>
</tr>
}
}
}
My jQuery function
$(function () {
$('.Detail').on('click', function (event) {
var dt = $(this).attr('id');
console.log('dt');
});
});
I also tried it this way:
if (Model != null) {
if (Model.Count() > 0) {
foreach (var item in Model) {
<tr>
<td><div class="centerText">#item.UserID</div></td>
<td><div class="centerText">#item.FirstName</div></td>
<td><div class="centerText">#item.LastName</div></td>
<td><div class="centerText">Details</div></td>
</tr>
}
}
}
Here is the Javascript function that I created. It kept giving
function test(e){
console.log(e);
};
I get this error:
0x800a1391 - JavaScript runtime error: 'test' is undefined
updated on 11/21/15 #7:53 AM EST
I removed the for loop and created a single cell with 1 click button. The click event is not registered. I tried it with 'on', 'live', and 'delegate' with no success.
<div class="table table-responsive" style="width:100%;height:100%;">
<table class="table table-bordered table-condensed table-striped">
<thead>
<tr>
<th><div class="centerText">Date Created</div></th>
<th colspan="2"></th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="6"><div class="centerText">Detail</div></td>
</tr>
</tbody>
</table>
</div>
#section Scripts {
<script type="text/javascript">
$('.table tbody tr td div').delegate('.Detail','click', function ()
{
console.log('you click me');
});
</script>
}
You are trying to get the attribute id which doesn't exist. Use below to get data-id
$(function () {
$('.Detail').on('click', function (event) {
var dt = $(this).data('id');
console.log(dt);
});
you can also use
var dt = $(this).attr("data-id");
Second one (remove var):
function test(e){
console.log(e);
};
Partial code of how data the links are generated in my view. change id instead of data-id.
if (Model != null) {
if (Model.Count() > 0) {
foreach (var item in Model) {
<tr>
<td><div class="centerText">#item.UserID</div></td>
<td><div class="centerText">#item.FirstName</div></td>
<td><div class="centerText">#item.LastName</div></td>
<td><div class="centerText">Details</div></td>
</tr>
}
}
}
My jQuery function
$(function () {
$('.Detail').on('click', function (event) {
var dt = $(this).attr('id');
console.log('dt');
});
});
I also tried it this way:
if (Model != null) {
if (Model.Count() > 0) {
foreach (var item in Model) {
<tr>
<td><div class="centerText">#item.UserID</div></td>
<td><div class="centerText">#item.FirstName</div></td>
<td><div class="centerText">#item.LastName</div></td>
<td><div class="centerText">Details</div></td>
</tr>
}
}
}
Here is the Javascript function that I created. It kept giving. (Remove var before function).
function test(e){
console.log(e);
};
Here is how I reproduced your problem with JavaScript runtime error: 'test' is undefined.
Scenario
I guess that you've defined the function test probably in your jQuery DOM ready function $(function(){...}.
If so, the function test is undefined because by the time the DOM is loading, AND the event handlers on DOM elements are being registered (in your case the onclick in the link), the function test is not yet known to the document, and therefore undefined.
Solution
Try to move your test function's declaration outside of jQuery's DOM ready function. That is,
$(function(){
//code that has to run once the DOM is ready
});
function test(e){
console.log(e);
}
DEMO
I found what was causing the problem. I had a modal popup within the form. I was looking to add a textarea, so I added this code '
<textarea name="reasonForArchiveText" id="reasonForArchiveText" />
That's the code that was preventing me from getting the click event. As I was playing around with the code, I commented out the modal popup, and things started to work. Then, I commented section of the modal until I finally found the culprit. The minute that I commented it out, the code works.

jQuery - Can not select a dynamic created element

Here is my HTML structure:
<table id="items" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<td> </td>
<td>Name</td>
<td>Description</td>
<td>Location</td>
</tr>
<tr>
<td>
<button type="button" class="btn btn-default btn-lg">
<span class="glyphicon glyphicon-plus"></span>
</button>
</td>
<td><input type="text" id="addName"></td>
<td><input type="text" id="addDescription"></td>
<td></td>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
</tfoot>
</table>
And here is my jQuery code:
function getTable(){
var args = new Object();
args.op = "getTable";
$.post("./service.php", args , function(data)
{
var dataObj = JSON.parse(data);
for(var i = 0; i<dataObj.length; i++){
$('table#items tbody').append("<tr class=\"items_tr\" id=\"item"+dataObj[i].sn+"\"><td>"+dataObj[i].sn+"</td><td>"+dataObj[i].name+"</td><td>"+dataObj[i].description+"</td><td>"+dataObj[i].belonged+"</td></tr>");
}
});
}
getTable();
$('tbody').children("tr").hide();
$("tbody tr").hide();
$("tr.items_tr").hide();
The Last 3 statements do not work at all.
Cannot select created <tr>s.
Why and how can I select them?
$("tr").hide();
This one hides only <tr>s inside <thead>, things that have already been created.
more questions - How about .each() function?
I want to bind a click event on each td tag, and I failed again..
How to iterate all new added <td>s?
For the last three statements to work, the elements have to exist. You are executing those methods before the Ajax response was received. Move the calls inside the $.get callback, after you added the elements (i.e. after the loop).
Kind of related (because they explain how async stuff works):
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
How do I return the response from an asynchronous call?
If you are trying to hide these elements on an event, add them to an event handler for example:
$(document).ready( function () {
getTable();
$(element).on('click', function () {
$('tbody').children("tr").hide();
$("tbody tr").hide();
$("tr.items_tr").hide();
});
)};
If you are trying to hide these elements right after call the getTable function then add the 3 functions to the $.post success function
code should look like:
function getTable(){
var args = new Object();
args.op = "getTable";
$.post("./service.php", args , function(data)
{
var dataObj = JSON.parse(data);
for(var i = 0; i<dataObj.length; i++){
$('table#items tbody').append("<tr class=\"items_tr\" id=\"item"+dataObj[i].sn+"\"><td>"+dataObj[i].sn+"</td><td>"+dataObj[i].name+"</td><td>"+dataObj[i].description+"</td><td>"+dataObj[i].belonged+"</td></tr>");
}
$('tbody').children("tr").hide();
$("tbody tr").hide();
$("tr.items_tr").hide();
});
}
getTable();
Hi jquery binds events on document.ready if there are elements which are created after document.ready then you need to register those elements for those events using .bind() function of jquery. But be sure first unregister alredy attached elements. using unbind();
Refer bind and unbind functions.

jQuery remove elements after creation

I'm building a jQuery sortable list where the user can add items from a table, drag and sort and/or remove them. I can add and sort no problem, but I can't work out how to remove an item element after it has been added. I'm relatively new to js / jQuery, so I have a feeling there's something new to learn here about how it works!
I'll leave out the ui.sortable stuff here as I'm only concerned with removing items..
<table>
<tr>
<td><a class="addrelease" href="#" cat_id="1">add</a></td>
<td>Item 1</td>
</tr>
<tr>
<td><a class="addrelease" href="#" cat_id="2">add</a></td>
<td>Item 2</td>
</tr>
</table>
<div id="list"></div>
<script>
$("a.addrelease").click(function (e) {
e.preventDefault();
cat_id = $(this).attr('cat_id');
remove_str = " remove";
str = cat_id + remove_str;
$(str).appendTo("#list").hide().fadeIn();
});
$("a.remove").click(function (e) {
alert("This function doesn't seem to be called");
$(this).parent().remove(); //Doesn't happen..
});
</script>
I'm guessing that javascript doesn't recognize the new generated items - but I'm not sure, so I'm not sure where to start fixing it
Cheers
You should use live function to attach events to dynamically added elements.
Try this to bind click event to a.remove elements:
$("a.remove").live("click", function (e) {
alert("This function doesn't seem to be called");
$(this).parent().remove(); //Doesn't happen..
});
You're absolutely right, javascript won't recognise new items.
jQuery selectors will normally only match against elements currently in the document. When you use $("a.remove").function(), jQuery builds a list of nodes matching "a.remove", then calls function() on each of them.
The .live() function is special, and doesn't attach events directly to elements - instead, events bubbling up to the top of the DOM are evaluated to see if they match the selector.
IMHO, the best approach is to bind the remove handler when you create the new list entry:
str = cat_id + remove_str;
var remove = $(str);
remove.appendTo("#list").hide().fadeIn();
remove.click(function(e) { .... })
Disclaimer: Typed late at night & not tested!
Here is my answer of how I think you should modify your code:
http://jsfiddle.net/RY5CP/
<table>
<tr>
<td><a class="addrelease" href="#" rel="1">add</a></td>
<td>Item 1</td>
</tr>
<tr>
<td><a class="addrelease" href="#" rel="2">add</a></td>
<td>Item 2</td>
</tr>
</table>
<div id="list"></div>
<script type="text/javascript">
$("a.addrelease").click(function(e) {
e.preventDefault();
var catId = $(this).attr('rel');
var itemName = $(this).closest('td').next('td').text();
var newItem = '<p>' + catId + ' ' + itemName + ' remove';
$(newItem).appendTo('#list').hide().fadeIn();
});
$("a.remove").live('click', function(e) {
$(this).parent('p').remove();
});
</script>
It's not valid to use cat_1, cat_2 as HTML attributes. You can use the rel attribute if you need to have a specific value to be associated to your items
Use the live() method to have the click event handler automatically attached to items dynamically created

.getScript() is not working when loaded from a .load()

You can see the way its suppose to work by visiting:
link text
That is loading everything using standard loading.
You can attempt to get it to load dynamically here:
link text and then simple just click the "Parts" link... As you can see no alert every comes to me.
I have a feeling it has to do with the ability of detecting when to fire the getscript..as ondomready wouldn't reload when loading dynamically.
Here is the main script that will load the content:
$('#sub-navigation li a').click(function(){
var toLoad = $(this).attr('href')+' #content-container > *';
$('#content-container').hide('fast',loadContent);
var href = $(this).attr('href');
if (href == ".") {
window.location.hash = "";
} else {
window.location.hash = href;
}
// first remove the selected class from the active one
$('#sub-navigation li.selected').removeClass('selected');
// then find the parent li of the clicked element and add the selected class
$(this).parents('li').addClass('selected');
//$('#load').remove();
//$('#wrapper').append('<span id="load">LOADING...</span>');
//$('#load').fadeIn('normal');
function loadContent() {
$('#content-container').load(toLoad,'',showNewContent())
}
function showNewContent() {
$('#content-container').show('normal',hideLoader());
}
function hideLoader() {
//$('#load').fadeOut('normal');
}
return false;
});
Here is the file that gets loaded:
<div id="sub-navigation-content" class="transparent">
</div>
<div id="content-container" class="transparent">
<div id="content">
<link rel="stylesheet" type="text/css" href="/files/tablesorter/themes/blue/style.css">
<script type="text/javascript">
$(document).ready(function () {
$.getScript("/files/tablesorter/jquery.tablesorter.min.js", function() {
$("#parts").tablesorter();
alert('sort performed');
});
});
</script>
!!!!!!! INSERT MORE HTML HERE !!!!!!!
</div>
</div>
here is the rest of the "INSERT MORE HTML HERE":
<h1>Parts</h1>
<table id=parts class=tablesorter style="width: 500px;">
<thead>
<tr>
<th>Part #</th>
<th>Part Description</th>
<th>Price</th>
<th>Additional<br>Shipping</th>
</tr>
</thead>
<tbody>
<tr>
<td>AM01</td>
<td>ECONOMY OARS (EACH)</td>
<td>$30.00</td>
<td>$3.00</td>
</tr>
<tr>
<td>AM02</td>
<td>DELUXE OARS (EACH)</td>
<td>$42.00</td>
<td>$3.00</td>
</tr>
<tr>
<td>AM03</td>
<td>OAR LOCKS (PAIR)</td>
<td>$10.00</td>
<td>$2.00</td>
</tr>
<tr>
<td>AM04</td>
<td>LAKER BOW CASTING</td>
<td>$25.00</td>
<td>$3.00</td>
</tr>
<tr>
<td>AM05</td>
<td>PRO BOW CASTING</td>
<td>$25.00</td>
<td>$3.00</td>
</tr>
<tr>
<td>AM06</td>
<td>OAR LOCK CASTING W/INSERT</td>
<td>$28.00</td>
<td>$3.00</td>
</tr>
<tr>
<td>AM07</td>
<td>REAR CORNER CASTING RIGHT</td>
<td>$25.00</td>
<td>$3.00</td>
</tr>
<tr>
<td>AM08</td>
<td>REAR CORNER CASTING LEFT</td>
<td>$25.00</td>
<td>$3.00</td>
</tr>
<tr>
<td>AM09</td>
<td>FISHERMAN BOW SET (3 PC.)</td>
<td>$25.00</td>
<td>$3.00</td>
</tr>
<tr>
<td>AM10</td>
<td>OAR LOCK INSERTS (EACH)</td>
<td>$4.00</td>
<td>$2.00</td>
</tr>
<tr>
<td>AM11</td>
<td>DRAIN PLUG (5/8 MODIFIED)</td>
<td>$5.00</td>
<td>$2.00</td>
</tr>
<tr>
<td>AC06</td>
<td>LIFT VEST TYPE III</td>
<td>$25.00</td>
<td>$2.00</td>
</tr>
<tr>
<td>AC07</td>
<td>1" DRIAN PLUG</td>
<td>$5.00</td>
<td>$2.00</td>
</tr>
<tr>
<td>AC09</td>
<td>5/8" DRAIN PLUG</td>
<td>$5.00</td>
<td>$2.00</td>
</tr>
<tr>
<td>AM11</td>
<td>TOUCH UP PAINT</td>
<td>$15.00</td>
<td>$2.00</td>
</tr>
<tr>
<td>AM69</td>
<td>CONSOLE (SUPER PRO 16)</td>
<td>$135.00</td>
<td>Please call</td>
</tr>
<tr>
<td>AM70</td>
<td>CONSOLE W/STEERING (SUPER PRO 16)</td>
<td>$430.00</td>
<td>Please call</td>
</tr>
</tbody>
</table>
As per the catch by jason in the answers. I have no choice to my knowledge than to make an ugly work-around as follows:
If anyone has an idea that is more elegant, please let me know.
function showNewContent() {
$('#content-container').show('normal',hideLoader);
if (href == "parts") {
$.getScript("/files/tablesorter/jquery.tablesorter.min.js", function() {
$("#parts").tablesorter();
});
}
}
Edit: 12/18/2010-
I now have to dynamically load different scripts depending on which "parts" page is loaded. Of course I can keep using the same workaround I did before, but I am looking for a better solution for this. I have added a bounty to help.
updated solution:
I was able to do what I want by just mimicing the load function using get():
$.get(href, function(response){
var contents = $(response).find('#content-container > *');
$("#content-container").html(contents);
});
Which is more or less what Jason was telling me to to; therefore I am giving him the bounty.
updated solution 2:
the find() command will strip out any script tags as of 1.4+, therefore I had to do this:
$.get(href, function(response){
var contents = $(response).find('#content-container > *');
$(response).filter('script[src=""]').each(function(){
contents.push(this);
});
$("#content-container").html(contents);
});
I am still looking to avoid this complicated mess, but it seems this is what I will have to live with.
Here's the relevant bit of code from $.load that performs the insert in jQuery 1.4.2 (starting line 4820):
// Request the remote document
jQuery.ajax({
url: url,
type: type,
dataType: "html",
data: params,
complete: function( res, status ) {
// If successful, inject the HTML into all the matched elements
if ( status === "success" || status === "notmodified" ) {
// See if a selector was specified
self.html( selector ?
// Create a dummy div to hold the results
jQuery("<div />")
// inject the contents of the document in, removing the scripts
// to avoid any 'Permission Denied' errors in IE
.append(res.responseText.replace(rscript, ""))
// Locate the specified elements
.find(selector) :
// If not, just inject the full result
res.responseText );
}
if ( callback ) {
self.each( callback, [res.responseText, status, res] );
}
}
});
As you can see, that if a selector is specified in the content to be loaded (which you are), jQuery will remove all inline script blocks from the HTML before inserting it into the page.
Why? The creation of a DOM document fragment on the fly is not at all consistent across browsers, and there's no real way to control when, how, or even if the script(s) will get executed.
One example would be: Should it happen when the fragment is created (as jQuery does, before it runs the selector on it)? In this case, your new content won't actually be part of the parent document when the script is ran.
How can you work around this? Well, you can always recreate what $.load() does internally, (essentially just issue a GET request, create a jQuery object from the response, then call .find(selector) on it before appending/injecting). But, beware. This may be unreliable, and I have no idea how different browsers may react to this.
I remember reading a long discussion about exactly this a while back--I can't find it right now, but when I do, I'll add a link here.
I was able to do what I want by just mimicing the load function using get():
$.get(href, function(response){
var contents = $(response).find('#content-container > *');
$("#content-container").html(contents);
});
Which is more or less what Jason was telling me to to; therefore I am giving him the bounty.

Categories