getting text from selective nodes in javascript - javascript

From below HTML code I want to get all the text except that in 'text_exposed_hide' span elements.
Initially I tried to get the text from span with no class names.
But this method misses the text which is not within any span but just in div.
How can I get the required text. I need this code in pure javascript
<div id="id_4f1664f84649d2c59795040" class="text_exposed_root">
9jfasiklfsa
<span>CT:PFOUXHAOfuAI07mvPC/</span>
<span>NAg==$1ZUlmHC15dwJX8JNEzKxNDGGT</span>
dwL/L1ubjTndn89JL+M6z
<span class="text_exposed_hide">...</span>
<span class="text_exposed_show">
<span>MDmclkBPI/</span>
<span>s4B7R9hJyU9bE7zT10xkJ8vxIpo0quQ</span>
55
</span>
<span class="text_exposed_hide">
<span class="text_exposed_link">
<a onclick="CSS.addClass($("id_4f1664f84649d2c59795040"), "text_exposed");">See More</a>
</span>
</span>
</div
Edit :
I tried removing nodes with class name 'text_exposed_hidden' and then getting text from remaining nodes. Below is the code. But its not working
Control is not entering for loop. Even visibleDiv.removeChild(textExposedHideNodes[0]) is not working. I am running this in Chrome Browser 16.0
//msg is the parent node for the div
visibleDiv = msg.getElementsByClassName("text_exposed_root");
textExposedHideNodes = visibleDiv.getElementsByClassName("text_exposed_hide");
for(var n = 0;n < textExposedHideNodes.legth ; n++ ) {
console.log("Removing");
msg.removeChild(textExposedHideNodes[n]);
}
return visibleDiv.innerText;

This code will collect all text from text nodes who don't have a parent with the class="text_exposed_hide" and put the results in an array.
It does this non-destructively without removing anything:
function getTextFromChildren(parent, skipClass, results) {
var children = parent.childNodes, item;
var re = new RegExp("\\b" + skipClass + "\\b");
for (var i = 0, len = children.length; i < len; i++) {
item = children[i];
// if text node, collect its text
if (item.nodeType == 3) {
results.push(item.nodeValue);
} else if (!item.className || !item.className.match(re)) {
// if it doesn't have a className or it doesn't match
// what we're skipping, then recurse on it to collect from it's children
getTextFromChildren(item, skipClass, results);
}
}
}
var visibleDiv = document.getElementsByClassName("text_exposed_root");
var text = [];
getTextFromChildren(visibleDiv[0], "text_exposed_hide", text);
alert(text);
If you want all the text in one string, you can concatenate it together with:
text = text.join("");
You can see it work here: http://jsfiddle.net/jfriend00/VynKJ/
Here's how it works:
Create an array to put the results in
Find the root that we're going to start with
Call getTextFromChildren() on that root
Get the children objects of that root
Loop through the children
If we find a text node, collect its text into the results array
If we find an element node that either doesn't have a className or who's className doesn't match the one we're ignoring, then call getTextFromChildren() recursively with that element as the new root to gather all text from within that element

Is this what you're looking for?
/*Get Required Text
Desc: Return an array of the text contents of a container identified by the id param
Params:
id = Container DOM object id
*/
function getRequiredText(id)
{
/*Get container, declare child var and return array*/
var el = document.getElementById(id),
child,
rtn = Array();
/*Iterate through childNodes*/
for(var i = 0; i < el.childNodes.length; i++){
/*Define child*/
child = el.childNodes[i]
/*If node isn't #text and doesn't have hidden class*/
if(child.nodeName !="#text" && child.className != "text_exposed_hide")
rtn.push(child.textContent);
}
/*Return results*/
return rtn;
}
This will go through all childNodes, including nested childNodes and place all text into an array. if you want to exclude nested children replace the "if" statement with.
if(child.nodeName !="#text" && child.className != "text_exposed_hide" && child.parentNode == el)

Instead of removing the node, why not set its innertext/html to empty string:
//msg is the parent node for the div
visibleDiv = msg.getElementsByClassName("text_exposed_root");
textExposedHideNodes = visibleDiv.getElementsByClassName("text_exposed_hide");
for(var i = 0;i < textExposedHideNodes.legth ; i++ ) {
//store to temp for later use
textExposedHideNodes[i].txt = textExposedHideNodes[i].innerHTML;
textExposedHideNodes[i].innerHTML = '';
}
return visibleDiv.innerText;

Related

Check the content in script tag

