Get the first number value from a descendant inside a table - javascript

I'm trying to a way to get the first number value present inside a table (and respective tbody), but it needs to be able to find the value the first number, and ignores all the tags it comes accross until it reaches the number value.
<table id="TableID">
<thead></thead>
<tbody>
<tr></tr>
<tr>
<td></td>
<td>
<div>
<div>
<span>
4031007
</span>
</div>
<div>
<span>
whatever
</span>
</div>
</div>
</td>
<td></td>
</tr>
</tbody>
</table>
in the above example, we would try to find 4031007, which is inside a <span>, but it could've been a <div> or something else. I need this without using JQuery. Any help?

You could do it the plain old way: make a recursive function that will return text of the first node which has a text content:
function findFirstNumber(node) {
// If this is a text node, return its contents. Trim it because there is
// whitespace between the elements that should be ignored
if (node.nodeType == Node.TEXT_NODE)
return node.textContent.trim();
// Iterate over all child nodes and finde the first one that has text in it
for (var child = node.firstChild; child; child = child.nextSibling) {
var content = firstText(child);
if (content && isNumber(content))
return content;
}
// No text found
return '';
}
function isNumber(value) {
return !!isNaN(value);
}
console.log(findFirstNumber(document.getElementById('TableID')));
I used the mdn page about Node to find out how to do this.
see fiddle (open your console)

How about a fancy find function that accepts regular expressions.
function findRegExp(start, reg, mod) {
if (! (reg && start)) return this;
return [].slice.call(start.querySelectorAll('*')).filter(function(elem) {
if (typeof reg == 'string')
reg = new RegExp(reg, mod ? mod : '');
var clone = elem.cloneNode(true),
child = clone.children;
for (var i=child.length; i--;)
clone.removeChild(child[i]);
var txt = clone.textContent.trim();
return reg.test(txt);
});
}
to be used like
var elems = findRegExp(document.getElementById('TableID'), /^\d+$/);
FIDDLE
and the jQuery version
$.fn.findRegExp = function(reg, mod) {
if (!reg) return this;
return this.find('*').addBack().filter(function() {
if (typeof reg == 'string')
reg = new RegExp(reg, mod ? mod : '');
var c = $(this).clone();
c.children().remove();
var txt = $.trim(c.text());
return reg.test(txt);
});
}
Then you can search for an element containing only numbers
$('#TableID').findRegExp(/^\d+$/);
FIDDLE

Related

How to get a list of <a> href found in an element?

