jquery to find all exact td matches - javascript

$('#servertable td:eq(' + server + ')')
this finds only 1 (first I think) match, how to find all matches.
btw. td:contains will not work for me.

eq expects a numerical index to only return a single row. If you want to match a td by its contents, you have to use the :contains selector. Saying "it doesn't work" and throwing it away is not the right approach to the problem, as the selector is (most likely) not at fault (Do note its case sensitive, which might be it...)
Anyhow, if you have a table like this:
<table>
<tr>
<td>Hello</td>
<td>World</td>
</tr>
<tr>
<td>World</td>
<td>Hello</td>
</tr>
<tr>
<td>Hello</td>
<td>Hello</td>
</tr>
</table>
This jQuery code:
$(function() {
$("td:contains('Hello')").css('color','red');
});
Will turn all cells with "Hello" to red. Demo.
If you need a case insensitive match, you could do this, using the filter function:
$(function() {
var search = 'HELLO'.toLowerCase();
$("td").filter(function() {
return $(this).text().toLowerCase().indexOf(search) != -1;
}).css('color','red');
});
If you need to match the exact contents of the cell, you could use something similar to the above:
$(function() {
var search = 'HELLO'.toLowerCase();
$("td").filter(function() {
return $(this).text().toLowerCase() == search;
}).css('color','red');
});
The above is case insensitive (by turning both the search and the contents to lower case when comparing) otherwise you can just remove those if you want case sensitivity. Demo.

I could be wrong, but the :eq positional selector takes an integer n an finds the nth matching element.
So if you said td:eq(1) -- you'd get the 2nd TD element in the table (second because the first index is zero/0).
My guess is that you don't want to use the :contains selector because you're looking for an exact string match and don't want partial matches.
I'm not aware that jquery has a built in selector that will meet your needs (if so, please correct me). You could add one as an extension or use another method, such as an attribute selector to do the search for you.
If you are able to control the generated HTML, you could add an ID attribute to each TD like so:
<table id="servertable" border="1">
<thead>
<tr><th>Server</th><th>Memory</th></tr>
</thead>
<tbody>
<tr><td id="server_mars">Mars</td><td>4 GB</td></tr>
<tr><td id="server_venus">Venus</td><td>1 GB</td></tr>
<tr><td id="server_jupiter">Jupiter</td><td>2 GB</td></tr>
<tr><td id="server_uranus">Uranus</td><td>8 GB</td></tr>
<tr><td id="server_mars_2010">Mars_2010</td><td>4 GB</td></tr>
</tbody>
</table>
<form>
<label for="server">Find:</label><input type="text" id="server" />
<button id="find">Find</button>
</form>
The following attribute selector would locate the correct TD in the table:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#find").click(function() {
var server = $("#server").val();
$("#servertable td").css("background-color", ""); // reset
$("#servertable td[id='server_" + server.toLowerCase() + "']").css("background-color", "#FFFF00");
return false;
});
});
</script>
If you instead want to target the entire row that has the TD that you're looking for, you can add additional selectors:
$("#servertable tbody tr").css("background-color", "");
$("#servertable tbody tr:has(td[id='server_" + server.toLowerCase() + "'])").css("background-color", "#FFFF00");
The tbody tag isn't completely necessary, it just helps to distinguish between rows in the table body and rows in the table header.

Try :containsExact
http://wowmotty.blogspot.com/2010/05/jquery-selectors-adding-contains-exact.html
$.extend( $.expr[":"], {
containsExact: $.expr.createPseudo ?
$.expr.createPseudo(function(text) {
return function(elem) {
return $.trim(elem.innerHTML.toLowerCase()) === text.toLowerCase();
};
}) :
// support: jQuery <1.8
function(elem, i, match) {
return $.trim(elem.innerHTML.toLowerCase()) === match[3].toLowerCase();
},
containsExactCase: $.expr.createPseudo ?
$.expr.createPseudo(function(text) {
return function(elem) {
return $.trim(elem.innerHTML) === text;
};
}) :
// support: jQuery <1.8
function(elem, i, match) {
return $.trim(elem.innerHTML) === match[3];
},
containsRegex: $.expr.createPseudo ?
$.expr.createPseudo(function(text) {
var reg = /^\/((?:\\\/|[^\/]) )\/([mig]{0,3})$/.exec(text);
return function(elem) {
return RegExp(reg[1], reg[2]).test($.trim(elem.innerHTML));
};
}) :
// support: jQuery <1.8
function(elem, i, match) {
var reg = /^\/((?:\\\/|[^\/]) )\/([mig]{0,3})$/.exec(match[3]);
return RegExp(reg[1], reg[2]).test($.trim(elem.innerHTML));
}
});

