Only execute onEdit if the users make edit within a specific range - google-apps-script

How can I trigger onEdit only when users make any edits with a specific range in Google App Script?

You cannot create an onEdit that only fires when certain cells are edited, but you can put a condition in the beginning of your function to check if the edited cells are part of the range you want to track changes from. You can do that with the use of an event object for that.
For example, you can do this (check inline comments):
function onEdit(e) {
// Information from edited range:
var range = e.range;
var editedRow = range.getRow();
var editedCol = range.getColumn();
var editedSheet = range.getSheet();
// Range to track (please change accordingly):
var sheetName = "Sheet To Track";
var minCol = 3;
var maxCol = 7;
var minRow = 2;
var maxRow = 5;
// Check that edited sheet, column and row are the ones you want to track changes from:
if (editedSheet.getName() === sheetName && editedRow >= minRow && editedRow <= maxRow && editedCol >= minCol && editedCol <= maxCol) {
// Actions to perform if edited range is correct
}
}
The function above will first check if the edited range is (1) between columns 3 and 7, (2) between rows 2 and 5 and (3) in the sheet called Sheet To Track.
Reference:
Event Objects: Edit

Related

Google Script - onEdit(e) to trigger specific cell change

I'm currently using the below script to trigger an automatic cell edit. However, as a script basically runs every time any cell on the worksheet is edited, I wonder if there is a way to modify it so that it only works when let's say a cell B24 is edited or a range of cells from B24 - B30 are then trigger the script. It seems like an onEdit (e) function could be an answer but I'm struggling to modify the code accordingly. Would you guys be so kind and help?
function myfunction() {
var app = SpreadsheetApp;
var targetSheet = app.getActiveSpreadsheet().getSheetByName("ABC");
targetSheet.getRange(154,2).setValue("Bingo");
}
When using the below onEdit(e) I keep receiving an error message " target sheet not defined"
function onEdit(e) {
var range = e.range;
var spreadSheet = e.source;
var sheetName = spreadSheet.getActiveSheet().getName();
var column = range.getColumn();
var row = range.getRow();
if(sheetName == 'Harmonogram wpłat' && column == 2 && row == 24)
{
SpreadsheetApp.getActiveSpreadsheet().gatSheetByName('ABC').getRange('B154').setValue('Bingo');
}
}
Most likely, the error is due to the typo in the last line of your code.
gatSheetByName('ABC')
should be
getSheetByName('ABC')
See if that helps?
Modification points:
You have a typo as the other answer has already mentioned. It should be getSheetByName(name) and not gatSheetByName(name).
You want to execute the script when a cell in the range B24 - B30 is edited. To achieve that you need to modify the if condition to include that range:
if(sheetName == 'Harmonogram wpłat' && column == 2 && row > 23 && row<31)
SpreadsheetApp.getActiveSpreadsheet() can be replaced by spreadSheet since e.source is exactly the same thing.
Solution:
function onEdit(e) {
var range = e.range;
var spreadSheet = e.source;
var sheetName = spreadSheet.getActiveSheet().getName();
var column = range.getColumn();
var row = range.getRow();
if(sheetName == 'Harmonogram wpłat' && column == 2 && row > 23 && row<31){
spreadSheet.getSheetByName('ABC').getRange('B154').setValue('Bingo');
}
}

Insert Timestamp when copy/paste whole row and value inserts/edits in Column C in google sheets