Given an html like this:
<tr>
<th style="padding-right:1em">Location</th>
<td>
<span class="location">Lower reaches of the Geum River, Korea</span>
</td>
</tr>
How do I get Geum_River and Korea?
This is what I am doing at the moment:
countryLinks = doSelect("Location").siblings('td').find('a').attr('href');
function doSelect(text) {
return $wikiDOM.find(".infobox th").filter(function() {
return $(this).text() === text;
});
}
function countryList() {
let pattern = new RegExp('\/wiki\/');
string = countryLinks;
countryListLinks = string.replace(pattern, '');
console.log(countryListLinks);
}
if (doSelect('Location').length > 0 && doSelect('Date').length > 0) {
countryList();
};
I am splitting /wiki/ from the string and it works but I am only getting the first one Geum_River while I would expect all of the <a>s href.
You were only selecting first <a> element .href, .attr() returns a single value. Also second condition at if && doSelect('Date').length > 0 is false given HTML at Question.
You can use .map() and .get() to return an array of <a> element .href values, then pass countryList function to Array.prototype.forEach() to iterate .href values.
The RegExp should also be adjusted to replace all characters up to and including "wiki" '^.+\/wiki\/'.
function doSelect(text) {
return $(".infobox th").filter(function() {
return $(this).text() === text;
});
}
countryLinks = doSelect("Location").siblings('td')
.find('a').map(function(i, el) {return el.href}).get(); // .attr('href');
// we can pass this function to `.forEach()` or `.map()`
function countryList(string) {
let pattern = new RegExp('^.+\/wiki\/'); // adjust `RegExp`
// string = countryLinks;
countryListLinks = string.replace(pattern, '');
console.log(countryListLinks);
}
// the second condition is `false` given HTML at Question
if (doSelect('Location').length > 0 /* && doSelect('Date').length > 0 */) {
countryLinks.forEach(countryList);
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="infobox">
<tr>
<th style="padding-right:1em">Location</th>
<td>
<span class="location">Lower reaches of the Geum River, Korea</span>
</td>
</tr>
</table>
Your main issue is when you call countryLinks = doSelect("Location").siblings('td').find('a').attr('href'); specifically, when you call the last bit .attr('href'); which the docs state this of
Description: Get the value of an attribute for the first element in the set of matched elements.
So basically, you're getting a collection of the links then reducing that collection to just the first element and return it's href attribute.
Here is how I would do this using .map() instead:
var $wikiDOM = $('.some-container');
var links = $.map($wikiDOM.find('td a'),function(link, i){
return (link.href || '').replace(/^.*[\\\/]/, '');
});
console.log(links);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="some-container">
<table>
<tr>
<th style="padding-right:1em">Location</th>
<td>
<span class="location">Lower reaches of the Geum River, Korea</span>
</td>
</tr>
</table>
</div>
you can use jQuery each() to get all the hrefs in a array and then display then one by one using for loop.
Here is the code:
var hrefs = new Array();
jQuery('.location').find('a').each(function() {
hrefs.push(jQuery(this).attr('href'));
});
function countryList() {
let pattern = new RegExp('\/wiki\/');
for(var i=0; i < hrefs.length ; i++){
string = hrefs[i];
var countryListLinks = string.replace(pattern, '');
alert(countryListLinks);
}
}
countryList();
Complete Code, should look somthing like this:
function doSelect(text) {
return $wikiDOM.find(".infobox th").filter(function() {
return $(this).text() === text;
});
}
var hrefs = new Array();
jQuery('.location').find('a').each(function() {
hrefs.push(jQuery(this).attr('href'));
});
function countryList() {
let pattern = new RegExp('\/wiki\/');
for(var i=0; i < hrefs.length ; i++){
string = hrefs[i];
var countryListLinks = string.replace(pattern, '');
console.log(countryListLinks);
}
}
if (doSelect('Location').length > 0 && doSelect('Date').length > 0) {
countryList();
};
var hrefs = new Array();
jQuery('.location').find('a').each(function() {
hrefs.push(jQuery(this).attr('href'));
});
function countryList() {
let pattern = new RegExp('\/wiki\/');
for(var i=0; i < hrefs.length ; i++){
string = hrefs[i];
var countryListLinks = string.replace(pattern, '');
alert(countryListLinks);
}
}
countryList();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<tr>
<th style="padding-right:1em">Location</th>
<td>
<span class="location">Lower reaches of the Geum River, Korea</span>
</td>
</tr>
First of all, I'm going to pop an id onto your span element so that I can locate it easily in my script.
<span id="locations"
Next, I'm going to drop your implementation, and instead, iterate through the child elements of the span with id="locations". Next, I'll get the substring of the href of these elements that we want, and push them to an array.
var locations = document.getElementById("locations").getElementsByTagName('a');
var rawLocations = [];
for (i in locations) {
if (locations[i].href) {
var lastIndex = locations[i].href.lastIndexOf('/') + 1;
rawLocations.push(locations[i].href.substring(lastIndex));
}
}
Now, console.log(rawLocations); gives us what we want:
(2) ["Geum_River", "Korea"]
This was not as easy as I thought:-)
But here we go:
// cache the element, you could do QuerySelectorAll and iterate through the
// node list, but let's keep it simple
var ts = document.querySelector('a');
// This is caching the href attribute from the link
var garbage = ts.href;
// here you take the link and run the native string method
// to find the lastIndexOf, this is great if you suck at regex like myself
var n = garbage.lastIndexOf('/');
Here we extract only the part after lastIndexOf and cache it in "result"
var result = garbage.substring(n + 1);
alert(result);

Jquery add tag inside a blockquote before a tag

