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

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.

Related

Uncaught ReferenceError: Function is not defined using external js file

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>

Can't get src of img (JS, Jquery)

I have some table.
<table>
<tr>
<td>1</td>
<td><img src="uploads/0/58.gif"></td>
<td>Name 1</td>
<td>8</td>
</tr>
<tr>
<td>2</td>
<td><img src="uploads/0/25.gif"></td>
<td>Name2</td>
<td>5</td>
</tr>
</table>
Need to get src of all images from table. I'm doing some replaces in table, so
var tbl = $('table').html();
After replaces, I need to get src-s:
I really don't know how to write it, like $(tbl + 'tr').each....
So I write:
$('table tr').each(function(index){
let tds = $(this).find('td'); // get array td-s
console.log(typeof(tds.html())) // Object
console.log(tds[1]) // <td><img src="uploads/0/58.gif"></td>
//$('img').src // error
//$('img').getAttribute('src') // error,
//$(this).find('img').src // error
//ets...
});
I try somethink like
or $(this).find('img) // == Object, i can't get src from
You could use a more complete selector:
$('table td > img[src]').each(function(i, e) { });
Or for all images in the page with a source, it would look like this:
$('img[src]').each(function(i, e) { console.log(e.src); });
You might want to read up on CSS selectors :)
A very simple solution based on your tried examples is:
console.log(tds[1].firstChild.src);
To get result inside a loop:
$('table tr').each(function(index){
console.log(tds[1].firstChild.src);
});

How to filter multiple table entries with search

How do I filter through the tables and only show the ones matching the keywords the user types?
I want to be able to have multiple tables within the page, but I want to still be able to show and hide based on user input.
<!-- <table class="responsive-stacked-table with-mobile-labels tablesection">
<tr>
<th>Submitted By</th>
<th>Egg or Nestling?</th>
<th>How Many?</th>
<th>Location</th>
<th>Description of Nest</th>
<th>Possible Species?</th>
<th>Edit // Delete</th>
</tr> -->
<br><table class="responsive-stacked-table with-mobile-labels tablesection">
<tr>
<th>Submitted By</th>
<th>Date Submitted</th>
<th>Egg or Nestling?</th>
<th>How Many?</th>
<th>Location</th>
<th>Description of Nest</th>
<th>Possible Species?</th>
<th>Edit // Delete</th>
</tr><tr><td data-label='Submitted By:' ><img src='images/profilepics/levi.jpg'class= 'img-circle submittedbypicture' > <br>partymo</td><td data-label='Date Submitted:' ><br> Last Edited: <br> </td><td data-label='Egg or Nestling:' >egg</td><td data-label='How Many:'>3</td><td data-label='Location:'>$nest['profilepicfile']</td><td data-label='Description:'>$nest['profilepicfile']</td><td data-label='Possible Species:'>edit if you know!</td><td>Edit // Delete</td></tr></table><br><br><br><br><table class="responsive-stacked-table with-mobile-labels tablesection">
<tr>
<th>Submitted By</th>
<th>Date Submitted</th>
<th>Egg or Nestling?</th>
<th>How Many?</th>
<th>Location</th>
<th>Description of Nest</th>
<th>Possible Species?</th>
<th>Edit // Delete</th>
</tr><tr><td data-label='Submitted By:' ><img src='images/profilepics/rubert.jpg'class= 'img-circle submittedbypicture' > <br>robster</td><td data-label='Date Submitted:' >2017-08-09<br> Last Edited: <br> 2017-08-11</td><td data-label='Egg or Nestling:' >nestling</td><td data-label='How Many:'>4</td><td data-label='Location:'>4</td><td data-label='Description:'>really big. </td><td data-label='Possible Species:'>edit if you know!</td><td>Edit // Delete</td></tr></table><br><br><br>
If I understand correctly, you basically want tables that can be filtered using a textbox?
If you're thinking js-based client-side filtering, and you're fine using jQuery, you could set up your HTML like so:
<input type="textbox" class="fitler-input" data-for="filterable-table1">
<table id="filterable-table1">
...
</table>
then you could use jQuery to do something like
<script type="text/javascript">
function filterfunction(tablerow,testvalue) {
// sample filter: include rows where any cell contains the testvalue text
return tablerow.text().toLowerCase().indexOf(testvalue.toLowerCase()) > -1;
}
jQuery(function($){
$(".filter-input").each(function(){ // start by iterating all filter textboxes
var $filter = $(this); // select the filter
var $table = $("#"+$filter.data("for")); // select this filter's table
$filter.on("change keydown keyup keypress", function(){ // whenever user types in box,
setTimeout(function(){ // timeout makes the value retrieval more instant
var $rows = $table.find(">tr, >tbody>tr"); // get all table rows
var value = $filter.val(); // get this filter's new value
$rows.each(function(){ // go through all rows, and test against filter
var $row = $(this); // get this row
if(filterfunction($row,value)) // test this row
$row.show(); // it matches
else
$row.hide(); // it doesn't match
});
},1);
});
});
});
</script>
This would also be something you could do in vanilla js (i.e., using no libraries), but it'd take a bit more time and code.
And here's a JSFiddle to demonstrate it in action.
EDIT:
Just my opinion, but:
As per #Peter-Rakmanyi's comment, if your site / webapp / interface is heavily data-driven, and you plan on having a lot of HTML manipulation based on user input, AngularJS might be something you should consider looking into. It would require a little refactoring of your HTML (which might mean some php tweaks), but it may be worth it.
If this is one of only a few small cases you'll need filtering for, you can probably just get away with using jQuery.

