Check the content in script tag - javascript

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);

Related

Highlight matched text instead of the whole the text

I have a function, getTextNodes, that searches text nodes recursively. Then I use a addHighlight function to highlight the text with <mark> tags:
const buttonEl = `<button>
<span>
Icon
</span>
Text
</button>
`;
document.body.innerHTML = buttonEl;
const foundButtonEl = document.querySelector("button");
const elements = [];
elements.push(foundButtonEl);
addHighlight(elements, "T");
function addHighlight(elements, text) {
elements.forEach((element, index) => {
const textNodes = getTextNodes(document.body);
const matchingNode = textNodes.find(node => node.textContent.includes(text));
const markElement = document.createElement('mark');
markElement.innerHTML = matchingNode.textContent;
matchingNode.replaceWith(markElement);
});
}
function getTextNodes(node) {
let textNodes = [];
if (node.nodeType === Node.TEXT_NODE) {
textNodes.push(node);
}
node.childNodes.forEach(childNode => {
textNodes.push(...getTextNodes(childNode));
});
return textNodes;
}
The problem is that addHighlight is highlighing the whole text (in the example, Text), instead of the matched text (in the example, T).
How to change this code so that only the matched text is highlighted (text)?
matchingNode is the whole node so you're replacing everything. If you want to match just part of it, you need to iterate though the textnode and find the index position of the substring that you're searching for.
Start by splitting the node into an array
matchingNode.wholeText.split("")
Then find the index position, insert markElement at that position, and go from there.
The problem is that the node you match is the element of which the innerContent contains the string you want to highlight.
What you should do instead of :
markElement.innerHTML = matchingNode.textContent;
matchingNode.replaceWith(markElement);
is probably something like
markElement.innerHTML = text;
matchingNode.replaceTextWithHTML(text, markElement);
replaceTextWithHTML is a fictive function :)

JavaScript add spaces between sentences while parsing XML string

I have this XML string which I am displaying as a text in a document:
<p>The new strain of <s alias="coronavirus">COVID</s>seems to be having a greater spread rate.</p>
The following function returns the text form of the XML:
function stripHtml(html) {
// Create a new div element
var temporalDivElement = document.createElement("div");
// Set the HTML content with the providen
temporalDivElement.innerHTML = html;
// Retrieve the text property of the element (cross-browser support)
return temporalDivElement.textContent || temporalDivElement.innerText || "";
}
The problem is, this function returns the following string:
The new strain of COVIDseems to be having a greater spread rate.
which is nearly what I want, but there is no space between the word COVID and seems. Is it possible that I can add a space between contents of two tags if it doesn't exist?
One option is to iterate over text nodes and insert spaces at the beginning if they don't exist, something like:
const getTextNodes = (parent) => {
var walker = document.createTreeWalker(
parent,
NodeFilter.SHOW_TEXT,
null,
false
);
var node;
var textNodes = [];
while(node = walker.nextNode()) {
textNodes.push(node);
}
return textNodes;
}
function stripHtml(html) {
// Create a new div element
var temporalDivElement = document.createElement("div");
// Set the HTML content with the providen
temporalDivElement.innerHTML = html;
// Retrieve the text property of the element (cross-browser support)
for (const node of getTextNodes(temporalDivElement)) {
node.nodeValue = node.nodeValue.replace(/^(?!\s)/, ' ');
}
return temporalDivElement.textContent.replace(/ +/g, ' ').trim();
}
console.log(stripHtml(`<p>The new strain of <s alias="coronavirus">COVID</s>seems to be having a greater spread rate.</p>`));

getting text from selective nodes in 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;

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.

Applying google app script code in selection in google docs

I have the following code that puts bold style some keywords in a whole google document:
function boldKeywords() {
// Words that will be put in bold:
var keywords = ["end", "proc", "fun"];
var document = DocumentApp.getActiveDocument();
var body = document.getBody();
var Style = {};
Style[DocumentApp.Attribute.BOLD] = true;
for (j in keywords) {
var found = body.findText(keywords[j]);
while(found != null) {
var foundText = found.getElement().asText();
var start = found.getStartOffset();
var end = found.getEndOffsetInclusive();
foundText.setAttributes(start, end, Style)
found = body.findText(keywords[j], found);
}
}
}
But I would like the code to put the keywords in bold only in the selected area of the document, for doing that, I tried using the function getSelection(), but I have the problem that this function returns a Range, but for applying findText I need a Body, somebody knows what could I do?
Modified Script
function boldKeywordsInSelection() {
const keywords = ["end", "proc", "fun"];
const document = DocumentApp.getActiveDocument();
const selection = document.getSelection();
// get a list of all the different range elements
const rangeElements = selection.getRangeElements();
const Style = {};
Style[DocumentApp.Attribute.BOLD] = true;
// forEach used here because for in was giving me trouble...
rangeElements.forEach(rangeElement => {
// Each range element has a corresponding element (e.g. paragraph)
const parentElement = rangeElement.getElement();
// fixing the limits of the bold operations depending on the selection
const startLimit = rangeElement.getStartOffset();
const endLimit = rangeElement.getEndOffsetInclusive();
for (j in keywords) {
let found = parentElement.findText(keywords[j]);
// wrapping in try catch to escape the for loop from within the while loop
try {
while (found != null) {
const foundText = found.getElement().asText();
const start = found.getStartOffset();
// Checking if the start of the word is after the start of the selection
if (start < startLimit) {
// If so, then skip to next word
found = parentElement.findText(keywords[j], found);
continue;
}
// Checking if the start of the word is after the end of the selection
// if so, go to next element
if (start > endLimit) throw "out of selection";
const end = found.getEndOffsetInclusive();
foundText.setAttributes(start, end, Style)
found = parentElement.findText(keywords[j], found);
}
} catch (e) {
Logger.log(e)
continue;
}
}
})
}
See the comments in the code for more details.
A getSelection produces a Range object, which contains within it various instances of RangeElement. Each RangeElement makes reference to a parent element, and the index positions within that parent. The parent is the element that the range element is a part of. For example:
This selection spans 3 elements. Therefore the selection has 3 range elements. You can only use the findText method on the whole element, not the range element.
This means that the flow of the script is generally the same, except that you need to go through each element and find the text within each element. Since this will return elements that are outside the selection, you need to keep track of the index positions of the selection and the found element and make sure the found element is within the selection.
References
Range
RangeElement
getSelection()

Categories

Resources