I tried inserting timestamp when a row is being copied and data inserts or edits in Column C same row cell, but it works only on manual entry, not on copy-paste.
Please suggest to me what I am missing or doing wrong.
function onChange() {
var s = SpreadsheetApp.getActiveSheet();
var sName = s.getName();
var r = s.getActiveCell();
var row = r.getRow();
var ar = s.getActiveRange();
var arRows = ar.getNumRows()
// Logger.log("DEBUG: the active range = "+ar.getA1Notation()+", the number of rows = "+ar.getNumRows());
if( r.getColumn() == 3 && sName == 'Sheet1') { //which column to watch on which sheet
// loop through the number of rows
for (var i = 0;i<arRows;i++){
var rowstamp = row+i;
SpreadsheetApp.getActiveSheet().getRange('F' + rowstamp.toString()).setValue(new Date()).setNumberFormat("MM/dd/yyyy hh:mm"); //which column to put timestamp in
}
}
}//setValue(new Date()).setNumberFormat("MM/dd/yyyy hh:mm:ss");
Use getLastColumn() to check whether column C is included in the pasted range.
Use getNumRows() to get the number of rows your copied range has, and so add the timestamp to all these rows.
No need to used an installed onChange() for this, a simple onEdit() is enough.
I'd also suggest to use event object in order to get information on which range was edited (even though this way you won't be able to fire this successfully from the script editor).
Edit: if you want to remove the timestamp when the range is cleared, you can just check that's the case, using every, or some, and clearContent if that's the case.
Code snippet:
function onEdit(e) {
var s = SpreadsheetApp.getActiveSheet();
var r = e.range;
var firstRow = r.getRow();
var numRows = r.getNumRows();
var firstCol = r.getColumn();
var lastCol = r.getLastColumn();
if((firstCol <= 3 || lastCol >= 3) && s.getName() == 'Sheet1') {
var emptyRange = r.getValues().every(row => row.every(value => value === ""));
var destRange = s.getRange(firstRow, 6, numRows);
if (emptyRange) destRange.clearContent();
else {
var dates = new Array(numRows).fill([new Date()]);
destRange.setValues(dates).setNumberFormat("MM/dd/yyyy hh:mm");
}
}
}
The following script will create timestamps starting from column F until the last column when you copy the row.
I think you are looking for this:
function onEdit(e) {
const startCol = 6; // column F
const s = e.source.getActiveSheet();
const sName = s.getName();
const ar = e.range;
const row = ar.getRow();
const arColumns = ar.getNumColumns();
const arRows = ar.getNumRows();;
if( sName == 'Sheet1') {
const rng = s.getRange(row,1,arRows,s.getMaxColumns());
check = rng.getValues().flat().every(v=>v=='');
if(check){
rng.clearContent();
}
else{
s.getRange(row,startCol,arRows,s.getMaxColumns()-startCol+1).setValue(new Date()).setNumberFormat("MM/dd/yyyy hh:mm");
}
}
}
Note:
Again, onEdit is a trigger function. You are not supposed to execute it manually and if you do so you will actually get errors (because of the use of the event object). All you have to do is to save this code snippet to the script editor and then it will be triggered automatically upon edits.

hide columns based on user input cell

hi all (I am new to scripting),
here is a sample of my sheet I need help with.
https://docs.google.com/spreadsheets/d/1iypkWdUsSkow9m8nTSFS25HgY1d5vsVMZvjEynwQJcM/edit?usp=sharing
what i need is a script that will run onedit.
the user has 5 selectable fields, highlighted in yellow. the script should run only if cell F3 is edited.
and what it should do is hide all column where row 1 does not match F3.
please please help!
I wrote this small function that hides or unhides columns depending on the value of the dropdown in F3, and that only runs when this cell is modified.
Copy this to the script bound to your spreadsheet:
function onEdit(e) {
var ss = e.source; // Spreadsheet that triggered the function
var sheet = ss.getActiveSheet();
var sheetName = "Sheet1"; // Change accordingly
var range = e.range; // Range that was edited
var editedColumn = range.getColumn();
var editedRow = range.getRow();
var column = 6;
var row = 3;
// Check that the edited cell is F3 and the edited sheet is the one you want:
if(column == editedColumn && row == editedRow && sheet.getName() == sheetName) {
var dropdownValue = range.getValue(); // Value of F3
// Index of last column with content. If you want to hide the blank columns after that, use sheet.getMaxColumns() instead:
var cols = sheet.getLastColumn();
// First row values:
var headers = sheet.getRange(1, 1, 1, cols).getValues()[0];
// Looping through each column (hiding or unhiding depending on whether value matches):
for(var i = 1; i < cols; i++) {
if(headers[i] != dropdownValue) {
sheet.hideColumns(i + 1);
} else {
sheet.showColumns(i + 1);
}
}
}
}
Please tell me if that works for you.

Google Sheets - Make sure cells have a value before executing script