jQuery undefined result with .attr() method

I have a table where I want to get the value of data-url from within a <td>-tag. The content of the table gets first populated via $.ajax GET. Then I use the complete-function to log onto the console the values of data-url. The result is undefined.
$(document).ready(function(){
$.ajax({
type: "GET",
cache: false,
url: "http://localhost:80/server/api/v1/files",
success: function(data){
if (data.error) {
console.log(data.error)
} else {
$("#t1").append(data);
}
},
error: function(data){
},
complete: function(data){
console.log( $('#t1 #f1').data('url') );
}
});
The table first looks like this:
<table id="t1"></table>
After the Ajax call it looks like this:
<table id="t1">
<tr>
<th>
Name
</th>
</tr>
<tr>
<td>
<a id="f1" data-url="test" href="/Logo.jpg">...</a>
</td>
</tr>
</table>
What could be the correct way to get the value of data-url in your oppinion?
Use data() function to access the data attributes using jQuery. and not attr() or prop(). Though they may work but might not be best suited. See this Question for reference.
jQuery.data() Reference
$(document).ready(function(){
console.log( $('#t1 #f1').data('url') );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="t1">
<tr>
<th>
Name
</th>
</tr>
<tr>
<td>
<a id="f1" data-url="test" href="/Logo.jpg">...</a>
</td>
</tr>
Hope this helps :)
EDIT2, after comments.
The problem is that you are running this code when the document is ready. But the table appears from an ajax call, which means the element is not there when your code is ran. You should move the function inside your ajax complete event.
EDIT: Use the one you want. Your question includes 2 different ones.
"How is it best to access data attributes?", which got covered here. What you'll want depends mostly on your case but using data attributes with jQuery makes it very interesting to use .data() instead of .attr(). Because .attr() will always return the value stored in the HTML, while data will return the data of the object, which could have been altered. This could even be more than plain text...
"What is the most efficient way to select a dom element", which got many answers everywhere, but to make it short, if your element has an id, use it. If not, try to be the most precise possible... I'll let you read on more about this.
Some of your tries work... See here.
$(document).ready(function(){
console.log(1, $('#t1 #f1').attr('data-url') ); //works
console.log(2, $('#t1 f1').attr('data-url') );
console.log(3, $('#t1 tr td a').attr('data-url') );//works
console.log(4, $('#t1').attr('data-url') );
console.log(5, $('#f1').attr('data-url') );//works
console.log(6, $('#f1').data("data-url") );
console.log(7, $('#f1').data("url") );//works (is best IMO)
console.log(8, $('#t1 tr td a f1').attr('data-url') );
console.log(9, $('#t1 tr td a f1').data('url') );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="t1">
<tr>
<th>
Name
</th>
</tr>
<tr>
<td>
<a id="f1" data-url="test" href="/Logo.jpg">...</a>
</td>
</tr>

how to make layout work in grails when link has id (or not)

The following is my code in land.gsp page.
<table style="padding: 10 ">
<thead>
<tr style="color: blue">
<td>Gname</td>
<td>Gowner</td>
<td>Device number</td>
<td>Edit </td>
<td>Delete </td>
</tr>
</thead>
<tbody>
<g:each in="${Groups.list()}" status="i" var="groupsInstance">
<g:set var="myid" value="${groupsInstance.id}"></g:set>
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td>${fieldValue(bean: groupsInstance, field: "gname")}</td>
<td>${fieldValue(bean: groupsInstance, field: "gowner")}</td>
<td>${fieldValue(bean: groupsInstance, field: "devicenum")}</td>
<td><g:link action="edit" id="${groupsInstance.id}">Edit</g:link></td>
<td><g:link action="deleteme" id="${groupsInstance.id}">Delete</g:link></td>
</tr>
</g:each>
</tbody>
</table>
But When I click on edit The layout of the page is not working( javascript, images, styles). But the id get passed to the edit.gsp page correctly. When I change
<td><g:link action="edit" id="${groupsInstance.id}">Edit</g:link></td>
to
<td><g:link action="edit" >Edit</g:link></td>
that is without passing id and clicking on edit will give a page with correct Layout. Following is my edit action
def edit(Long id) {
def groupsInstance = Groups.get(id)
if (!groupsInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'groups.label', default: 'Groups'), id])
redirect(action: "list")
return
}
[groupsInstance: groupsInstance]
}
Where I went wrong? I want to pass the id to edit page with all correct layout.
Try to view the source html in your browser, that might give you a clue to what is wrong.
Try to adopt with below option based on your preference :
<g:link action="/conference/participated" id="${it.conference.id}"
params="[foo: 'bar', bar: 'foo']">My Link!</g:link>
I see in your code that when
def groupsInstance = Groups.get(id)
if (!groupsInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'groups.label', default: 'Groups'), id])
redirect(action: "list")
return
You have redirected it to a list page , am sure list page has different layout setting and that is the change , please check on that and let me know!
From looking at your edit action it looks like if no id is supplied then you redirect to the list action and you say the layout is fine there. If the id is specified (and it is valid) you are rendering the edit view. I expect that the problem is in your edit.gsp which is not currently shown in your description.

Categories