$('#servertable td')
will find all td elements, but it's not entirely clear what you expect.

I too encountered this very same problem as the original author. As Paulo the original question poser had. Which selector can I use to match elements based equality check on element contents. At least I presume that's what he did (as did I) try to achieve and that would also explain why he (as well as I) can't use contains for the obvious reasons ra170 pointed out in his comment. Anyhow if someone happens to stumble here looking for the answer to that question here's the short answer to it:
jQuery has no such matcher by default. The solution is to define your own matcher. To tackle the problem at hand see this excellent blog post by Motte.

Related

Is there simple way to get array from td elements on page? (node.js/cheerio/jQuery)

I am using node.js/cheerio (the same as jQuery syntax) to parse the page.
And when I am using:
$ = cheerio.load(body);
console.log($('.td-title a').text());
I have in my console very long string from words "mainaboutcontactusprofile" etc. How can I make an array of td text?
None of the answers provided worked for me, but this did.
let arr = $('.td-title a').toArray().map((x) => { return $(x).text()});
Credit: Github
jQuery's map()
console.log($("td").map(function(){ return this.textContent}).get());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
</table>
I suggest using jQuery's $.map() function for this. It creates an array based on a matched set of elements:
var textArray = $('.td-title a').map( function(index, domElement) {
return domElement.innerText; //each element input gets turned into text output
});
console.log( textArray );
See http://api.jquery.com/map/ for more details
in javascript
simply document.getElementsByTagName("td") return an array of all td.
Using jQuery you should use something like $('#table1 td')

How to make a <tr> invisible if it's <td>'s are empty?

I have a table which contains 'n' rows dynamically generated. All <td>'s may or may not contain data. I want to hide or delete '<tr>' if all it's td's are empty.
I don't know how to parse through all <td>'s and make sure it's empty or not.
example table as follows,
<table id='ex_table'>
<tr>
<td>one</td>
<td>two</td>
</tr>
<tr>
<td></td>
<td>one</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
In the above table last row need to be hidden since all it's <td>'s are empty. I prefer jquery to do this.
You don't actually have to inspect the td elements. You can select the rows and use .filter to filter out those with text content, i.e. only keep those which are empty:
$('#ex_table tr').filter(function() {
return $.trim($(this).text()) === '';
}).hide(); // or .remove()
This works because .text gets the combined inner text of all descendants and $.trim removes whitespace characters that might occur due to your HTML formatting (but are not actually content).
If you have cells which contain HTML elements but no text content (e.g. icons styled through CSS), and you want to keep those, you'd have actually have to test whether a cell contains HTML or not. The approach is basically the same as testing for text content
$('#ex_table tr').filter(function() {
return $(this).children().filter(function() {
return $.trim($(this).html()) !== '';
}).length === 0;
}).hide();
only this time we count the number of cells in a row that contain HTML. If there is none, the row is considered empty.
try something like this
$(function(){
$('#ex_table tr').each(function(){
var val = $(this).text().trim();
if(val == ''){
$(this).remove();
}
})
})
Try this:
$('#ex_table tr').each(function){
if($(this).text()==''){
$(this).remove();
}
}
Straight jQuery you say?
EDIT: I'd use Felix's answer.. just change ".hide()" to ".remove()" if you'd like to delete the element.
$('tr').each(function(){
var hasValue = false;
$(this).children().each(function(){
if ($(this).html() != "")
hasValue = true;
});
if (!hasValue)
$(this).remove();
});
Try this code
var hide;
$("tbody").find("tr").each(function(){
hide=true;
$(this).find("td").each(function(){
if($(this).html()!='') {
hide=false;
}
});
if(hide==true) {
$(this).hide();
}
});

Traversing the DOM indepth