I have added span tag for all comma using jquery to adding class for css. unfortunately It adds span tag inside script and get collapsed.I want to check the content is not in script tag and add span tag.I want to replace all comma (,) except the content in script tag.
if ($("#overview").length > 0) {
$("#overview").html( $("#overview").html().replace(/,/g,"<span class='comma'>,</span>"));
}
You need to be careful because the html attributes can also contain text with ','.
One way to solve this problem is iterate by the TextNodes, and for each one split the value in multiples TextNodes separated by a SpanNode.
// get all text nodes excluding the script
function getTextNodes(el) {
if (el.nodeType == 3) { // nodeType 3 is a TextNode
return el.parentElement && el.parentElement.nodeName == "SCRIPT" ? [] : [el];
}
return Array.from(el.childNodes)
.reduce((acc, item) => acc.concat(getTextNodes(item)), []);
}
// this will replace the TextNode with the necessary Span and Texts nodes
function replaceComma(textNode) {
const parent = textNode.parentElement;
const subTexts = textNode.textContent.split(','); // get all the subtexts separated by ,
// for each item in subtext it will insert a new TextNode with a SpanNode
// (iterate from end to beginning to use the insertBefore function)
textNode.textContent = subTexts[subTexts.length - 1];
let currentNode = textNode;
for(var i = subTexts.length - 2; i>= 0 ; i --) {
const spanEl = createCommaEl();
parent.insertBefore(spanEl, currentNode);
currentNode = document.createTextNode(subTexts[i]);
parent.insertBefore(currentNode, spanEl)
}
}
// create the html node: <span class="comma">,</span>
// you can do this more easy with JQUERY
function createCommaEl() {
const spanEl = document.createElement('span');
spanEl.setAttribute('class', 'comma');
spanEl.textContent = ',';
return spanEl;
}
// then, if you want to replace all comma text from the element with id 'myId'
// you can do
getTextNodes(document.getElementById('myId'))
.forEach(replaceComma);

Changing DOM elements by reference

