jQuery undefined result with .attr() method - javascript

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>

Related

How to obtain text of a <td> element from another <td> element with jquery

I am trying to use a button inside a table division to set a variable as the same value as another division in the same row, but whenever I run my code (below), it returns the value of all the table divisions concatenated together. I am unsure why this was happening, so I replaced '.children()' with 'childnodes[0]' to try and get only the first name, but this just doesn't work and I don't why.
My html looks like this:
<table>
<tr>
<td>John</td>
<td>Doe</td>
<td><button>Get First Name</button></td>
</tr>
</table>
And my Javascript is this:
$(document).ready(function(){
$("button").click(function(){
var first = $(this).closest("tr").childNodes[0].text();
alert(first)
})
});
set a variable as the same value as another division in the same row
there are lots of possibilities for this, here are some (with the most useful first (opinion based))
$("button").click(function() {
var first = $(this).closest("tr").find("td:first").text();
var first = $(this).closest("tr").find("td").first().text();
var first = $(this).closest("tr").find("td").eq(0).text();
var first = $(this).closest("tr").children().first().text();
var first = $(this).closest("tr").children().eq(0).text();
var first = $(this).closest("td").siblings().first().text();
});
it returns the value of all the table cells concatenated together
https://api.jquery.com/text
Get the combined text contents of each element in the set of matched elements, including their descendants, or set the text contents of the matched elements.
because you're passing the "tr" to text() it gets the text of all the cells (tds) and their content etc and combines them as one, so you need to limit to the first as you've attempted.
however .childNodes[0] can only be applied to a DOM element/node, while $(this).closest("tr") gives you a jquery object/collection, which doesn't have .childNodes property.
So the jquery equivalent would be to use .children().eq(0).
You could use class identifiers to get information you need as well.
<table>
<tr>
<td><span class="first-name">John</span></td>
<td><span class="last-name">Doe</span></td>
<td>
<button class="btn-get-data" data-class="first-name">Get First Name</button>
<button class="btn-get-data" data-class="last-name">Get Last Name</button>
</td>
</tr>
</table>
$(document).ready(function() {
$(".btn-get-data").click(function() {
$btn = $(this);
$tr = $btn.closest('tr');
var first = $tr.find('.' + $btn.attr('data-class')).html();
alert(first);
})
});
If you make the button click generic like so, you can add additional buttons on the page and use that to get the class within that row.
Here is a working fiddle example: https://jsfiddle.net/b1r0nucq/
you could find the :first child and get his html(), as below:
$(document).ready(function() {
$("button").click(function() {
var first = $(this).closest("tr").children(":first").html();
alert(first)
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr>
<td>John</td>
<td>Doe</td>
<td><button>Get First Name</button></td>
</tr>
</table>

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.

Getting the value of TD in a foreach loop

I have this code which show me the list of my item
<tbody class="table-color2">
<c:forEach var="defect" items="${defectList}">
<tr>
<td id="defectId"><a onclick="getDefectId()">${defect.id}</a></td>
<td>${defect.createdDate}</td>
<td>${defect.reportedBy.firstName}</td>
<td>${defect.title}</td>
<td>${defect.bugtype.description}</td>
<td>${defect.status.description}</td>
<td>${defect.priority.description}</td>
</tr>
</c:forEach>
</tbody>
what Im trying to do is to get the value of the td id="defectId" when it is clicked, my jquery script for the onclick is this :
function getDefectId(){
var defectId = $(' #defectId ').val();
alert("Defect ID " + defectId);
}
but currently, I'm getting a value of undefined, how do I get the value?
You have several issues. Firstly you're appending the same id in a loop, which will result in duplicates which is invalid. Also, a elements don't have a value to retrieve so you need to use text(). Finally note that using your current method would mean that you need to pass the this reference of the clicked element to the function.
However you can tidy all that up by using classes and hooking up the event in JS. Try this:
<tbody class="table-color2">
<c:forEach var="defect" items="${defectList}">
<tr>
<td class="defect">${defect.id}</td>
<td>${defect.createdDate}</td>
<td>${defect.reportedBy.firstName}</td>
<td>${defect.title}</td>
<td>${defect.bugtype.description}</td>
<td>${defect.status.description}</td>
<td>${defect.priority.description}</td>
</tr>
</c:forEach>
</tbody>
$(function() {
$('.defect a').click(function(e) {
e.preventDefault();
console.log($(this).text());
});
});
Working example
You can do the following(remember that id needs to be unique):
<td class="defectId"><a onclick="getDefectId(${defect.id})">${defect.id}</a></td>
And in you function:
function getDefectId(value){
alert("Defect ID " + value);
}
Try this : use text() instead of val() because you want to know the text inside td and not the value of input element.
EDIT: - Sorry, missed that there are multiple td's. In this case, you must not use same id for all tds. Just make it class and do following changes in function.
function getDefectId(){
var defectId = $('.defectId').text();
alert("Defect ID" + defectId);
}
I would suggest to use jQuery event handler instead of calling javascript function. See below code
HTML: remove onclick call
<td class="defectId">${defect.id}</td>
jQuery : register a click handler and read text
$(function(){
$('.defectId a').click(function(){
var defectId = $(this).text();
alert(defectId);
});
});

the use of addClass/removeClass of a table td with ajax

The following code outputs a table with values, and according to the value, it gets a layout. I use jQuery (Ajax) to update the information every 10 seconds.
php script that generates the wanted variables and stores (echoes) them into a json array (example.php)
$variable1 = 20;
if ($variable1 > 0) {
$td_variable1class="positive";
} else {
$td_variable1class="negative";
}
$array['variable1'] = $variable1;
$array['td_variable1'] = $td_variable1;
echo json_encode($array);
html table where the variable is retrieved from the json generated by the example.php:
<table>
<tr>
<td class='variable1class'>
<div id='variable1'></div>
</td>
</tr>
</table>
javascript:
<script type="text/javascript">
$(function() {
refresh();
});
function refresh() {
$.getJSON('example.php', function(data) {
$('div#variable1').html(data.variable1);
$('td.td_variable1').addClass(data.td_variable1);
});
setTimeout("refresh()",10000);
}
The problem is the "addClass" adds a class to the existing class, resulting in an output like this:
before the refresh:
<td class="td_variable1 positive">
after (assuming the variable changed from positive to negative):
<td class="td_variable1 positive negative">
I tried to avoid this by using removeclass:
$('td.td_variable1').removeClass().addClass(data.td_variable1);
But then the actual class name of the td is removed and my output looks like this:
<td class="negative">
and it should look like this:
<td class="td_variable1 negative">
Thanks for your help in advance!
Assuming you only have those 2 options, remove both (and only those two) then add the new one:
$('td.td_variable1').removeClass("positive negative").addClass(data.td_variable1);
.removeClass() removes all classes when not given any parameter. Use .removeClass("negative") instead.

.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