Let's say I have this TR tag
<tr>
<td>1</td>
<td><a class="links" href="http://www.linkedin.com/in/jannuelanarna">Jannuel Christian Anarna</a></td>
<td>Calabarzon, Philippines</td>
<td>Market Research</td>
<td>BDA Partnership</td>
<td>Inside Sales Representative</td>
<td>Invite</td>
<td><input type="checkbox" /></td>
</tr>
Now I've targeted the a.links with :
var memberDiv = document.getElementByClassName('links');
Now in my code, i have a loop that evaluates if the href value has a match on an array. And it will return an array matching the values. Now my question is, how could I target the <td> with an a href, and remove the anchor tag?.
After reading, I think I understand. You want to remove the anchors within the TD elements that have been found as a match and convert them to regular text, right?
jQuery code to unwrap the anchors would look like this.
$('tr td a[href="matched-link.html"]').contents().unwrap();
Here's a fiddle showing a test of what I think your question needs: http://jsfiddle.net/EcKKt/
Edit:
For fun, I also created a pure JS example of the same thing here: http://jsfiddle.net/xhR85/
The gist is this:
matches.forEach(function(match, i) {
elements = document.querySelectorAll('tr td a[href="' + match + '"]');
Array.prototype.forEach.call(elements, function(element, ii) {
element.parentNode.innerHTML = element.innerHTML;
});
});

How to do this in jQuery?

I have to attach the method copyToLeft on onClick event of all images which are inside the TD. TD is inside the table named mismatchList, so that the structure becomes like this mismatchList > tbody > tr > td > img
Although i have already done this, but that is using plain javascript. What i did was, i manually added copyToLeft(this); method on onClick event of all specified elements at the time of creation. [ This is the step which i want to omit and use jQuery to do this somehow ].
Also definition of copyToLeft goes like this:-
function copyToLeft(obj){
leftObj = getLeftTD (obj); // my method which returns the adjacent Left TD
rightObj = getRightTD (obj);
if ( leftObj.innerHTML != rightObj.innerHTML ) {
leftObj.innerHTML = rightObj.innerHTML;
leftObj.bgColor = '#1DD50F';
}else{
alert ( 'Both values are same' );
}
}
If required copyToLeft method's definition can also be changed. [ just in case you think, jQuery can be used to make this method better :) ]
Edit
Instead of asking another question i am just adding the new requirement :) [ let me know if i am supposed to create new one ]
i have to add copyToLeft method to all images as i specified, but alongwith that image src should be left_arrow.gif, and add copyToRight method if src is right_arrow.gif. Also, how can we get the adjacent left/right TD in jQuery, as i want to replpace my getLeftTD and getRightTD method as well?
If i've understood your question correctly, in jQuery, you'd bind the event as such:
$(document).ready(function() {
$('mismatchList > tbody > tr > td > img').click(copyToLeft);
});
In your copyToLeft function, you don't accept obj as an input parameter, instead this will be the image. $(this) will be a jQuery object, containing the image, should you require it...
You could do something like this to match the image src.
$('#mismatchList > tbody > tr > td > img[src='left_arrow.gif']').click(copyToLeft);
$('#mismatchList > tbody > tr > td > img[src='right_arrow.gif']').click(copyToRight);
It is worth noting that the part matching the image src does use the entire contents of src, so if you move the images to a different directory it will stop working. If you just want to match the end of source you can use $= instead of just =.
Here's a variation on TheVillageIdiots rewrite of your copy left function.
function copyToLeft() {
var cell = $(this).closest('td');
var leftObj = cell.prev();
var rightObj = cell.next();
if ( leftObj.html() != rightObj.html()) {
leftObj.html(rightObj.html());
leftObj.css('background-color','#1DD50F');
} else {
alert ( 'Both values are same' );
}
}
Part of me also thinks it would make sense to just have one copyToSibling function where you check $(this).attr('src') for whether it's left_arrow.gif or right_arrow.gif and act accordingly, rather than the two selectors I posted before.
try this code:
<table id="tbl">
<tbody>
<tr>
<td></td><td><img src="file:///...\delete.png" /></td>
</tr>
<tr>
<td></td><td><img src="file:///...\ok.png" /></td>
</tr>
</tbody>
</table>
<script type="text/javascript">
$(document).ready(function(){
$("table#tbl img").click(function(){
var td=$(this).parents("td");
var tr=$(td).parents("tr");
var left=$(td).prev("td");
$(left).html($(td).html());
});
});
</script>

jQuery to find all previous elements that match an expression

