Fetch the data of a particular <td> in a table - javascript

I need to get the data of an particular <td>, but I don't have any id or name for that particular <td>. How do you get the contents of that <td>?
For example:
<table>
<tr><td>name</td><td>praveen</td></tr>
<tr><td>designation</td><td>software engineer</td></tr>
</table>
Is it possible to get the value "designation" from this table.. I need to extract the word "software engineer" using javascript.

I prefer to use jQuery to do all the heavy lifting for this sort of task.
For example, the following function will return the text of the next element of the same type that you're searching for:
function GetNextChildText(tagToFind, valueToFind) {
var nextText = "";
$(tagToFind).each(function(i) {
if ($(this).text() == valueToFind) {
if ($(this).next() != null && $(this).next() != undefined) {
nextText = $(this).next().text();
}
}
});
return (nextText);
}
So, for your example table, your call to return the designation could be:
var designationText = GetNextChildText('td', 'designation');
And the result is that the variable designationText would contain the value 'software engineer'.

A quick solution:
function GetTdContent(label)
{
var TDs = document.getElementsByTagName("TD");
var foundFlag = false;
for (i = 0; i < TDs.length; i++)
{
if (foundFlag) return TDs[i].innerHTML;
foundFlag = TDs[i].innerHTML.toLower() == label.toLower();
}
}
elsewhere call:
var value = GetTdContent("designation");
Explanation:
The function iterates all TDs in the document. If it finds one with the given label, say "designation", it loops one more time and returns the next TD content.
This makes a few assumptions about your source HTML. If you know your data though, it can be enough.

Something along the line of:
(not tested, just quick code to give an idea)
var tables = document.getElementById('TABLE'); // instead of document.all.tag
var rows;
var cells;
var maxCells = 1;
var designation;
if (tables) {
for (var t=0; t<tables.length; t++) {
rows = tables[t].all.tags('TR');
if (tables[t].all.tags('TABLE').length == 0) {
for (var r=0; r<rows.length; r++) {
if (rows[r].innerText != '') {
cells = rows[r].all.tags('TD');
for (var c=0; c<cells.length; c++) {
if (cells[c].innerText == 'designation' && c<(cells.length-1)) {
designation = cells[c+1].innerText;
}
}
}
}
}
}
}
Since document.all is IE specific, you should rather user getElementById, with the following to redefine that function for IE:
if (/msie/i.test (navigator.userAgent)) //only override IE
{
document.nativeGetElementById = document.getElementById;
document.getElementById = function(id)
{
var elem = document.nativeGetElementById(id);
if(elem)
{
//make sure that it is a valid match on id
if(elem.attributes['id'].value == id)
{
return elem;
}
else
{
//otherwise find the correct element
for(var i=1;i<document.all[id].length;i++)
{
if(document.all[id][i].attributes['id'].value == id)
{
return document.all[id][i];
}
}
}
}
return null;
};
}