I have this text inside a blockquote:
<blockquote class="tr_bq">
4 Text<br />
20 TExt<br />
2 Another text a little longer<br />
<br />
20 text</blockquote>
I want to add for each line a tag or convert the br to include a class. if the br was including all the line i would know how to do it. This is how i want to end like:
<blockquote class="tr_bq">
<strike>4 Text</strike><br/>
<strike>20 TExt</strike><br/>
<strike>2 Another text a little longer</strike><br/>
<br />
<strike>20 text</strike></blockquote>
or
<blockquote class="tr_bq">
<br class="X">4 Text<br>
<br class="X">20 TExt<br>
<br class="X">2 Another text a little longer<br>
<br />
<br class="X"> 20 text</br></blockquote>
I've tried with wrap but with no sucess, any way to do this?
You can do this by manipulating the inner HTML of the blockquote.
$('.tr_bq').each(function () {
var html = $(this).html();
var newHtml = html.split('<br>').map(function (str) {
return '<strike>' + str + '</strike>';
}).join('<br>');
$(this).html(newHtml);
});
Just to offer a plain-JavaScript means of achieving this, avoiding the (unnecessary) use of a library:
function wrapNodesWith(nodes, tag) {
// if we have neither nodes to wrap, nor a tag to wrap
// them with, we quit here:
if (!nodes || !tag) {
return false;
}
// otherwise:
// we convert the nodes to an array (using Array.prototype.slice,
// in conjunction with Function.prototype.call):
nodes = Array.prototype.slice.call(nodes, 0);
// if the tag parameter passed to the function is a string ('strike'),
// we create that element using document.createElement(tag),
// otherwise we assume we've got an HTMLElement (this is a very
// naive check) and so we use that:
tag = 'string' === typeof tag ? document.createElement(tag) : tag;
// an unitialised variable for use within the following forEach:
var clone;
nodes.forEach(function(n) {
// n is the node over which we're iterating,
// cloning the tag (to avoid multiple calls
// to document.createElement):
clone = tag.cloneNode();
// setting the textContent of the clone to the nodeValue
// of the node (if it's a textNode), or to the textContent of
// element (again a simple check):
clone.textContent = n.nodeType === 3 ? n.nodeValue : n.textContent;
// replacing the childNode, using parentNode.replaceChild(),
// inserting clone and removing n:
n.parentNode.replaceChild(clone, n);
});
}
// finding the first <blockquote> element:
var blockquote = document.querySelector('blockquote'),
// creating an array of the childNodes of the <blockquote>:
children = Array.prototype.slice.call(blockquote.childNodes, 0),
// filtering the children array, retaining only those nodes for
// which the assessment returns true:
textNodes = children.filter(function(n) {
return n.nodeType === 3;
});
// can be called with:
wrapNodesWith(textNodes, 'strike');
// or:
wrapNodesWith(textNodes, document.createElement('strike'));
function wrapNodesWith(nodes, tag) {
if (!nodes || !tag) {
return false;
}
nodes = Array.prototype.slice.call(nodes, 0);
tag = 'string' === typeof tag ? document.createElement(tag) : tag;
var parent, clone;
nodes.forEach(function(n) {
clone = tag.cloneNode();
clone.textContent = n.nodeType === 3 ? n.nodeValue : n.textContent;
n.parentNode.replaceChild(clone, n);
});
}
var blockquote = document.querySelector('blockquote'),
children = Array.prototype.slice.call(blockquote.childNodes, 0),
textNodes = children.filter(function(n) {
return n.nodeType === 3;
});
wrapNodesWith(textNodes, 'strike');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<blockquote class="tr_bq">
4 Text
<br />20 TExt
<br />2 Another text a little longer
<br />
<br />20 text
</blockquote>
References:
Array.prototype.filter().
Array.prototype.forEach().
Array.prototype.slice().
Conditional ('ternary') operator.
document.createElement().
document.querySelector().
Function.prototype.call().
Node.nodeValue.
Node.replaceChild().
Well,
i managed to get it working with this:
var pre = document.getElementsByTagName('blockquote'),pl = pre.length;
for (var i = 0; i < pl; i++) {
var pro = pre[i].innerHTML.split(/<br>/), pz = pro.length;
pre[i].innerHTML = '';
for (var a=0; a < pz ; a++) {
pre[i].innerHTML += '<strike>' + pro[a] + '</strike><br/>';
}
}

jQuery replace all occurrences of a string in an html page

I'm working on a project where I need to replace all occurrences of a string with another string. However, I only want to replace the string if it is text. For example, I want to turn this...
<div id="container">
<h1>Hi</h1>
<h2 class="Hi">Test</h2>
Hi
</div>
into...
<div id="container">
<h1>Hello</h1>
<h2 class="Hi">Test</h2>
Hello
</div>
In that example all of the "Hi"s were turned into "Hello"s except for the "Hi" as the h2 class.
I have tried...
$("#container").html( $("#container").html().replace( /Hi/g, "Hello" ) )
... but that replaces all occurrences of "Hi" in the html as well
This:
$("#container").contents().each(function () {
if (this.nodeType === 3) this.nodeValue = $.trim($(this).text()).replace(/Hi/g, "Hello")
if (this.nodeType === 1) $(this).html( $(this).html().replace(/Hi/g, "Hello") )
})
Produces this:
<div id="container">
<h1>Hello</h1>
<h2 class="Hi">Test</h2>
Hello
</div>
jsFiddle example
Nice results with:
function str_replace_all(string, str_find, str_replace){
try{
return string.replace( new RegExp(str_find, "gi"), str_replace ) ;
} catch(ex){return string;}}
and easier to remember...
replacedstr = str.replace(/needtoreplace/gi, 'replacewith');
needtoreplace should not rounded by '
//Get all text nodes in a given container
//Source: http://stackoverflow.com/a/4399718/560114
function getTextNodesIn(node, includeWhitespaceNodes) {
var textNodes = [], nonWhitespaceMatcher = /\S/;
function getTextNodes(node) {
if (node.nodeType == 3) {
if (includeWhitespaceNodes || nonWhitespaceMatcher.test(node.nodeValue)) {
textNodes.push(node);
}
} else {
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
getTextNodes(node.childNodes[i]);
}
}
}
getTextNodes(node);
return textNodes;
}
var textNodes = getTextNodesIn( $("#container")[0], false );
var i = textNodes.length;
var node;
while (i--) {
node = textNodes[i];
node.textContent = node.textContent.replace(/Hi/g, "Hello");
}
Note that this will also match words where "Hi" is only part of the word, e.g. "Hill". To match the whole word only, use /\bHi\b/g
here you go => http://jsfiddle.net/c3w6X/1/
var children='';
$('#container').children().each(function(){
$(this).html($(this).html().replace(/Hi/g,"Hello")); //change the text of the children
children=children+$(this)[0].outerHTML; //copy the changed child
});
var theText=$('#container').clone().children().remove().end().text(); //get the text outside of the child in the root of the element
$('#container').html(''); //empty the container
$('#container').append(children+theText.replace(/Hi/g,"Hello")); //add the changed text of the root and the changed children to the already emptied element

