Insert text to selected range with button - javascript

I apologize in advance for having little to no knowledge on this matter. I am familiar enough to utilize VBA but have recently made the switch to Sheets and I am lost.
I was originally looking for a way to prompt a drop down menu when double clicking a cell in a range that will then add the selected information as a prefix to the text.
Example: cell contains "First Last", upon double clicking you get a drop down to select "Mr., Mrs." when selecting "Mr." the cell now says "Mr. First Last" but it doesn't appear that you can set a double click event.
So now I am trying to come up with another solution where you select desired cells and 'apply' the function.
Example: Select cells A1 B1 C3 D9, click a button that applies "Mr. " as a prefix. Selecting it again will remove it.
Please help me get this moving.

Try this:
// menu
function onOpen() {
SpreadsheetApp.getUi().createMenu('⚡ Scripts')
.addItem('🎩 Add Mr', 'add_Mr')
.addItem('Remove Mr', 'remove_Mr')
.addToUi();
}
// functions
function add_Mr() {
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getSelection();
var ranges = selection.getActiveRangeList().getRanges();
for (var i in ranges) {
var data = ranges[i].getValues();
for (var row in data) for (var col in data[row])
if (data[row][col] != '') data[row][col] = 'Mr. ' + data[row][col];
ranges[i].setValues(data);
}
}
function remove_Mr() {
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getSelection();
var ranges = selection.getActiveRangeList().getRanges();
for (var i in ranges) {
var data = ranges[i].getValues();
for (var row in data) for (var col in data[row])
if (data[row][col] != '') data[row][col] = data[row][col].replace(/^Mr\. /,'');
ranges[i].setValues(data);
}
}
It will add menu Scripts and two commands 'Add Mr' and 'Remove Mrs' for selected cells.
And as a homework you can try to add add_Mrs() and remove_Mrs() functions.
If you need to toggle 'Mr' here you go:
function toggle_Mr() {
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getSelection();
var ranges = selection.getActiveRangeList().getRanges();
for (var i in ranges) {
var data = ranges[i].getValues();
for (var row in data) for (var col in data[row]) {
var cell = data[row][col];
if (cell == '') continue; // if empty --> go to next cell
if (cell.match(/^Mr\. /)) { // if contains 'Mr.'
data[row][col] = cell.replace(/^Mr\. /,''); // --> remove 'Mr.'
continue; // --> go to next cell
} else {
data[row][col] = 'Mr. ' + data[row][col]; // else --> add 'Mr.'
}
}
ranges[i].setValues(data);
}
}
Update
Here is the same function toggle_Mr() with example how you can get the 'coordinates' of all processed cells:
function toggle_Mr() {
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getSelection();
var ranges = selection.getActiveRangeList().getRanges();
for (var i in ranges) {
// get 'coordinates' (col, row) of first (left-top) cell of selected range
var start_col = ranges[i].getColumn();
var start_row = ranges[i].getRow();
var data = ranges[i].getValues();
for (var row in data) for (var col in data[row]) {
var cell = data[row][col];
// calculate the real 'coordinates' of processed cells
var real_col = +start_col + +col;
var real_row = +start_row + +row
console.log('Process the cell [' + real_col + '][' + real_row + ']');
if (cell == '') continue;
if (cell.match(/^Mr\. /)) {
data[row][col] = cell.replace(/^Mr\. /,'');
continue;
} else {
data[row][col] = 'Mr. ' + data[row][col];
}
}
ranges[i].setValues(data);
}
}
You can see the cell 'coordinates' in console if you run the function from the Script Editor.

Related

JS Adding/Removing image from cell (toggle button)