Use XPath (tutorial here, including instructions for IE and other browsers: http://www.w3schools.com/XPath/xpath_examples.asp)
The xpath for your example is
//table/tr/td[text()="designation"]/following::td
("the td that follows the td with text "designation" that's in a tr that's in a table somewhere in the document")
Simpler Xpaths are possible - if it's the only table cell that could contain 'designation' you could use
//td[text()="designation"]/following::td
One issue with writing code to do the particular search is that it needs changing, possibly significantly, if the structure of your page changes. The Xpath may not need to change at all, and if it does, it's only one line.

Tomalak's a little quicker:
<script type="text/javascript">
function getText(tText){
var tds = document.getElementsByTagName("td");
for(var i=0, im=tds.length; im>i; i++){
if(tds[i].firstChild.nodeValue == tText)
return tds[i].nextSibling.firstChild.nodeValue;
}
}
</script>

Related

How to implement text search in html and get the xpath of the DOM element containing the text?

My webpage has a set of divs overlapping each other and but with different z-index values. I am trying to implement a text search functionality so that when the text is found in a div, I can bring that div to the foreground. Something like flipping the pages of a book to the page containing the text.
One way to do this would be to loop through the innerHTMLs of all the text DOM elements in each div and then using the xpath of each resultant element to determine which div it belongs to and bringing that div to the foreground. But it would be highly inefficient.
Rather I am wondering if there is some way to delegate this task to the browser itself. I tried text searching in Chrome, firefox, safari etc.. They found the text even in the hidden divs. Is there any way I can refer to those search results to determine the xpath of the results?
Basically do browsers provide us with any APIs that can help me?
Thanx in advance!!
Note: I am looking for a pure javascript solution or at best I can use google closure.
I rustled up an approach I would take to doing a text search.
The basic idea would be that you do the searching without touching the dom, therefore saving quite a bit of processing overhead.
http://jsfiddle.net/j84aX/2/
Basically this is what my example does:
//Data comes in the form of an array of objects.
var results = [{
id: Math.floor(Math.random() * 10000),
title: 'Some sort of title'
}, {
id: Math.floor(Math.random() * 10000),
title: 'Some other item'
}, {
id: Math.floor(Math.random() * 10000),
title: 'Maybe a thingy'
}]
// Templates are created and references attached to the original objects
// Templates are attached to the dom
renderResults = function() {
var i = 0,
len = results.length,
item;
for (; i < len; i++) {
item = $('<li data-id="'+ results[i].id +'">'+ results[i].title +'</li>');
results[i].item = item;
item.appendTo(resultsEl);
}
$('body').append(resultsEl);
}
// A user searches and a lookup is done in the result set
$('.search input').on('keyup', function() {
});
// Any matches already have the item on their object and do not need to do a dom lookup
// Matches are highlighted using a class name
for (; i < len; i++) {
if (results[i].title.toLowerCase().indexOf(val) !== -1) {
results[i].item.addClass('match')
}
}
You can use jQuery plugin: Search engine:
$('div').searchengine(myText,'html',false);
All divs selected
myText : text that I search on Divs
'html' : Using html method, it can be 'val' method if its input or 'text' method if its pre ,...etc
Adding this plugin after loading jQuery
$.fn.searchengine = function(text, action, casesensitive, exactText) {
var arrayJq = []
var all = $(this);
all.each(function() {
var html = $(this).html();
var vll = $(this).val();
var ttx = $(this).text();
if (casesensitive === false) {
html = html.toLocaleLowerCase();
vll = vll.toLocaleLowerCase();
ttx = ttx.toLocaleLowerCase();
text = text.toLocaleLowerCase();
}
var test;
if (exactText = true) {
test = (html === text) || (vll === text) || (ttx === text)
} else {
test = (html.indexOf(text) !== -1)
|| (vll.indexOf(text) !== -1)
|| (ttx.indexOf(text) !== -1)
}
if (test === true) {
arrayJq.push($(this));
if (action.length !== 0) {
$(this)[action]();
}
}
})
return arrayJq;
};
Update:
To get th xpath, you can use fullselector jQuery plugin :
$('div#myDiv').fullselector();
known that you must add this code after jQuery loading :
$.fn.id=function(){
var arr=[];
if($(this).length>0){
arr.push($(this).get(0).tagName.toLocaleLowerCase());
$(this).parents().each(function(i,e){
var ind=$(e).index();
ind='['+ind+']';
arr.push($(e).get(0).tagName.toLocaleLowerCase()+ind)});
arr.reverse();
}
return arr.join('//');
};
/**
* Full jquery selector of element
* #depend of $.id() jquery plugi
*/
$.fn.fullselector=function(){
var arr=[];
if($(this).get(0)){
arr.push($(this).get(0).tagName.toLocaleLowerCase());
}
$(this).parents().each(function(i,e){
//var ind=mthpathid.indexChildsametag($(e).parent(),$(e))
var ind=$(e).index();
ind=':eq('+ind+')';
if($(e).get(0).tagName.toLocaleLowerCase()=='html'){
arr.push($(e).get(0).tagName.toLocaleLowerCase()+ind)
}else{
arr.push(ind)
}
});arr.reverse();
return arr.join('>');
};
var mthpathid={
indexChildsametag:function(jqparent,jqchild){
var j=-1;
jqparent.children(jqchild.get(0).tagName.toLocaleLowerCase()).each(function(i,e){
if($(e).id()===jqchild.id()){
j=i;
}
});
return j;
}
};

How to remove all jQuery validation engine classes from element?

I have a plugin that is cloning an input that may or may not have the jQuery validation engine bound to it.
so, it's classes may contain e.g. validate[required,custom[number],min[0.00],max[99999.99]] or any combination of the jQuery validation engine validators.
The only for sure thing is that the class begins with validate[ and ends with ], but to make it more complicated as in the example above, there can be nested sets of [].
So, my question is, how can I remove these classes (without knowing the full class) using jQuery?
Here is my implementation, It's not using regex, but meh, who said it had too?
//'first validate[ required, custom[number], min[0.00], max[99999.99] ] other another';
var testString = $('#test')[0].className;
function removeValidateClasses(classNames) {
var startPosition = classNames.indexOf("validate["),
openCount = 0,
closeCount = 0,
endPosition = 0;
if (startPosition === -1) {
return;
}
var stringStart = classNames.substring(0, startPosition),
remainingString = classNames.substring(startPosition),
remainingSplit = remainingString.split('');
for (var i = 0; i < remainingString.length; i++) {
endPosition++;
if (remainingString[i] === '[') {
openCount++;
} else if (remainingString[i] === ']') {
closeCount++;
if (openCount === closeCount) {
break;
}
}
}
//concat the strings, without the validation part
//replace any multi-spaces with a single space
//trim any start and end spaces
return (stringStart + remainingString.substring(endPosition))
.replace(/\s\s+/g, ' ')
.replace(/^\s+|\s+$/g, '');
}
$('#test')[0].className = removeValidateClasses(testString);
It might actually be simpler to do that without JQuery. Using the className attribute, you can then get the list of classes using split(), and check whether the class contains "validate[".
var classes = $('#test')[0].className.split(' ');
var newClasses = "";
for(var i = 0; i < classes.length; i++){
if(classes[i].indexOf('validate[') === -1){
newClasses += classes[i];
}
}
$('#test')[0].className = newClasses
I think this solution is even more simple. You just have to replace field_id with the id of that element and if the element has classes like some_class different_class validate[...] it will only remove the class with validate, leaving the others behind.
var that ='#"+field_id+"';
var classes = $(that).attr('class').split(' ');
$.each(classes, function(index, thisClass){
if (thisClass.indexOf('validate') !== -1) {
$(that).removeClass(classes[index])
}
});

get the next element with a specific class after a specific element

I have a HTML markup like this:
<p>
<label>Arrive</label>
<input id="from-date1" class="from-date calender" type="text" />
</p>
<p>
<label>Depart</label>
<input id="to-date1" class="to-date calender" type="text" />
</p>
<p>
<label>Arrive</label>
<input id="from-date2" class="from-date calender" type="text" />
</p>
<p>
<label>Depart</label>
<input id="to-date2" class="to-date calender" type="text" />
</p>
I want to get the next element after from dates to get the corresponding to date. (Layout is a little more complex but from date has from-date class and to date has to-date class).
This is I am trying to do, I want to take a from date element and find the next element in the dom with to-date class. I tried this:
$('#from-date1').next('.to-date')
but it is giving me empty jQuery element. I think this is because next gives the next sibling matching the selector. How can I get the corresponding to-date?
Couldn't find a direct way of doing this, so wrote a little recursive algorithm for this.
Demo: http://jsfiddle.net/sHGDP/
nextInDOM() function takes 2 arguments namely the element to start looking from and the selector to match.
instead of
$('#from-date1').next('.to-date')
you can use:
nextInDOM('.to-date', $('#from-date1'))
Code
function nextInDOM(_selector, _subject) {
var next = getNext(_subject);
while(next.length != 0) {
var found = searchFor(_selector, next);
if(found != null) return found;
next = getNext(next);
}
return null;
}
function getNext(_subject) {
if(_subject.next().length > 0) return _subject.next();
return getNext(_subject.parent());
}
function searchFor(_selector, _subject) {
if(_subject.is(_selector)) return _subject;
else {
var found = null;
_subject.children().each(function() {
found = searchFor(_selector, $(this));
if(found != null) return false;
});
return found;
}
return null; // will/should never get here
}
.next('.to-date') does not return anything, because you have an additional p in between
You need .parent().next().find('.to-date').
You might have to adjust this if your dom is more complicated than your example. But essentially it boils down to something like this:
$(".from-date").each(function(){
// for each "from-date" input
console.log($(this));
// find the according "to-date" input
console.log($(this).parent().next().find(".to-date"));
});
edit: It's much better and faster to just look for the ID. The following code searches all from-dates and gets the according to-dates:
function getDeparture(el){
var toId = "#to-date"+el.attr("id").replace("from-date","");
//do something with the value here
console.log($(toId).val());
}
var id = "#from-date",
i = 0;
while($(id+(++i)).length){
getDeparture($(id+i));
}
Take a look at the example.
try
var flag = false;
var requiredElement = null;
$.each($("*"),function(i,obj){
if(!flag){
if($(obj).attr("id")=="from-date1"){
flag = true;
}
}
else{
if($(obj).hasClass("to-date")){
requiredElement = obj;
return false;
}
}
});
var item_html = document.getElementById('from-date1');
var str_number = item_html.attributes.getNamedItem("id").value;
// Get id's value.
var data_number = showIntFromString(str_number);
// Get to-date this class
// Select by JQ. $('.to-date'+data_number)
console.log('to-date'+data_number);
function showIntFromString(text){
var num_g = text.match(/\d+/);
if(num_g != null){
console.log("Your number:"+num_g[0]);
var num = num_g[0];
return num;
}else{
return;
}
}
Use JS. to get the key number from your id. Analysis it than output the number. Use JQ. selecter combine string with you want than + this number. Hope this can help you too.
I know this is an old question, but I figured I'd add a jQuery free alternate solution :)
I tried to keep the code simple by avoiding traversing the DOM.
let inputArray = document.querySelectorAll(".calender");
function nextInput(currentInput, inputClass) {
for (i = 0; i < inputArray.length - 1; i++) {
if(currentInput == inputArray[i]) {
for (j = 1; j < inputArray.length - i; j++) {
//Check if the next element exists and if it has the desired class
if(inputArray[i + j] && (inputArray[i + j].className == inputClass)) {
return inputArray[i + j];
break;
}
}
}
}
}
let currentInput = document.getElementById('from-date1');
console.log(nextInput(currentInput, 'to-date calender'));
If you know that the to date will always be the next input element with a class of "calender", then you don't need the second loop.

Need help finding/traversing dom in Javascript

I'm sure this is a redundant question, but I've looked for an hour or so and come up empty-handed so was hoping someone could help...
Looking for a way to use JS (not jquery) to return the class of the li below when searching for 'Chicken' (or whatever the value is).
<li class='113252'>
<span>Chicken</span>
</li>
So was hoping the javascript would return the li class when given the span value (in this case Chicken).
Thanks!
Try this:
var spanArray = document.getElementsByTagName('span');
for (var i=0; i<spanArray.length; i++) {
if(spanArray[i].innerHTML.toUpperCase() === 'CHICKEN')
{
alert(spanArray[i].parentNode.className);
break;
}
}
Now, I'm more familiar with jQuery but seems to work in the fiddle linked here: http://jsfiddle.net/FranWahl/fCzYc/2/ (Updated to include suggested break; after match)
You can add more type checking for the parentNode to ensure it is an li and so on, but this should get you started.
Also, I'm not sure at all how efficient this is in a big document.
Edit
Having read through some comments I have updated my code above to include the break as suggested by ajax333221.
Dennis mentioned that it would be better to call getElementByTagName on the ul.
Given you can have an li without a ul I added it here as separate code as I'm not sure if the OP has ul tags.
Code querying against each ul (jsFiddle here)
var ulArray = document.getElementsByTagName('ul');
var parentFound = false;
for (var i = 0; i < ulArray.length; i++) {
var spanArray = ulArray[i].getElementsByTagName('span');
for (var i = 0; i < spanArray.length; i++) {
if (spanArray[i].innerHTML.toUpperCase() === 'CHICKEN') {
alert(spanArray[i].parentNode.className);
parentFound = true;
break;
}
}
if(parentFound)
{
break;
}
}​
This is by no means fully complete, which is why you should seek a library, but it does two things:
recursively traverse child elements (starting with the document's BODY), to find the supplied text
recursively traverse the parent element to find the supplied parent element tag, once found it will return the class of that parent
JSFiddle: http://jsfiddle.net/vol7ron/bttQN/
function getParentClass(element, parentTag){
if (element.tagName == parentTag.toUpperCase())
return element.className;
return getParentClass(element.parentNode,parentTag);
}
window.findParentClass = function (text,tagName){
var elements = document.getElementsByTagName('body');
for (var n=elements.length; n--;){
var foundClass = (function searchNextChild(el){
if (!el.children.length) {
if (el.textContent == text)
return getParentClass(el,tagName);
return;
}
for (var i=0, n=el.children.length; i<n; i++)
return searchNextChild(el.children[i]);
})(elements[n]);
return foundClass;
}
};
Example Call:
alert( findParentClass('Chicken','li') );

javascript not removing undefined objects from array

I've got an in page text search using JS, which is here:
$.fn.eoTextSearch = function(pat) {
var out = []
var textNodes = function(n) {
if (!window['Node']) {
window.Node = new Object();
Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;
}
if (n.nodeType == Node.TEXT_NODE) {
var t = typeof pat == 'string' ?
n.nodeValue.indexOf(pat) != -1 :
pat.test(n.nodeValue);
if (t) {
out.push(n.parentNode)
}
}
else {
$.each(n.childNodes, function(a, b) {
textNodes(b)
})
}
}
this.each(function() {
textNodes(this)
})
return out
};
And I've got the ability to hide columns and rows in a table. When I submit a search and get the highlighted results, there would be in this case, the array length of the text nodes found would be 6, but there would only be 3 highlighted on the page. When you output the array to the console you get this:
So you get the 3 tags which I was expecting, but you see that the array is actually consisting of a [span,undefined,span,undefined,undefined,span]. Thus giving me the length of 6.
<span>
<span>
<span>
[span, undefined, span, undefined, undefined, span]
I don't know why it's not stripping out all of the undefined text nodes when I do the check for them. Here's what I've got for the function.
performTextSearch = function(currentObj){
if($.trim(currentObj.val()).length > 0){
var n = $("body").eoTextSearch($.trim(currentObj.val())),
recordTitle = "matches",
arrayRecheck = new Array(),
genericElemArray = new Array()
if(n.length == 1){
recordTitle = "match"
}
//check to see if we need to do a recount on the array length.
//if it's more than 0, then they're doing a compare and we need to strip out all of the text nodes that don't have a visible parent.
if($(".rows:checked").length > 0){
$.each(n,function(i,currElem){
if($(currElem).length != 0 && typeof currElem != 'undefined'){
if($(currElem).closest("tr").is(":visible") || $(currElem).is(":visible")){
//remove the element from the array
console.log(currElem)
arrayRecheck[i] = currElem
}
}
})
}
if(arrayRecheck.length > 0){
genericElemArray.push(arrayRecheck)
console.log(arrayRecheck)
}
else{
genericElemArray.push(n)
}
genericElemArray = genericElemArray[0]
$("#recordCount").text(genericElemArray.length + " " +recordTitle)
$(".searchResults").show()
for(var i = 0; i < genericElemArray.length; ++i){
void($(genericElemArray[i]).addClass("yellowBkgd").addClass("highLighted"))
}
}
else{
$(".highLighted").css("background","none")
}
}
If you look at the code below "//check to see if we need to do a recount on the array length. ", you'll see where I'm stripping out the text nodes based off of the display and whether or not the object is defined. I'm checking the length instead of undefined because the typeof == undefined wasn't working at all for some reason. Apparently, things are still slipping by though.
Any idea why I'm still getting undefined objects in the array?
My apologies for such a big post!
Thanks in advance
I've modified your eoTextSearch() function to remove dependencies on global variables in exchange for closures:
$.fn.extend({
// helper function
// recurses into a DOM object and calls a custom function for every descendant
eachDescendant: function (callback) {
for (var i=0, j=this.length; i<j; i++) {
callback.call(this[i]);
$.fn.eachDescendant.call(this[i].childNodes, callback);
}
return this;
},
// your text search function, revised
eoTextSearch: function () {
var text = document.createTextNode("test").textContent
? "textContent" : "innerText";
// the "matches" function uses an out param instead of a return value
var matches = function (pat, outArray) {
var isRe = typeof pat.test == "function";
return function() {
if (this.nodeType != 3) return; // ...text nodes only
if (isRe && pat.test(this[text]) || this[text].indexOf(pat) > -1) {
outArray.push(this.parentNode);
}
}
};
// this is the function that will *actually* become eoTextSearch()
return function (stringOrPattern) {
var result = $(); // start with an empty jQuery object
this.eachDescendant( matches(stringOrPattern, result) );
return result;
}
}() // <- instant calling is important here
});
And then you can do something like this:
$("body").eoTextSearch("foo").filter(function () {
return $(this).closest("tr").is(":visible");
});
To remove unwanted elements from the search result. No "recounting the array length" necessary. Or you use each() directly and decide within what to do.
I cannot entirely get my head around your code, but the most likely issue is that you are removing items from the array, but not shrinking the array afterwards. Simply removing items will return you "undefined", and will not collapse the array.
I would suggest that you do one of the following:
Copy the array to a new array, but only copying those items that are not undefined
Only use those array items that are not undefined.
I hope this is something of a help.
Found the answer in another post.
Remove empty elements from an array in Javascript
Ended up using the answer's second option and it worked alright.

Categories

Resources