Let's say I have a paragraph of text in a HTML file. Using TreeWalker I pushed every non-empty text node to an array:
function getTextNodes(elem) {
var textNodes = [];
var nextNode;
var treeWalker = document.createTreeWalker(elem, NodeFilter.SHOW_TEXT, {
acceptNode: function(node) {
return node.textContent !== "\n" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
}, false);
var counter = 1;
while (nextNode = treeWalker.nextNode()) {
textNodes.push(nextNode);
console.log("Pushed " + counter + " times.");
counter++;
}
return textNodes;
}
When I try to change the contents of the array, like replacing every element with the string "changed ", nothing happens in the browser window.
Is it somehow possible storing the nodes by reference, such that every time they are changed, the text in the browser changes, too?
EDIT:
function changeTextNodes(elem) {
for (var i = 0; i < elem.length; i++) {
elem[i] = "change ";
}
}
The code that changes the array elements.
EDIT2:
$(document).ready(function() {
changeTextNodes(getTextNodes(document.body));
});
Here's how the 2 functions are called.
Your code that attempts to change the text examines the array result from gathering up the nodes. That array consists of nodes, so if you want to modify the content you need to do that through the node API. Currently, your code just modifies the array contents, which will have no effect on the DOM; it's just a JavaScript array.
To change the content, you'd modify the nodeValue property:
for (var i = 0; i < elem.length; i++) {
elem[i].nodeValue = "change ";
}

Make a text Highlight using javascript

I want to make a word bold in given paragraph. Here is a javascript code.
var hlWord = "idm";
var nregex = new RegExp(hlWord,"gi");
var div = document.getElementById("SR").innerHTML;
var rword = div.replace(nregex,"<b>"+hlWord+"</b>");
document.getElementById("SR").innerHTML = rword;
Here is a HTML code.
<div id="SR">
Download here free idm.
click here to download
</div>
This is work well and make all idm bold but here is a problem that it also change
url to like this
click here to download
This is not a valid url.This is the problem that this code make the url damaged.
Please tell me how can I avoid this.
Thanks...
You can iterate through all the text nodes with the methods in this thread, change them and replace them with new bold ones.
var hlWord = "idm";
var nregex = new RegExp(hlWord,"gi");
var sr = document.getElementById('SR');
function escape_html(html) {
return html.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
}
(function findTextNodes(current) {
// make a shadow copy of the child nodes.
var current_children = Array.prototype.slice.call(current.childNodes);
for(var i = 0; i < current_children.length; i++) {
var child = current.childNodes[i];
// text node
if(child.nodeType == 3) {
var value = escape_html(child.nodeValue);
var html = value.replace(nregex, '<b>' + hlWord + '</b>');
if (html != value) {
var node = document.createElement('div');
node.innerHTML = html;
// make a shadow copy of the child nodes.
var childNodes = Array.prototype.slice.call(node.childNodes);
// replace the plain text node with the bold segments
for (var j = 0; j < childNodes.length; j++) {
var c = childNodes[j];
current.insertBefore(c, child);
}
current.removeChild(child);
}
}
else {
findTextNodes(child);
}
}
})(sr);
Check the code example at jsFiddle.
UPDATE:
Passerby pointed out that innerHTML should be used carefully. Escape text nodeValue before processing.
After some try-and-fail, I made a working demo that may be more complicated than you might have think:
http://jsfiddle.net/4VKNk/
var cache=[];
var reg=/idm/gi;
var id=function(ID){return document.getElementById(ID);}
function walkElement(ele){
if(ele.childNodes.length>0){
for(var i=0;i<ele.childNodes.length;i++){
walkElement(ele.childNodes[i]);
}
}else if(ele.nodeType==3){//text node
if(reg.test(ele.nodeValue)){
cache.push(ele);
}
}
}
id("test").onclick=function(){
cache=[];
walkElement(id("SR"));
while(cache.length>0){
var ele=cache.shift();
var val=ele.nodeValue;
var pnt=ele.parentNode;
var nextSibling=ele.nextSibling;
var i=0;
var r,tmp;
pnt.removeChild(ele);
while(r=reg.exec(val)){
tmp=document.createTextNode(val.substring(i,r.index));
if(nextSibling){
pnt.insertBefore(tmp,nextSibling);
tmp=document.createElement("strong");
tmp.appendChild(document.createTextNode("idm"));
pnt.insertBefore(tmp,nextSibling);
}else{
pnt.appendChild(tmp);
tmp=document.createElement("strong");
tmp.appendChild(document.createTextNode("idm"));
pnt.appendChild(tmp);
}
i=reg.lastIndex;
}
if(i<val.length-1){
tmp=document.createTextNode(val.substring(i,val.length));
if(nextSibling){
pnt.insertBefore(tmp,nextSibling);
}else{
pnt.appendChild(tmp);
}
}
}
};
I took the approach of DOM manipulation.
Explanation:
Walk through the whole DOM tree under target element, and cache all TEXT_NODE (nodeType==3);
Use RegExp.exec() method to get the index of each match;
While you find a match, add back the text that come before it, and then add a highlight element (<strong>) that contains the match; continue this step;
If we still have text left, add it back.
The reason I need to cache the TEXT_NODEs first, is that if we directly modify it in walkElement, it will change childNodes.length of its parent, and break the process.

How to select this text using pure javascript, no jquery?

I need to select some text using javascript (no jquery), and Im not sure how to do it. I need to select the text ('Hello World') in myClass element before the first child element. Here are two examples, sometimes it will be an <img> tag as the first element and sometimes it will be a <br> tag. I also need to select the text between the <br> tags.
<p class="myClass" style="padding: 5px;">
Hello World
<img src="http://xyz.com/image.gif">
<br>
30-12-2011 19:45
<br>
Testing
<br>
</p>
<p class="myClass" style="padding: 5px;">
Hello World
<br>
30-12-2011 19:45
<br>
Testing
<br>
</p>
Edit note: I also need to select the other text nodes in myClass. And note that sometimes there will be an <img> as one of the child elements and sometimes there won't.
So I would like to end up with
var a = 'Hello World'
var b = '30-12-2011 19:45'
var c = 'Testing'
Anyone able to do this?
Not really sure what you want. If you want to get all the text, then use:
var text = element.innerText || element.textContent;
If you want to get the text in several pieces, then you have to iterate over all child nodes and extract the text nodes:
var texts = [],
children = element.childNodes;
for(var i = 0, len = children.length; i < len; i++) {
var node = children[i];
if(node.nodeType === 3) {
var text = node.nodeValue.replace(/^\s+|\s+$/g, '');
if(text.length > 0) {
texts.push(text);
}
}
}
This would not concatenate consecutive text nodes though, which can occur if you inserted new text nodes or split text nodes with JavaScript. In this case, you could do this:
var texts = [''],
children = element.childNodes,
new_bucket = false,
bucket = 0;
for(var i = 0, len = children.length; i < len; i++) {
var node = children[i];
if(node.nodeType === 1 && new_bucket) {
new_bucket = false;
texts[++bucket] = '';
}
else if(node.nodeType === 3) {
var text = node.nodeValue.replace(/^\s+|\s+$/g, '');
if(text.length > 0) {
new_bucket = true;
texts[bucket] += text
}
}
}
var elem = document.getElementsByClassname("myClass")[0],
txtNode = elem.childNodes[0],
txtNodeValue = txtNode.nodeValue;
console.log(txtNodeValue);
Example.
We select the element by its class ([0] just tells it to select the first element in the matched set) and then look for its first child node. In your case this would be the text node, so all we have to do from there is get the nodeValue.
This will get the first element with the class myClass and the first text node of that element:
document.getElementsByClassName("myClass")[0].childNodes[0].nodeValue
Here is a great article explaining.
You will most likely want to trim the string as well to remove white-space. You can add the following to give you trim functionality:
if(typeof String.prototype.trim !== 'function') {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
}
}
...and use it as such:
var trimmedString = document.getElementsByClassName("myClass")[0].childNodes[0].nodeValue.trim();
http://jsfiddle.net/kgdnR/1/
var abc = [].filter.call((document.getElementsByClassName("myClass")[0].childNodes), function(node) {
if (node.nodeType === Node.TEXT_NODE && node.nodeValue.trim().length) {
return true;
}
return false;
}).map(function(node) {
return node.nodeValue.trim();
});
var a = abc[0],
b = abc[1],
c = abc[2];
console.log(a, b, c);
Can be made work in IE but if you wanted that you might as well use jQuery.
I dont know how regular your example is but you can use the document object to interact with the DOM.
select the myClass elements
var elements = document.getElementsByClassName('myClass');
then take the first index , its childNodes and the first index of that which is a text node
var nodes = elements[0].childNodes;
for (var i = 0; i < nodes.length; i++)
{
var node = nodes[i],
text;
// loop over the nodes and see if its as textnode. Then trim and skip empty strings
if(node.nodeType === 3) {
text = node.textContent.trim();
if(text) {
alert(text);
}
}
}
first of all, you can use
document.getElementsByClassName('test')
to select the elements using the class name.
select the element you want (it returns an array), and then use
nodeValue
which will return the text.
With using XPath expression:
var contextNode = document.body;
var node, textNodes = [];
var search = document.evaluate('.//text()', contextNode, null, XPathResult.ANY_TYPE);
while(node = search.iterateNext()) textNodes.push(node);
Selecting text as asked by the OP:
var my_class = document.getElementsByClassName('myClass')[0];
window.getSelection().selectAllChildren(my_class);

