Google Spreadsheet SCRIPT Check if edited cell is in a specific range - google-apps-script

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 ...
}

Related

Use range instead of array of indivdual cells

I have this code which works for cells A2-A4:
//The function onEdit ensures that checkboxes deleted (by mistake) in the sheet are immediately re-created.
function onEdit(e) {
var spreadsheet = SpreadsheetApp.getActive();
if(spreadsheet.getSheetName()=='MySheet') { //to avoid executing for another sheet
var checkboxCells = [
'A2','A3','A4'];
var range = e.range;
var value = range.getValue();
var a1Notation = range.getA1Notation();
if (checkboxCells.indexOf(a1Notation) != -1 && value != 'TRUE' && value != 'FALSE') {
range.insertCheckboxes();
}
}
};
However I need to work with a 1D range (since there are many cells) and hence I am trying to use this:
//The function onEdit ensures that checkboxes deleted (by mistake) in the sheet are immediately re-created.
function onEdit(e) {
var spreadsheet = SpreadsheetApp.getActive();
if(spreadsheet.getSheetName()=='MySheet') { //to avoid executing for another sheet
var checkboxCells = spreadsheet.getSheetByName('MySheet')
.getRange('MyRange').getA1Notation();
var range = e.range;
var value = range.getValue();
var a1Notation = range.getA1Notation();
if (checkboxCells.indexOf(a1Notation) != -1 && value != 'TRUE' && value != 'FALSE') {
range.insertCheckboxes();
}
}
};
I think this second version should work, but it does not.
Why does it not work?
Modification points:
In order to check whether the edited cell is included in the specific range you expect, the following sample script can be used. (var { range } = e;)
var checkboxCells = sheet.getRange('MyRange');
var startRow = checkboxCells.getRow();
var endRow = startRow + checkboxCells.getNumRows() - 1;
var startCol = checkboxCells.getColumn();
var endCol = startCol + checkboxCells.getNumColumns() - 1;
var check = range.rowStart >= startRow && range.rowEnd <= endRow && range.columnStart >= startCol && range.columnEnd <= endCol;
In order to check whether the edited cell is the checkbox, isChecked() can be used. In this case, when the cell is the checkbox, true or false are returned. When the cell is not the checkbox, null is returned. I thought that this might be able to be used.
When these points are reflected in your script, how about the following modification?
Modified script:
Please set your sheet name and your range.
function onEdit(e) {
var { range } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() == 'MySheet') {
var checkboxCells = sheet.getRange('MyRange');
var startRow = checkboxCells.getRow();
var endRow = startRow + checkboxCells.getNumRows() - 1;
var startCol = checkboxCells.getColumn();
var endCol = startCol + checkboxCells.getNumColumns() - 1;
var check = range.rowStart >= startRow && range.rowEnd <= endRow && range.columnStart >= startCol && range.columnEnd <= endCol;
if (check && range.isChecked() === null) {
range.insertCheckboxes();
}
}
}
In this modified script, when a cell is edited, when the edited cell is included in MyRange, it checks whether the edited cell is a checkbox when the edited cell is not a checkbox, the checkboxes are inserted into the edited cell.
Reference:
isChecked()

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.

Only execute onEdit if the users make edit within a specific range

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

Google Sheet: Insert timestamp in specific rows while edit specific range

Because I'm low of scripting but I really have to use it (I didn't found any other solution) I would like to ask you for a help with timestamp.
I'm looking for help with function onEdit that would allow me to add timestamp in specific column (S) in each of row while editing specific ranges F4:Q4, F5:Q5. The most important thing there is that I would like to avoid few rows (because it's a title rows and I don't want to add timestamp while changing title rows)
Part of it is working with code below, But there's not included mechanism of editing only specific range (any change in whole row is noticed as a modification) and also there is nothing for skipping the title rows
function onEdit(e) {
// Your sheet params
var sheetName = "Sklad";
var dateModifiedColumnIndex = 4;
var dateModifiedColumnLetter = 'S';
var range = e.range; // range just edited
var sheet = range.getSheet();
if (sheet.getName() !== sheetName) {
return;
}
// If the column isn't our modified date column
if (range.getColumn() != dateModifiedColumnIndex) {
var row = range.getRow();
var time = new Date();
time = Utilities.formatDate(time, "GMT-08:00", "dd/MM/yy, hh:mm:ss");
var dateModifiedRange = sheet.getRange(dateModifiedColumnLetter + row.toString());
dateModifiedRange.setValue(time);
};
};
I would be very grateful for your help,
Regards
You can add the number of header rows as part of your second if()
This change will not add a timestamp in the first 4 rows if edited.
if (range.getColumn() != dateModifiedColumnIndex && range.getRow() > 4) {
Thank you #James
It works for me as
function onEdit(e) {
// Your sheet params
var sheetName = "Sklad";
var dateModifiedColumnIndex = 3;
var dateModifiedColumnLetter = 'S';
var range = e.range; // range just edited
var sheet = range.getSheet();
if (sheet.getName() !== sheetName) {
return;
}
// If the column isn't our modified date column
if (range.getColumn() != dateModifiedColumnIndex && range.getRow() > 3 && range.getRow() != 8 && range.getRow() != 15 && range.getRow() != 32 && range.getRow() < 49) {
var row = range.getRow();
var time = new Date();
time = Utilities.formatDate(time, "GMT", "dd-MM-yyyy");
var dateModifiedRange = sheet.getRange(dateModifiedColumnLetter + row.toString());
dateModifiedRange.setValue(time);
};
};

OnEdit script to change timestamp when a row is edited (error)

I found this script and am trying to use it in a google sheet. The trouble I am having is that I get an error message
You do not have permission to call setValue (line 8).
and when I change a value it will only create the timestamp for the first entry in a cell. If I edit the cell the timestamp does not change. Could someone take a look at this script to see what is wrong.
Here is the sheet
This is the script I am using:
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
if( r.getColumn() != 2 ) { //checks the column
var row = r.getRow();
var time = new Date();
time = Utilities.formatDate(time, "GMT-08:00", "MM/DD/yy, hh:mm:ss");
SpreadsheetApp.getActiveSheet().getRange('M' + row.toString()).setValue(time);
}
}
In order to run the script I typed =onedit(a3:l3) it seemed like the only way to get it to work at all.
Thanks for any help
Try making the following commented changes to your code
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
if( r.getColumn() != 2 ) { //checks the column
var row = r.getRow();
var time = new Date();
time = Utilities.formatDate(time, "GMT-08:00", "MM/DD/yy, hh:mm:ss");
// remove this line below
SpreadsheetApp.getActiveSheet().getRange('M' + row.toString()).setValue(time);
//replace with this line below
return time;
}
}
Reference
You cannot set values to a cell outside of where the custom formula is being set on the spreadsheet, its illogical.
If you want to date/timestamp col M when edits are done in col B, try something like this:
function onEdit(e) {
var s = SpreadsheetApp.getActiveSheet();
if(s.getName() !== 'Sheet1' || e.range.columnStart != 2 || e.range.rowStart < 2) return;
e.range.offset(0, 11).setValue(e.value ? new Date() : "");
}
As this is an onEdit there is no need to enter a formula (=onedit()) in the spreadsheet. Note that:
- erasing a value in col B will cause the stamp to disappear (if you don't want this, just let me know).
- format col M as date/time via the 123-button.
See if this helps ?