How to loop till text element in table

Problem
I want to collect all table data using javascript and send back to some part of application.
html
<table>
<tbody>
<tr>
<td>
<span>hello</span>
</td>
<td>
<ul>
<li>1<span>:2000</span></li>
<li>2<span>:3000</span></li>
<li>
<span>link</span>
<span>
<a href=''>failed login</a>
</span>
</li>
</ul>
</td>
</tbody>
</table>
output Expected :
hello 1 :2000 :3000 link failed login
Thanks in advance.
You can use a recursive function like the following. There is no normalisation, you may wish to remove excess whitespace and insert spaces between the text from each element.
// Get the text within an element
// Doesn't do any normalising, returns a string
// of text as found.
function getTextRecursive(element) {
var text = [];
var el, els = element.childNodes;
for (var i=0, iLen=els.length; i<iLen; i++) {
el = els[i];
// May need to add other node types here that you don't want the text of
// Exclude script element content
if (el.nodeType == 1 && el.tagName && el.tagName.toLowerCase() != 'script') {
text.push(getTextRecursive(el));
// If working with XML, add nodeType 4 to get text from CDATA nodes
} else if (el.nodeType == 3) {
// Deal with extra whitespace and returns in text here if required
text.push(el.data);
}
}
// Insert a space between each text string and return
// a single string
return text.join(' ');
}
try this
$(document).ready(function() {
var texts = [];
$("table tr td").each(function(i, elem) {
texts.push($.trim($(elem).text()))
});
var str = texts.join(':').replace(/(\r\n|\n|\r)/gm, "")
alert(str);
});
see working example:-
http://jsfiddle.net/fL28r/84/
thansks

Remove/Delete ":" Colon in an HTML page

I dont have access to the following HTML its displayed dynamically using some External JS.
<td class="quantitybox">
<span class="qty">Quantity</span>
<br style="clear:both;">
:<input type="text" value="1" onkeydown="javascript:QtyEnabledAddToCart();" maxlength="8" size="3" name="QTY.1121309" class="v65-productdetail-cartqty">
</td>
I want that : after to be Removed/Deleted using Jquery, but i am not getting what handler should be used shall i apply a class to <br> dynamically and do something to it
The jQuery way, without regex:
$('td.quantitybox').contents().filter(function(){
return this.nodeType === 3 && // textNode
$.trim(this.nodeValue) === ":";
}).remove();
Or simply change the textnode to an empty string:
$('td.quantitybox').contents().filter(function(){
return this.nodeType === 3 && // textNode
$.trim(this.nodeValue) === ":";
})[0].nodeValue = "";
If you have multiple textnode with colons- : that you want to remove:
$('td.quantitybox').contents().filter(function() {
return this.nodeType === 3 && // textNode
$.trim(this.nodeValue) === ":";
}).each(function() {
this.nodeValue = "";
});​
If you want to do it with regex and aware of it's risks:
$('td.quantitybox').html(function(i, old){
return old.replace(/:\s*</, '<');
});
Note that your HTML code in the question was edited, so I added the white space to the regex so it will work with the initial markup as well....
I'd suggest, though currently untested, the following:
$('td').each(function() {
var i = this.getElementsByTagName('input'),
text = i.previousSibling.nodeValue.replace(/:/g, '');
i.previousSibling.nodeValue = text;
});​
$(document).ready(function(){
var texts = $('input').map(function(){
var text = this.previousSibling.nodeValue;
this.previousSibling.nodeValue = text.replace(/:/g, '');
});
});​
Another solution. Example: http://jsfiddle.net/k25yx/1/
$(document).ready(function() {
// Cache Object
var obj = $('.quantitybox');
obj.each(function(){
var that = $(this);
// remove br
that.find('br').remove();
// trim colon
that.html(function(i, val) {
return val.replace(':', '');
});
})
});

Categories

Resources