Good morning,
I am having an issue with a piece of script which will open the spreadsheet at the first available row after a data table. I am fairly new to this, but as I understand it from research, the onOpen trigger will not work for scripts featuring Activate() methods (which I assume are things like getActiveRange() and setActiveRange() etc.).
I'd really like for this to work, but I can only do it currently by placing a button at the top of the sheet which the user has to click to run the script. I'm at a loss as to how to write in a different way, and have spent ages googling but only get things which don't help me much at all. My code is as below, any help you can give in writing it without using Activate() methods would be very much appreciated. Thanks, MB
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange("A2");
var rowCount = range.getValue() + 3;
var newRange = ss.getRange("A" + rowCount);
SpreadsheetApp.setActiveRange(newRange); };
The value on line 4 refers to a value in a cell which is counting the number of rows in the dataset (I had issues trying to count this using script because there are other columns in the dataset which throw functions like getLastRow()).
As mentioned in issue 3928 there is no way (as of now, september 2014) to set a selection on a cell/range/sheet from a triggered function.
This issue was closed as "wont fix" but recently reopened - probably because a lot of people were very disappointed (to use a polite word) - so I suggest you star this issue to increase its visibility and its priority.
In the mean time I'm afraid there is not much we can do except continue to use old version spreadsheet in which this works without problem.
About what you said about counting the last row in a single column, this has been the subject of one of the most popular thread on this forum, look at the suggested solutions there (and also in other similar threads).
Related
I'm making a Google Sheet for a TTRPG I play with some friends and I'm trying to find the best way to make automated cell notes. I found a solution here that works and modified it to match my sheet but I'm VERY new to Google Scripts and am sure I'm doing things inefficiently.
Right now the solution is:
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Copy of Digital');
var range = sheet.getRange("R31:T59");
range.clearNote();
for (var x=1;x<30;x++) {
range.getCell(x, 1).setNote(range.getCell(x, 3).getValue());
}
}
This is a workable solution for my needs but I always want to take things farther than just "workable" :). I know a small amount of C# but JavaScript is entirely new to me. The issues I see with my current implementation and possible areas of improvement are:
Because of formatting requirements on another section of the sheet, every other row where I am running this solution will ALWAYS be empty, but the solution is going to check the empty rows for notes every time. Is there a way for me to have the solution skip every second row? Can I specify exact cells rather than a range (and does JavaScript use absolute references like sheet formulas)?
Can I specify multiple ranges within the sheet for this solution to act on? Is it as simple as defining a second range?
Is it possible to restrict the onEdit(e) function's activation to only fire when specific ranges are edited? I'm generating a list of "Talents" that need the script to run whenever a player changes their gear through a validation list but to my understanding (and testing) onEdit(e) is going to run when any cell is modified (which will lead to numerous unneccessary script activations).
If I have multiple copies of the same tab active in the same sheet is it possible for multiple players to be using their own version of the sheet at the same time? (I think this must require the removal/modification of the getSheetByName line, but I'm only 2 days into trying to learn this!)
Huge apology for having so many questions! Any and all help is deeply appreciated!
Visual of the sheet for formatting reference
Try it this way:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == "Copy of Digital") {
var range = sheet.getRange("R31:T59");
let notes = sheet.getRange("T31:T59").getDisplayValues()
range.clearNote();
sh.getRange(31,18,notes.length,notes[0].length).setNotes(notes);
}
}
I've been trying to learn on my own by watching YouTube videos and reading forums, but I'm still struggling and...I'm a little embarrassed because it seems so simple. I'm hoping any answers/suggestion from you will help me in the future.
I have two sheets "Bishop" and "Log". I want to copy two specific cells (K11 and K12) from "Bishop", which are vertically aligned, and paste them horizontally in the sheet "Log" at C4 and D4, respectively, without any formatting or formulas being copied over.
Each time I press a button (I've already made and attached a script to) I want it to paste in those two cells, and then move down to the next empty row (so C4,D4 to C5,D5, C6,D6...and so on).
I never intended on getting someone to finish my script for me, but I'm getting confused by the examples I've found in my research. Here's my script so far (after deleting many dysfunctional versions)
function Logger(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var bishop = ss.getSheetByName('Bishop');
var log = ss.getSheetByName('Log');
}
I tried to copy and modify code examples from YouTube videos and articles, but I think my by modifying them I'm just making a mess.
Thank you for any help or suggestions.
Dustin
EDIT
After some tinkering and reading some more, I now have a pretty nifty script that copies data in to where I want it! However, my next goal is to learn how to move down one row each time I run the script, so it creates an actual list, or log. "InputData" and "PasteRange" are named ranges on my source sheet and destination sheet, respectively. Here's my code so far:
function Logger(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var bishop = ss.getSheetByName('Bishop');
var log = ss.getSheetByName('Log');
ss.getRange("InputData").copyTo(log.getRange("PasteRange"),SpreadsheetApp.CopyPasteType.PASTE_VALUES,true);
}
So far your script has gotten to the point of creating 2 variables that reference the Bishop and Log sheets.
Next, you'll need to select the specific ranges in those sheets. To get a range in a sheet, use the getRange method, which has a few variations on what inputs it takes. For example:
var range1 = sheet.getRange("A1");
Once you have references to the ranges, you can use the getValue or getValues method to extract the values inside a range, and then use the setValue or setValues method to overwrite new data into an existing range. For example:
var myValues = range1.getValues();
range2.setValues(myValues);
If you haven't seen this page before, take a look; it's probably the most helpful single webpage for writing sheet scripts.
(Also, I won't write the code for you; Stackoverflow is about specific questions and answers, not about figuratively doing your homework, though the lines are blurry at times. But feel free to keep asking questions!)
Alright, I'm stuck.
I've got a sheet that an entire team of people use to communicate with each other and sign up for timeslots for internal logins.
I've got the sheet formatting performing the way I want to, but, it's too easily edited by people, because they have to be able to edit the values in the cells.
It's currently running Conditional Formatting rules, based upon time/date. Basically, if the cell's timeslot has passed, then it's blacked out. It is also blacked out if the cell isn't associated with the current day.
https://docs.google.com/spreadsheets/d/1TP5iJ9AA_xqDQPiyl89ntZehd7aqDPELcN0bRMGP-SU/edit?usp=sharing
I'm trying to create a script that "onEdit" will restore any formatting (not the value) that had been changed during the edit.
Got it this far, but I'm struggling to make it conditional like the UI rules allow me to do.
function myFunction() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var range1 = sheet.getRange('A1:B10');
range1.mergeAcross()
range1.setBackground('white');
range1.setBorder(true,true,true,true,true,true);
range1.setFontColor('black');
range1.setFontFamily('Arial');
range1.setFontSize(10);
range1.setFontWeight("normal");
range1.setFontStyle("normal");
range1.setHorizontalAlignment("left");
range1.setVerticalAlignment("center");
range1.setWrap(false);
}
Here's where I'm struggling (notes inserted)
function myFunction() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var range1 = sheet.getRange('A1:B10'); // I'd have this repeated for multiple ranges
range1.mergeAcross()
range1.setBackground('white'); // Need an "IF" argument for this, such as if today is Wednesday and time is before 2:30pm, then white, otherwise black. Could probably put this at a different point in the script.
range1.setBorder(true,true,true,true,true,true); // All the following attributes would simply restore defaults
range1.setFontColor('black');
range1.setFontFamily('Arial');
range1.setFontSize(10);
range1.setFontWeight("normal");
range1.setFontStyle("normal");
range1.setHorizontalAlignment("left");
range1.setVerticalAlignment("center");
range1.setWrap(false);
}
Thanks in advance for any help you might offer!
Adam
I've actually been trying to deal with this same problem.
Sadly, google scripts doesn't seem to have a way programmatically set the conditional programming. (Here's a link to the issue. You can cast a vote at the bottom on to get them to implement it)
However, I think you might be able to implement a solution to your problem it by scratch.
The onEdit trigger passes a variable to the function it's assigned to.
function testOnEdit(e) {
Logger.log(JSON.stringify(e));
}
Here is an example of the JSON from the event object for onEdit.
{"range":{"columnStart":3,"rowStart":2,"rowEnd":2,"columnEnd":3},"source":{},"value":"I typed this into B3!","authMode":{},"triggerUid":*********}
You might be able to use this to directly reset the format of the cells they edit. BUT... Unfortunately, onEdit is only fired when a range's values are changed--not when formatting is changed, not when data validation is changed, not when conditional formatting is changed--so to catch the cases that some rouge user decides that purple looks nice, you may want to also make the script run every minute using a timed trigger (but you need to make sure it doesn't take too long to run since it will be going off every minute and google puts a limit on total amount of time triggers can run).
As for comparing dates, these functions may be useful, but be warned that they return the local time (if you have users from multiple time zones you may need to convert to UTC).
function myFunction() {
var date = new Date();
date.getDay(); // the value returned by getDay() is the index of the day of the week, where 0 is Sunday, 1 is Monday...
date.getHours(); // the value returned by getHours() is an integer between 0 and 23
date.getMinutes(); // the value returned by getMinutes() is the current minute in the range 0-60
}
Since I too am dealing with this problem, I plan to add more insight to this solution later. Let me know if you have any insights, Adam.
UPDATE: Based on earlier answers, I joined the chorus of people asking Google to re-enable the ability to do this, and they have apparently complied.
Now to figure out how to implement it since I know nothing about scripting in Google Sheets.
ORIGINAL QUESTION BELOW
I have a spreadsheet with a number of different sheets that require daily entry of data. For example, there's a sheet called US-Sales, with a row for each day of the year. There are also several other identical sheets, like UK-Sales, US-Rentals, etc.
I would like focus to auto-jump to the proper row, based on the current date, whenever one of these sheets is opened.
Possible? If so, how?
This could normally be achieved by a simple script with an onOpen() function (that executes on spreadsheet opening) and activates the desired cell (or sheet)...
but, due to a change in the new version of spreadsheets (that you are most probably using) this is not possible anymore (for now), see this issue (3928) and feel free to star it so that hopefully Google team will change their mind and make it possible again ;-)
edit : this simple code works in old version of spreadsheets, it does not in new version.
function onOpen() {
SpreadsheetApp.getActive().getSheets()[0].getRange('B6').activate();// an arbitrary cell
// not worth trying more complex cell selection (on date or anything else) while this is blocked by design .
}
Details on this change here and below.
EDIT 2 :
On october 16 2014 this issue is now fixed and the code above works also in new version of spreadsheets. details of issue here
to automatically activate the row corresponding to the day of the year you can use a code like below :
function onOpen() {
var day = new Date().getDOY();// this uses a custom date method that returns the day of the year and is defined below
SpreadsheetApp.getActive().getSheets()[0].getRange(day,1).activate();// in cloumn 1 for example. Add an offset if necessary (if headers...)
}
Date.prototype.getDOY = function() {
var onejan = new Date(this.getFullYear(),0,1);
return Math.ceil((this - onejan) / 86400000);
}
This seems like it should be easy as pie, but I can't seem to get any of my little test implementations to work. The ultimate goal is to create a function that runs only when cells in a certain range are edited. To accomplish this, I am creating a function that runs on every edit but starts by checking to see if the range of the edited cell is within a certain range, and only executing the rest of the code if this is true, and otherwise, it just ends immediately.
The first step in doing this, it seems to me, is to find out how to return the range of the edited cell. I've tried various tests.
Here is the current version, which I test by editing the spreadsheet:
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var eRange = e.range;
var message = eRange.getA1Notation();
ss.toast(message);
}
(output: toast does not appear/nothing happens)
I had about 10 other versions posted here, but I realized it was not helpful.
I've managed to return [object Object] and [undefined] in my toast message, so I know I'm not making some silly error with my toast function.
Doesn't e.range return a range? Is that where I go wrong? If it returns a range, shouldn't I be able to use any of the methods in range, just as if I'd used something like "ss.getRange("A1").someMethod()"? Or is there some difference that I don't understand?
Okay, I realize I've asked a couple of different questions here-- I'm just trying to give a clearer picture of my thought process in case it helps you to correct me where I'm wrong. The simple question is this: what sort of command or method can I use to return the range of an edited cell?
I'm so sorry to ask such a basic question. I'm really afraid that someone will enter the right combination of search terms and the answer will be right on the top of the results... but I just can't seem to find it on my own, so here I am. I've tried lots of snippets of code from both StackOverflow and the old Google Groups support forum, as well as lots of my own attempts. I see people have asked lots of similar questions regarding my ultimate goal here-- only running the onEdit function if cells in a certain range are edited-- but the answers are all more complex and seem tailored to each individual situation, and they don't fit mine. I'm certain the answer is out there, but I've been searching for three straight evenings now and found many similar questions asked, and just can't seem to get my own implementation of those suggestions to work. Thanks for being patient with me, and thank you so much for any answers you can offer!
here is the code taken (and slightly modified) from the doc :
function onEdit(event){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = event.source.getActiveSheet();
var r = event.source.getActiveRange();
r.setComment("Last modified: " + (new Date())+' by '+Session.getActiveUser());
ss.toast('Last cell modified = '+r.getA1Notation()+' by '+Session.getActiveUser());
}