I am looking for a way to add and remove an image in a cell.
I have the button assigned to this function but I can't seem to add it to the dynamic range. If I hardcode the column and row for testing it adds the image fine but then I can't figure out how to delete it.
function test() {
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getSelection();
var ranges = selection.getActiveRangeList().getRanges();
for (var i in ranges) {
var data = ranges[i].getValues();
for (var row in data) for (var col in data[row]) {
var cell = data[row][col];
if (cell == '') continue; // if empty --> go to next cell
IF CELL CONTAINS IMAGE REMOVE IT
continue; // --> go to next cell
} else {
sheet.insertImage("URL", [col], [row], 125, 2); //Add image at col/row
}
}
ranges[i].setValues(data);
}
}
Before button click
After button click
Select new cells
After button click
I believe your goal is as follows.
When the script is run after the cells are selected, you want to put the image on the cell.
When the image is not put on the cell, you want to put the image on the cell.
When the image has already been put on the cell, you want to remove the image.
When the cell value is empty, you don't want to do anything.
In this case, how about the following modified script?
Modified script:
function test() {
const url = "###"; // Please set your URL.
const sheet = SpreadsheetApp.getActiveSheet();
const ranges = sheet.getActiveRangeList().getRanges();
let images = sheet.getImages();
ranges.forEach(r => {
const row = r.getRow();
const col = r.getColumn();
const numRows = r.getNumRows();
const numCols = r.getNumColumns();
for (let i = 0; i < numRows; i++) {
for (let j = 0; j < numCols; j++) {
if (sheet.getRange(row + i, col + j).isBlank()) continue;
const image = images.filter(e => {
const anchor = e.getAnchorCell();
return anchor.getRow() == row + i && anchor.getColumn() == col + j;
});
if (image.length > 0) {
image.forEach(e => e.remove());
images = sheet.getImages();
} else {
sheet.insertImage(url, col + j, row + i, 125, 2);
}
}
}
});
}
At first, please set your URL.
When you use this script, please select the cells and run the script.
When you run this script for the selected cells, the images are put on the cells which have no images, and the images are removed from the cells which have the images.
In this case, the image on the cells can be checked using the method of getAnchorCell() of Class OverGridImage.
References:
getImages() of Class Sheet
getAnchorCell() of Class OverGridImage

Speeding up UrlFetch Google App Scripts?

The goal is to run through about 10,000 lines of links. Determine which have page numbers > 3 and highlight the first column. I have all of this done, but the problem is that it takes Url Fetch too long, I run into a maximum run time error. Is there anyway I can speed up this code so I can run through the 10,000 lines?
function readColumns() {
//program is going to run through column 3 by going through the amount of rows, truncating last three characters to see if pdf, then highlighting first column
var sheet = SpreadsheetApp.getActiveSheet();
var columns = sheet.getDataRange();
var rowNum = columns.getNumRows();
var values = columns.getValues();
var html;
var htmlString;
for(var i = 1; i <= rowNum; i++){
var columnLogger = values[i][2];
try{
html = UrlFetchApp.fetch(values[i][2],
{
muteHttpExceptions: true,
}
);
}catch(e){
Logger.log("Error at line " + i);
var error = true;
}
htmlString = html.getContentText();
var index = htmlString.indexOf("Pages") + 6;
var pageNumber = parseInt(htmlString.charAt(index),10);
var lastChars = "" + columnLogger.charAt(columnLogger.length-3) + columnLogger.charAt(columnLogger.length-2) + columnLogger.charAt(columnLogger.length-1);
if((error) || (!lastChars.equals("pdf") && values[i][6].equals("") && !pageNumber >= 3)){
//goes back to first column and highlights yellow
var cellRange = sheet.getRange(1, 1, rowNum, 3)
var cell = cellRange.getCell(i+1, 1)
cell.setBackground("yellow");
}
}
}
Edit - short scripts:
function foreverCall(){
var start = 1480;
for(;;){
readColumns(start);
start = start + 100;
}
}
function readColumns(start) {
//program is going to run through column 3 by going through the amount of rows, truncating last three characters to see if pdf, then highlighting first column
var sheet = SpreadsheetApp.getActiveSheet();
var columns = sheet.getDataRange();
var rowNum = columns.getNumRows();
var values = columns.getValues();
var html;
var htmlString;
var error;
for(var i = start; i < start+100; i++){
if(loop(values, error, html, htmlString, rowNum, sheet, columns, i)){
var cellRange = sheet.getRange(1, 1, rowNum, 3)
var cell = cellRange.getCell(i, 1)
cell.setBackground("yellow");
}
}
}
function loop(values, error, html, htmlString, rowNum, sheet, columns, i){
var columnLogger = values[i][2];
var lastChars = columnLogger.slice(-4);
if(!lastChars.equals(".pdf") && values[i][6].equals("")){
return true;
}else{
try{
error = false
html = UrlFetchApp.fetch(values[i][2].toString());
if(html == null){
error = true;
}
}catch(e){
Logger.log("Error at line " + i);
error = true;
}
if(!error){
htmlString = html.getContentText();
var index = htmlString.indexOf("Pages") + 6;
var pageNumber = parseInt(htmlString.charAt(index),10);
}
//goes back to first column and highlights yellow
if(error || !pageNumber >= 3){
return true;
}
}
return false;
}
You can replace this:
var lastChars = "" + columnLogger.charAt(columnLogger.length-3) + columnLogger.charAt(columnLogger.length-2) + columnLogger.charAt(columnLogger.length-1);
With this:
var lastChars = columnLogger.slice(-3);
You could also initiate the fetch script from an html sidebar or dialog to run short batches and then return back to the success handler which could then initiate another batch depending upon the return value. The return value could also be used to start the next batch at the next row. It would actually take longer to run but you could probably stay well under the script limit by keeping your batches small.
You can replace with the line with
var lastChars = columnLogger.slice(-3);