JavaScript: Add elements in textNode

I want to add an element to a textNode. For example: I have a function that search for a string within element's textNode. When I find it, I want to replace with a HTML element. Is there some standard for that?
Thank you.
You can't just replace the string, you'll have to replace the entire TextNode element, since TextNode elements can't contain child elements in the DOM.
So, when you find your text node, generate your replacement element, then replace the text node with a function similar to:
function ReplaceNode(textNode, eNode) {
var pNode = textNode.parentNode;
pNode.replaceChild(textNode, eNode);
}
For what it appears you want to do, you will have to break apart the current Text Node into two new Text Nodes and a new HTML element. Here's some sample code to point you hopefully in the right direction:
function DecorateText(str) {
var e = document.createElement("span");
e.style.color = "#ff0000";
e.appendChild(document.createTextNode(str));
return e;
}
function SearchAndReplaceElement(elem) {
for(var i = elem.childNodes.length; i--;) {
var childNode = elem.childNodes[i];
if(childNode.nodeType == 3) { // 3 => a Text Node
var strSrc = childNode.nodeValue; // for Text Nodes, the nodeValue property contains the text
var strSearch = "Special String";
var pos = strSrc.indexOf(strSearch);
if(pos >= 0) {
var fragment = document.createDocumentFragment();
if(pos > 0)
fragment.appendChild(document.createTextNode(strSrc.substr(0, pos)));
fragment.appendChild(DecorateText(strSearch));
if((pos + strSearch.length + 1) < strSrc.length)
fragment.appendChild(document.createTextNode(strSrc.substr(pos + strSearch.length + 1)));
elem.replaceChild(fragment, childNode);
}
}
}
}
Maybe jQuery would have made this easier, but it's good to understand why all of this stuff works the way it does.

Categories

Resources