I'm using the following code to delete text between a range of characters.
However, when I try to delete multiple paragraphs of text, I receive the following error:
Index (548) must be less than the content length (377). (line 195, file "")
How I can remove unlimited amounts of text between two text values?
function removeCbSevHD1(X) {
var rangeElement1 = DocumentApp.openById(X).getBody().findText('<CS1>');
var rangeElement2 = DocumentApp.openById(X).getBody().findText('<CS2>');
Logger.log(rangeElement1.getElement());
if (rangeElement1.isPartial()) {
var startOffset = rangeElement1.getStartOffset();
var endOffset = rangeElement2.getEndOffsetInclusive();
rangeElement1.getElement().asText().deleteText(startOffset,endOffset);}
}
}
Expanding on my answer to your previous question, you cannot select from the beginning of one range to the end of another range. That is what the if (element.isPartial()) { ... } else { ... } condition is for. If the range is the entire element, it will remove the whole element.
If you want to remove multiple ranges, then you have to remove one at a time.
In the following example I do this by looping through an array of search strings and applying the function to each.
function removeCbSevHD1(X) {
// If you want to add more things to match and remove, add to this array
var search = /<CS1>.*<CS2>/;
var rangeElement = DocumentApp.openById(X).getBody().findText(search);
if (rangeElement.isPartial()) {
var startOffset = rangeElement.getStartOffset();
var endOffset = rangeElement.getEndOffsetInclusive();
rangeElement.getElement().asText().deleteText(startOffset,endOffset);
} else {
rangeElement.getElement().removeFromParent();
}
}
Note: Not tested.
Tiny G! Thanks for all the help man.
I was able to find some help. Here's the answer:
function removeSection3(X) {
for (var i = 1; i <= 7; i++) {
var search = '<ZY' + i + '>';
var rangeElement = DocumentApp.openById(X).getBody().findText(search);
if (rangeElement) {
rangeElement.getElement().getParent().removeFromParent();
}
}
}
Related
What I want to do
I want to be able to work out how much of a string is shown before it is truncated.
Why I want to do it
I have a list of items. You can select any amount of items from this list. I have a panel element that shows a comma separated string of the selected selected item names. If the string is too long, it should be truncated and display a +{number} value of any additional selected items hidden by the truncate.
Basic example
https://stackblitz.com/edit/angular-ivy-cbyemy
The stackblitz above shows a basic example of the use case.
Currently panel title:
an item, another item, som... +6
Desired result:
an item, another item, som... +3
What I have tried
Using element.innerHTML and element.innerText to read the displayed string. both show the full string, not the displayed truncated string.
Borrowed an answer from here and modified it a bit as per requirement.
Your forked stackblitz, updated working demo here.
You can get the count of visible characters from this function -
countVisibleCharacters(element): number {
var text = element.firstChild.nodeValue;
var count = 0;
element.removeChild(element.firstChild);
for (var i = 0; i < text.length; i++) {
var newNode = document.createElement('span');
newNode.appendChild(document.createTextNode(text.charAt(i)));
element.appendChild(newNode);
if (newNode.offsetLeft < element.offsetWidth) {
count++;
}
}
return count;
}
Then you can get the suffix this way -
getTitleSuffix(): void {
let element = document.getElementById('title');
let count = this.countVisibleCharacters(element);
let substr = element.textContent;
substr = substr.substring(count - 1);
this.titleSuffix = substr.split(',').length;
}
I am trying to figure out how to get each value within my div. I am using
var cart = $('.basic-cart-cart-node-title.cell').text();
It is giving the results of OI-01OP-01OS-10-5SOR-04OR-05
I need to view them one by one: OI-01, OP-01, OS-10-5S, OR-04 OR-05.
So that I can match them against another field.
If you care to help me further, I have another div on the page:
var ParNum = $('.assess-title').text();
I would like to compare the values returned from the var cart and see if that value is in the ParNum. If it is there, I would like to apply a class.
Any help would be greatly appreciated.
Thanks!
You can store the values in an array using .map() method:
var values = $('.basic-cart-cart-node-title.cell').map(function() {
return $.trim( $(this).text() );
}).get();
For checking existence of the ParNum value in the array:
var does_exist = values.indexOf(ParNum) > -1;
Try this to iterate over elements:
var text = '';
$('.basic-cart-cart-node-title.cell').each(function (i, div) {
text += ' ' + $(div).text();
});
or this to get an array of matching div elements:
var divs = $('.basic-cart-cart-node-title.cell').toArray();
for (var i = 0; i < divs.length; i++) {
// $(div).text();
}
Reason for this is that $('.basic-cart-cart-node-title.cell') returns all div's at once, and you need to loop through the result. More specifically, $(selector) returns a so-called "wrapped set". It can be used to access each matching element (as I've shown above) or it can be used to apply any other jQuery function to the whole set at once. More info here.
var text = "";
$('.basic-cart-cart-node-title.cell').each(function(){
text += $(this).text() + ", ";
});
// remove the last ", " from string
text = text.substr(0, text.length -2);
var cart = [];
$('.basic-cart-cart-node-title.cell').each(function {
cart.push($(this).text());
}
This performs the matching and class adding you mentioned in the question.
var ParNum = $('.assess-title').text();
$('basic-cart-cart-node-title.cell').each(function () {
if ($(this).text() == ParNum) {
$(this).addClass("someclass");
}
}
You should try using
var cart ='';
$('.basic-cart-cart-node-title'.find('.cell').each(function()
{
cart = cart + $(this).val();
});
Hope it works for you.
var cart = $('.basic-cart-cart-node-title.cell').text().match(/.{5}/g);
This will give you an array with items 5 chars long. Regexes arent very fast, but a loop might be slower
Or easier to read, and in a string with commas:
var cart = $('.basic-cart-cart-node-title.cell').text(); // get text
cart = cart.match(/.{1,5}/g); // split into 5 char long pieces
cart = cart.join(",",); join on comma
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.
I dynamically create this list element and information a user has typed in shows up in it when a button is clicked 'info' is text and shuld show as it is but 'grade' is a number that i want to convert to another sign with the function changeNumber() but I am new to javascript and cant figure out how to make this function, can anyone give a suggestion or point me in the right direction?
var list = $("#filmlista");
var list_array = new Array();
function updateFilmList()
{
document.getElementById("name").value = '';
document.getElementById("star").value = 0;
var listan = list_array[0][0];
var grade = list_array[0][1];
var element = '<li class="lista">' + list + '<span class="grade">'+ changeNumber(grade) +'</span></li>';
list.append(element);
}
should I use innerHTML? not shure I understand how it works? and how do I use the replace method if I have to replace many different numbers to the amount of signs the number is?
for example if the number is 5 it should show up as: *****, if number is 3 show up as: *** and so on
Here's some code that should do the trick:
Add this function into your script.
function changeNumber(number) {
var finalProduct = "";
for (var i = 0; i < number; i++) {
finalProduct += "*";
}
return finalProduct;
}
Replace the updateFilmsList with this code.
document.getElementById("name").value = '';
document.getElementById("star").value = 0;
var listan = list_array[0][0];
var grade = changeNumber(list_array[0][1]);
var element = '<li class="lista">' + list + '<span class="grade">'+ grade +'</span></li>';
list.append(element);
It looks like you're trying to do something like PHP's str_repeat. In that case, take a look at str_repeat from PHPJS
There are options other than a loop:
function charString(n, c) {
n = n? ++n : 0;
return new Array(n).join(c);
}
charString(3, '*'); // ***
You can use innerHTML to set the text content of an element provided none of the text might be mistaken for markup. Otherwise, set the textContent (W3C compliant) or innerText (IE proprietary but widely implemented) property as appropriate.
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()