Google Form Script Population

I am trying to populate a google form with questions scraped from a google sheet. Currently when I run my code I am getting the questions created, but only 25% or so actually have the string, the rest are simply blank. The questions that appear correctly change every time I run the script. It is seemingly random.
function formPopulation() {
var ss = SpreadsheetApp.openById("--");
var sheet = ss.getSheetByName('Tracker');
var auditTool = ss.getSheetByName('Audit Tool');
var validatorInfo = ss.getSheetByName('Validator Info');
//Sheet Info
var rows = auditTool.getLastRow(); //Number of Rows
var columns = auditTool.getLastColumn(); //Number of Columns
var startRow = 1;
var startColumn = 1;
var dataRange = auditTool.getRange(startRow, startColumn, rows, columns);
//getRange(first row of data, first column of data, last row of data, last column of data)
var data = dataRange.getValues();
//Sets working range of script
var form = FormApp.openById("--");
var item = form.addListItem();
var entityName = "";
var arrayOfEntities = [];
var newEntity = '';
for (var i = 4; i < columns; i++) {
//4 because that is where entity names begin
entityName = data[i][2];
Logger.log('entityName: ' + entityName);
newItem = item.createChoice(entityName);
arrayOfEntities.push(newItem);
};
item.setTitle("Select Entity").setChoices(arrayOfEntities);
var requirement = "";
var arrayOfRequirements = [];
var newRequirement = '';
for (var j = 5; j < rows; j++) {
//5 because that is where Requirements begin
if (data[0][j] != null) {
requirement = data[0][j];
if (requirement != "" || requirment != null){
requirement = "question #" + j;
Logger.log('requirement: ' + requirement);
form.addMultipleChoiceItem().setTitle(requirement).setChoiceValues(['Complete', 'Incomplete']);
};
};
};
};
The first question is supposed to be a multiple choice item where each 'entity' is an option. The remainder of the questions are supposed to be whether each 'requirement' is marked complete or incomplete.
Here is the spreadsheet I am working from
you have a typo:
if (requirement != "" || requirment != null){
should be 'requirement'
Here in last forloop
requirement = "question #" + j;
Please verify, is it ok ? or you should use
requirement = "question #" + j + ' ' +data[0][j];

How to find out which row was clicked?

Hello i generate a Table with javascript and now i wont to find out which row and column the user has clicked?
Here are my function for the table:
function doNextSteps() {
removeAustriaFromCountries();
//insert table
var table = document.createElement("table");
table.setAttribute('id', 'matrixTable');
table.setAttribute('class', 'jbiTable');
// insert MATRIX row
var matrixRow = table.insertRow();
var cell = matrixRow.insertCell(); // left column for countries
cell.setAttribute('class', 'jbiMatrixCell');
cell.setAttribute('colSpan', departments.length + 1);
cell.appendChild(document.createTextNode("MATRIX"));
// insert departments row
var departmentsRow = table.insertRow();
var cell = departmentsRow.insertCell(); // left column for countries
cell.setAttribute('class', 'jbiBlankCell');
for (var i = 0; i < departments.length; i++) {
var cell = departmentsRow.insertCell();
cell.appendChild(document.createTextNode(departments[i].name));
cell.setAttribute('class', 'jbiDepartmentCell');
}
for (var i = 0; i < countries.length; i++) {
var countryRow = table.insertRow();
var cell = countryRow.insertCell(); // left country column
//cell.appendChild(document.createTextNode(countries[i].name));
var img = document.createElement('img');
img.src = "example.com + flags[i].name";
cell.appendChild(img);
cell.setAttribute('class', 'jbiCountryCell');
for (var j = 0; j < departments.length; j++) {
var cell = countryRow.insertCell();
var img = document.createElement('img');
img.src = "https://intranet.windkraft.at/OrganisationManual/Documents/Kreis.jpg";
img.onclick = function () {
window.location.href = "example.com" + pdfFiles[i].name;
};
cell.appendChild(img);
cell.setAttribute('class', 'jbiCircleCell');
}
}
$("#divTable").append(table);
}
The table gets generated and now i want to know in which header and in which column the user has clicked. With that information i want to make a new query to get files dynamically displayed in the Table. Any help would be great. And thanks for your Help.
To get the index of the row, you can use this code in your event listener function:
function onClick() {
var cell = this;
var row = cell.parentNode;
var cellIndex = Array.prototype.indexOf.call(row.children, cell);
var rowIndex = Array.prototype.indexOf.call(row.parentNode.children, row);
// do stuff with rowIndex, cellIndex
// rowIndex is the row number starting with row 0
// cellIndex is the column number starting with column 0
}
You can use parentNode.rowIndex & cellIndex to get the cell & rowIndex
document.getElementsByTagName('table')[0].addEventListener('click', function(e) {
console.log(e.target.parentNode.rowIndex,' ',e.target.cellIndex);
}, false);
Check this jsFiddle

Returning cell value based on Radio selection

The goal I am trying to achieve is to retrieve a cell value based on a form radio selection and update a text area.
Process: User opens dialog box. They select a field office. Onclick runs the function check. After check runs google.script.run.withSuccessHandler(addSignatureLine).getSignatureLine(cellElement); is supposed to run and update the textarea with the Id 'AdditionalMessage' with the signature line retrieved from .getSignatureLine.
Here are two functions of the html code:
<script>
function addSignatureLine(signatureLine){
document.getElementById('AdditionalMessage').value = '\n\n'signatureLine;
};
function updateSignatureLine() {
var cellElement = document.getElementById('ET');
console.log('cellElement: ' + cellElement);
google.script.run.withSuccessHandler(addSignatureLine)
.getSignatureLine(cellElement);
};
function check() {
var ele = document.getElementsByName('fieldOfficeET');
var flag = 0;
for (var i = 0; i < ele.length; i++) {
if (ele[i].checked)
flag = 1;
}
if (flag == 1)
document.getElementById('Submit').disabled = false;
};
</script>
Here is the getSignatureLine.gs script
function getSignatureLine(cellObject) {
var ss = SpreadsheetApp.openById('googleSheetId');
var sheet = ss.getSheetByName('AMS Contact Information');
var firstRow = 2;
var lastRow = 10;
var dataRange = sheet.getRange(firstRow, 1, lastRow, 11);
var dataValues = dataRange.getValues();
for (var key in cellObject) { //Loop through all the data in the form
Logger.log('key: ' + key);
Logger.log('value: ' + cellObject[key]);
}
//Determines the row the Field Office is in
for (var rr = 0; rr < dataValues.length; rr++) {
if (dataValues[rr][0] == cellObject.fieldOfficeET) {
var r = rr + 2
break;
}
}
var signatureLine = sheet.getRange(r, 11).getValue();
Logger.log("signatureLine: " + signatureLine)
return signatureLine;
}
There is a problem with this line:
document.getElementById('AdditionalMessage').value = '\n\n'signatureLine;
I would try:
document.getElementById('AdditionalMessage').value = '\n\n' + signatureLine;
Add a plus sign to concatenate the text.

Categories

Resources