Using jQuery, how do you match elements that are prior to the current element in the DOM tree? Using prevAll() only matches previous siblings.
eg:
<table>
<tr>
<td class="findme">find this one</td>
</tr>
<tr>
<td>find the previous .findme</td>
</tr>
<tr>
<td class="findme">don't find this one</td>
</tr>
</table>
In my specific case, I'll be searching for the first .findme element prior to the link clicked.
Ok, here's what I've come up with - hopefully it'll be useful in many different situations. It's 2 extensions to jQuery that I call prevALL and nextALL. While the standard prevAll() matches previous siblings, prevALL() matches ALL previous elements all the way up the DOM tree, similarly for nextAll() and nextALL().
I'll try to explain it in the comments below:
// this is a small helper extension i stole from
// http://www.texotela.co.uk/code/jquery/reverse/
// it merely reverses the order of a jQuery set.
$.fn.reverse = function() {
return this.pushStack(this.get().reverse(), arguments);
};
// create two new functions: prevALL and nextALL. they're very similar, hence this style.
$.each( ['prev', 'next'], function(unusedIndex, name) {
$.fn[ name + 'ALL' ] = function(matchExpr) {
// get all the elements in the body, including the body.
var $all = $('body').find('*').andSelf();
// slice the $all object according to which way we're looking
$all = (name == 'prev')
? $all.slice(0, $all.index(this)).reverse()
: $all.slice($all.index(this) + 1)
;
// filter the matches if specified
if (matchExpr) $all = $all.filter(matchExpr);
return $all;
};
});
usage:
$('.myLinks').click(function() {
$(this)
.prevALL('.findme:first')
.html("You found me!")
;
// set previous nodes to blue
$(this).prevALL().css('backgroundColor', 'blue');
// set following nodes to red
$(this).nextALL().css('backgroundColor', 'red');
});
edit - function rewritten from scratch. I just thought of a much quicker and simpler way to do it. Take a look at the edit history to see my first iteration.
edit again - found an easier way to do it!
Presumably you are doing this inside an onclick handler so you have access to the element that was clicked. What I would do is do a prevAll to see if it is at the same level. If not, then I would do a parent().prevAll() to get the previous siblings of the parent element, then iterate through those backwards, checking their contents for the desired element. Continue going up the DOM tree until you find what you want or hit the root of the DOM. This a general algorithm.
If you know that it is inside a table, then you can simply get the row containing the element clicked and iterate backwards through the rows of the table from that row until you find one that contains the element desired.
I don't think there is a way to do it in one (chained) statement.
edit: this solution works for both your original problem, the problem you mention in your first comment, and the problem you detail in the comment after that.
$('.myLinks').click(function() {
var findMe = '';
$(this).parents().each(function() {
var a = $(this).find('.findme').is('.findme');
var b = $(this).find('.myLinks').is('.myLinks');
if (a && b) { // look for first parent that
// contains .findme and .myLinks
$(this).find('*').each(function() {
var name = $(this).attr('class');
if ( name == 'findme') {
findMe = $(this); // set element to last matching
// .findme
}
if ( name == 'myLinks') {
return false; // exit from the mess once we find
// .myLinks
}
});
return false;
}
});
alert(findMe.text() ); // alerts "find this one"
});
this works for your example in the OP as well as a modified example as explained in the comments:
<table>
<tr>
<td class="findme">don't find this one</td>
</tr>
<tr>
<td class="findme">find this one</td>
</tr>
<tr>
<td>find the previous .findme</td>
</tr>
<tr>
<td class="findme">don't find this one</td>
</tr>
</table>
as well as this test case which you added:
<table>
<tr>
<td class="findme">don't find this one</td>
</tr>
<tr>
<td class="findme">don't find this one</td>
</tr>
<tr>
<td class="findme">find this one</td>
</tr>
</table>
find the previous .findme
I usually number elements (1,2,3..) (rel="number"), so then i use this code to give class to all previous elements:
var num = $(this).attr("rel");
for (var i = 1; i<=num; i++)
{
$('.class[rel="'+i+'"]').addClass("newclass");
}
had the same problem, heres what i came up with. my function uses compareDocumentPosition. dont know how it compares to the other solutions in terms of performance though.
$.fn.findNext = function ( selector ) {
var found, self = this.get(0);
$( selector )
.each( function () {
if ( self.compareDocumentPosition( this ) === 4 ){
found = this;
return false;
}
})
return $(found);
}
of course one could change this quite easily to fetch ALL elements following the calling element.
$.fn.nextALL= function ( selector ) {
var found = [], self = this.get(0);
$( selector )
.each( function () {
if ( self.compareDocumentPosition( this ) === 4 )
found.push(this);
})
return $(found);
}
EDIT: streamlined version
$.fn.findNext = function( s ){
var m = this[0], f=function(n){return m.compareDocumentPosition(n)===4;};
return this.pushStack( $(s).get().filter(f) );
}

Categories

Resources