I want to have a script execute only if all required cells contain information.
The above sheet shows how the layout is. The rows that need to be populated for the script to start are columns B to G
An extra feature that I would also like but is not required is if the user does not fill in all the cells required it will highlight them in red and delete the values in columns H & I of the active row in question.
Below is the script that executes when column H has the value "Completed"
function moveComplete() {
Utilities.sleep(200);
var sheetNameToWatch = "REQUEST";
var columnNumberToWatch = 9;
var valueToWatch = "COMPLETED";
var sheetNameToMoveTheRowTo = "ARCHIVE";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveCell();
if (sheet.getName() == sheetNameToWatch && range.getColumn() == columnNumberToWatch && range.getValue() == valueToWatch) {
var targetSheet = ss.getSheetByName(sheetNameToMoveTheRowTo);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 2);
sheet.getRange(range.getRow(), 2, 1, sheet.getLastColumn()).setDataValidation(null).moveTo(targetRange);
sheet.deleteRow(range.getRow());
var lastrow = sheet.getLastRow();
sheet.insertRows(lastrow, 1);
}
}
You should trigger a checking (depending on what value you need) on all the cells that correspond as a required value with onEdit(). Afterwards, simply trigger your moveComplete() function to populate the rest of the other cells you need.
This Google Docs Help Forum post might give you an idea on how to do it. Just modify some of the code snippets that are already provided there. Other helpful docs:
Google AppScipt Triggers
Google AppScript Event Objects

Google Spreadsheet SCRIPT Check if edited cell is in a specific range

I need to detect if changes made to a spreadsheet are being made within a certain range of data and if so, set the current update time.
The issue is, I have a spreadsheet that I edit headers and text on and I do not want the update time in a specific cell to be updated on the spreadsheet but when I edit the data in a range of cells, I DO want the update time changed.
Here's what I have to update the time.
function onEdit(e)
{
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
ss.getRange("G10").setValue(new Date());
} ​
I only want the date in G10 set if I edit certain cells (in a range "B4:J6")
There is an Event provided as a parameter to your onEdit() function, and it contains the necessary information about what was edited. If you were wondering what that (e) was all about, this is it.
Since an onEdit() function is called for every edit, you should invest as little processing as possible in determining whether you should exit. By using the event that's passed in, you will require fewer Service calls, so will be more efficient. The way that Rasmus' answer converts the A1 notation to column and row numbers is good if you need to be flexible, but if the edit range is fixed, you can simply use constant values for comparisons - again, to reduce the processing time required.
function onEdit(e)
{
var editRange = { // B4:J6
top : 4,
bottom : 6,
left : 2,
right : 10
};
// Exit if we're out of range
var thisRow = e.range.getRow();
if (thisRow < editRange.top || thisRow > editRange.bottom) return;
var thisCol = e.range.getColumn();
if (thisCol < editRange.left || thisCol > editRange.right) return;
// We're in range; timestamp the edit
var ss = e.range.getSheet();
ss.getRange(thisRow,7) // "G" is column 7
.setValue(new Date()); // Set time of edit in "G"
} ​
Easiest Way is to NAME your Trigger Range
By naming your "trigger" range, then you don't have to mess with the script once you've set the range name inside the script. By simply editing the range of your named range inside the standard google sheets interface, the script will still work even if you expand or decrease the size of your range.
Step 1: Name Your Range
Name the range of cells you are interested in acting as the trigger for your script should you edit any of the cells in your range as per the instructions here: https://support.google.com/docs/answer/63175
I named my range "triggerRange".
Step 2: Edit your range name into the following script:
function onEdit(e) {
var myRange = SpreadsheetApp.getActiveSheet().getRange('triggerRange'); //<<< Change Your Named Ranged Name Here inside the getRange() function.
//SpreadsheetApp.getUi().alert("myRange in A1 Notation is: " + myRange.getA1Notation()); //If you're having problems, uncomment this to make sure your named range is properly defined
//Let's get the row & column indexes of the active cell
var row = e.range.getRow();
var col = e.range.getColumn();
//SpreadsheetApp.getUi().alert('The Active Cell Row is ' + row + ' and the Column is ' + col); //uncomment this out to do testing
//Check that your active cell is within your named range
if (col >= myRange.getColumn() && col <= myRange.getLastColumn() && row >= myRange.getRow() && row <= myRange.getLastRow()) { //As defined by your Named Range
SpreadsheetApp.getUi().alert('You Edited a Cell INSIDE the Range!');//Repalace Your Custom Code Here
} else {
SpreadsheetApp.getUi().alert('You Edited a Cell OUTSIDE the Range!');//Comment this out or insert code if you want to do something if the edited cells AREN'T inside your named range
return;
}
}
PS: Thanks to the other posters for providing the basic framework for this basic script. I've obviously commented the script pretty heavily so you can easily test it. Remove the alerts that I created inside the script for a cleaner look...or just copy and paste this instead:
function onEdit(e) {
var myRange = SpreadsheetApp.getActiveSheet().getRange('triggerRange'); //<<< Change Your Named Ranged Name Here
//Let's get the row & column indexes of the active cell
var row = e.range.getRow();
var col = e.range.getColumn();
//Check that your active cell is within your named range
if (col >= myRange.getColumn() && col <= myRange.getLastColumn() && row >= myRange.getRow() && row <= myRange.getLastRow()) { //As defined by your Named Range
SpreadsheetApp.getUi().alert('You Edited a Cell INSIDE the Range!');//Repalace Your Custom Code Here
}
}
You can simply check whether the edit event occurred within the range.
function onEdit(e)
{
var sheet = SpreadsheetApp.getActiveSheet();
var editRange = sheet.getActiveRange();
var editRow = editRange.getRow();
var editCol = editRange.getColumn();
var range = sheet.getRange("B4:J6");
var rangeRowStart = range.getRow();
var rangeRowEnd = rangeRowStart + range.getHeight();
var rangeColStart = range.getColumn();
var rangeColEnd = rangeColStart + range.getWidth();
if (editRow >= rangeRowStart && editRow <= rangeRowEnd
&& editCol >= rangeColStart && editCol <= rangeColEnd)
{
// Do your magic here
}
}
I acknowledge that this is very verbose, but I still haven't found a simple method on range ala range.contains(range)
Just a quick fix to a possible problem to the previous answer by Rasmus:
On the lines:
var rangeRowEnd = rangeRowStart + range.getHeight();
var rangeColEnd = rangeColStart + range.getWidth();
it is adding (actually it is not subtracting the initial row and column. This results in a bigger range than what is expected by 1 row and 1 whole column. Just subtract 1 in the same row:
var rangeRowEnd = rangeRowStart + range.getHeight()-1;
var rangeColEnd = rangeColStart + range.getWidth()-1;
Code by Rasmus Fuglsnag and the correction should look like this:
function onEdit(e)
{
var sheet = SpreadsheetApp.getActiveSheet();
var editRange = sheet.getActiveRange();
var editRow = editRange.getRow();
var editCol = editRange.getColumn();
var range = sheet.getRange("B4:J6");
var rangeRowStart = range.getRow();
var rangeRowEnd = rangeRowStart + range.getHeight()-1;
var rangeColStart = range.getColumn();
var rangeColEnd = rangeColStart + range.getWidth()-1;
if (editRow >= rangeRowStart && editRow <= rangeRowEnd
&& editCol >= rangeColStart && editCol <= rangeColEnd)
{
// Do your magic here
}
}
Thanks to Rasmus Fuglsnag for providing the simplest way to do this. I can not believe there is a better simpler way to do this by code.
If you want very few lines of code, you could use:
function onEdit(){
var cell = SpreadsheetApp.getActive().getActiveCell();
var row = cell.getRow();
var col = cell.getColumn();
//if col > A && col < K ...
if(col > 1 && col < 11 && row > 3 && row < 7){ //B4:J6
//do something
}
However, changing the range could take a little longer than the other methods.
To keep a confortable code, you can use a dispatcher (inspired by the Doomd answer).
/**
if the changed cell is in the good sheet and range:
#returns {Range} : the changed cell range
else
#returns {boolean} : false
*/
function onEdit_inRange(e,sheetName,sheetRange){
var sh=e.range.getSheet();
if(sh.getName()===sheetName){
var range = SpreadsheetApp.getActiveSheet().getRange(sheetRange);
var xy=[e.range.getColumn(),e.range.getRow()];
if (xy[0]>=range.getColumn() && xy[0]<=range.getLastColumn() && xy[1]>=range.getRow() && xy[1]<=range.getLastRow()) {
return e.range;
}
}
return false;
}
How to use :
function onEdit(e){
var range=0;
if((range=onEdit_inRange(e,'Stats','Q47:Z47'))){
_handle_stats(range);
}else if ((range=onEdit_inRange(e,'Calendar','A5:B29'))) {
_handle_calendar(range);
}// etc ...
}