Now that Google Sheets has added the onSelectionChange(e) function we should be able to hide the pivot table after it pops up. We just need to trigger the script when a cell inside the pivot table is selected and then activate a cell outside the pivot table to hide the pivot table editor.
I've taken some snippets from the interwebs to get the ball rolling but if someone with more coding knowledge can help figure this out I'm sure everyone would really appreciate it!
function onSelectionChange(e) {
// Set active cell to K1 if selected cell is not blank
var range = e.range;
if(range.getNumRows() === 1
&& range.getNumColumns() === 1
&& range.getCell(1, 1).getValue() != "") {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('K1').activate();
console.log('Cell was changed');
}
}
Related
This is the simplified version of my data. I want to place a button at the bottom of the grey area, so that upon clicking, a new row is inserted above the bottom. the new row must have the formula as the rows before it.
my real formulas are very complicated so I cant use arrayFormula to fulfill the data in new rows.
this a link to my spreadsheet.
You can refer to this sample code that uses a custom menu to insert new row with formula based on the previous row.
Code:
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Custom Menu')
.addItem('Insert New Row', 'insertRow')
.addToUi();
}
function insertRow() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
//Exit function if current active sheet is not Sheet1
if(sheet.getName() != "Sheet1"){
return;
}
//insert new row
sheet.insertRowAfter(lastRow);
sheet.getRange(lastRow,2,1,3).copyTo(sheet.getRange(lastRow+1,2));
}
What it does?
Get the last row and the last column that has content in the active sheet using getLastRow() and getLastColumn()
Insert a new row after the last row that has content using insertRowAfter(afterPosition)
Get the range of the last row that has content using getRange(row, column, numRows, numColumns)
Where:
row = your last row that has content
column = column index where you want to start (in the sample sheet it is in column B/ index 2)
numRows = should be 1 (we only want to copy the current last row that has content)
numCols = how many columns to select. In the sample sheet there are 3 columns (column B-D)
Use copyTo(destination) to copy both values and formatting of the range selected in step 3 and paste it in the newly added row. Destination range should be the top-left cell. Since we want to copy it to the new row.I used getRange(last row +1, 2)
Output:
Note:
If you want to use button placed below the grey row, just use insertRow when you assign a script in your button
Please make sure that the last row with content is the row that has formula.
If you want to use the function on a specified list of sheets, you can add this in the sample code. This will exit the function if the current active sheet name is not included in the valid sheets listed
var validSheets = ["Sheet1", "Sheet2"];
if(!validSheets.includes(sheet.getName())){
return;
}
Remove the last comment ... then click on the new menu : this script will add a new line in the active sheet
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('>> Action <<')
.addItem('Copy last row (formulas only)', 'copyLastRow')
.addToUi();
}
function copyLastRow() {
var sh=SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var rng=sh.getRange(sh.getLastRow(),1,1,sh.getLastColumn())
rng.copyTo(sh.getRange(sh.getLastRow()+1,1,1,sh.getLastColumn()), SpreadsheetApp.CopyPasteType.PASTE_FORMULA, false)
}
I have 2 sheets A and B. I want sheet A to colour cells if the exact same cell in sheet B is not empty. For example if cell A1 in sheet B has a string, then cell A1 in sheet A will be coloured.
Let's say I want to do this to compare a large range, how should I go about doing this?
Example: https://docs.google.com/spreadsheets/d/1P3Ob_mclpXWmILfKwD4R6JN2wAYPUcNZlmtF9LxilV0/edit?usp=sharing
Cells A2:C2, D5 and K2 in sheet B are not empty. So the corresponding cells in sheet A will be coloured red.
You can use the following formula as a conditional formatting rule:
=NOT(ISBLANK(INDIRECT("B!"&address(row(),column()))))
(the formula in A!A2 is there to just show you what happens in sheet B)
Explanation:
Not familiar how this could be done with a custom conditional formatting formula so wait for other guys to help you out with that.
However, I can offer you a Google Apps Script solution.
The following script will check for the data range in sheet B and it will construct a color array. An element of this array will have the white hex color #ffffff" if the value in the values array is empty, or a red color #ff0000 if the value is not blank.
Then it will color sheet A based on this color array in one go. The script is relatively fast even for large ranges.
Solution / Workaround:
Don't forget to adjust "sheet A" and "sheet B" to the name of your sheets.
function colorCells(){
const ss = SpreadsheetApp.getActive();
const sheetToColor = ss.getSheetByName("sheet A");
const sheetToCheck = ss.getSheetByName("sheet B");
const bValues = sheetToCheck.getDataRange().getValues();
const aColors=bValues.map(r=>r.map(v=>v==''?"#ffffff":"#ff0000"));
sheetToColor.getRange(1,1,sheetToCheck.getLastRow(),sheetToCheck.getLastColumn()).
setBackgrounds(aColors);
}
How to use that:
Click on Tools => Scipt editor, copy, paste & save the aforementioned code and then execute it by clicking on the play button.
If you want this to work "automatically" then you can set up a time trigger to execute this function for you every n minutes.
Bonus code:
You can do the same operation but on a more user-interactive way. Again, copy, paste & save the following script to the script editor but this time don't execute it as it can work by itself.
The following script will change the color of a cell in sheet A upon user edits on the same cell in sheet B:
function onEdit(e) {
// adjust this to your needs:
const sheetToColorName = "sheet A";
const sheetToCheckName = "sheet B";
const rng = e.range;
const cell = rng.getA1Notation();
if (rng.getSheet().getName()==sheetToCheckName){
const sheetToColor = e.source.getSheetByName(sheetToColorName);
if(rng.getValue()!=''){
sheetToColor.getRange(cell).setBackground("#ff0000"); //red
}
else {
sheetToColor.getRange(cell).setBackground("#ffffff"); //white
}
}
}
I would like to change the tab color of a sheet (sheet 2) to the same color as the background color of a cell in another sheet (sheet 1). For example, sheet 1 acts as a table of contents and lists all the other sheets in the workbook. When I set the background color of the cell in my table of contents, I would like the corresponding sheet's tab to change to that same color.
Any help would be appreciated.
Thanks!
Yes, I did try to code something, and I actually figured it out. This is what I ended up doing:
function getSheetTab() {
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var tabColor = activeSheet.getActiveCell().getBackground();
var sheetName = activeSheet.getActiveCell().getValue();
var newSheet = app.getActiveSpreadsheet().getSheetByName(sheetName);
newSheet.setTabColor(tabColor);
}
Took me quite a bit of time, as I'm a bit of a noob to Google Sheets.
Thanks!
I found this code in another post. For Google Sheets, basically it copies the color formatting in the "Status" tab and colors in the matching cells in "Monday".
function colorCodeRevised() {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var lr=ss.getSheetByName("Monday").getLastRow() // get last row of sheet1
var lc=ss.getSheetByName("Monday").getLastColumn() //get last column of sheet1
var lr1=ss.getSheetByName("Status").getLastRow() // get last row of sheet2
var lc1=ss.getSheetByName("Status").getLastColumn() ////get last column of sheet2
var sv=ss.getSheetByName("Monday").getRange(5,2,1,lc-15).getValues() // get vehicles. startrow,startcolumn,numrows to return,numcolumns to return
var sn=ss.getSheetByName("Monday").getRange(6,1,lr-5,1).getValues() // get names
var s1=ss.getSheetByName("Status").getRange(2,1,lr1-2,lc1)//exclude legend. squarebackets is sheet numbermm 0=1, 1=2
var rng1=s1.getValues() // get sheet2 data
var rng2=s1.getBackgrounds() // get background colors of dheet2 data
var test= sn.length
var test1= sv.length
var test2=rng1[0].length
var col=1 //column for vehicles on sheet1
for(var m=0;m<sv[0].length;m++){ //for each vehicle
col=col+1 //add one to vehicle column
for(var n=0;n<sn.length;n++){ //for each name
for(var i=0;i<rng1.length;i++){ //loop sheet2 data
for(var j=0;j<rng1[0].length;j++){
if(rng1[i][j].indexOf(sv[0][m])>-1 && rng1[i][j].indexOf(sn[n][0])>-1){ //if sheet2 data cell contains vehicle and name
var c=ss.getSheetByName("Monday").getRange(n+6, col).setBackground(rng2[i][j]) //set color of vehicle and name on sheet1
}}}}}
}
I have a very similar spreadsheet to this but I am struggling to make it run not only for Monday but for all the days of the week. I could make 6 identical scripts but I feel like it would be too taxing having the sheet run 7 scripts every time a change needs to be made. The cells that I want to be colored are in the exact same pattern/order as Monday. For example if C6 on Monday is red, C6 on Tuesday should also be red. Can anyone help me make this script color the same cells from Tuesday-Sunday as it does Monday, without it taking 7 times as long to run?
To your specific question; I think all you really need to do is add a loop with a list of the sheets you want to modify. From there, you can just make everything a variable. That solution is shown last.
Before I just let you off with that though, I'd like to suggest a few changes ^_^
Getting that code-sample and making it work for your sheet was great! I liked the concept and I get where you are with organizing all of that data. I made some changes that (I think) might help you out.
Most of these changes were made with the intention of bringing the logic out of the script and into the sheet. The phenomenal thing about using the formulas in the sheets is that they are optimized by Google very well. If you could break down what you're looking to do into code-snippets that Google has implemented, things get much faster.
Suggested changes:
Status page
Changed to matrix of users and roles. Each role that someone plays has an x in it. In the future, you can populate this matrix based on a form input if that's what you're going for.
Set A1 to contain all of the conditional formatting needed to properly color the table on the day pages.
Manually colored the role headers since I didn't want to re-do all the conditional formatting in A1 and hierarchical conditional formatting isn't a thing.
Day pages
Each cell in the color table now has a formula in it that will set the value to the role based on whether the user in that row has an x in that role on the status page.
Based on the text in those cells, they are colored to have the class' selected color for the text and background.
The script
Can be configured at the top with a list of pages to change and the location of the cell with the formatting.
I wish I could have created the formatting rules in the script, but that functionality isn't available yet. Hopefully it will be implemented when Google gets to this issue
Grabs the cell with the formatting
Loops through each page
Gets the color table
Copies the formula (in the script) and the formatting to the table
Finally, it adds a custom menu at the top that can be used to call the function named "Script tools".
Advantages
You only need to copy the formulas and formatting to each sheet once
When changes are made in status, they are immediately reflected on the proper page
Alternate solution
link to my sheet
link to make an editable copy in your drive
/* Loops through all whitelisted sheets and applies a pre-defined format to the table on each.
*
* Michael Kenworthy 12/21/16
*/
//Set-up variables
var whitelist = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]
var colorKeySheet = "Status"
var colorKeyFormatCell = "A1"
var colorTableRange = "B6:M"
var doc = SpreadsheetApp.getActive();
var ui = SpreadsheetApp.getUi();
function formatting() {
statusSheet = doc.getSheetByName(colorKeySheet);
formatCell = statusSheet.getRange(colorKeyFormatCell);
for(var i=0; i<whitelist.length; i++){
sheet = doc.getSheetByName(whitelist[i])
colorTable = sheet.getRange(colorTableRange);
cell = colorTable.getCell(1, 1)
cell.setFormula('if(not(isblank(INDIRECT("Status!R"&MATCH($A6,Status!$A:$A,0)&"C"&MATCH(B$5,Status!$1:$1,0),false))),B$5,)')
formatCell.copyTo(cell, {formatOnly: true});
cell.copyTo(colorTable)
}
}
function onLoad(){
// Or DocumentApp or FormApp.
ui.createMenu('Sheet tools')
.addItem('Apply formatting to day sheets', 'formatting')
.addToUi();
}
Solution to original question
var whitelist = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]
function colorCodeRevised() {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var lr1=ss.getSheetByName("Status").getLastRow() // get last row of sheet2
var lc1=ss.getSheetByName("Status").getLastColumn() ////get last column of sheet2
var s1=ss.getSheetByName("Status").getRange(2,1,lr1-2,lc1)//exclude legend. squarebackets is sheet numbermm 0=1, 1=2
var rng1=s1.getValues() // get sheet2 data
var rng2=s1.getBackgrounds() // get background colors of dheet2 data
for(var sheetNum=0;sheetNum<whitelist.length;sheetNum++){
var lr=ss.getSheetByName(whiltelist[sheetNum]).getLastRow() // get last row of sheet1
var lc=ss.getSheetByName(whiltelist[sheetNum]).getLastColumn() //get last column of sheet1
var sv=ss.getSheetByName(whiltelist[sheetNum]).getRange(5,2,1,lc-15).getValues() // get vehicles. startrow,startcolumn,numrows to return,numcolumns to return
var sn=ss.getSheetByName(whiltelist[sheetNum]).getRange(6,1,lr-5,1).getValues() // get names
var col=1 //column for vehicles on sheet1
for(var m=0;m<sv[0].length;m++){ //for each vehicle
col=col+1 //add one to vehicle column
for(var n=0;n<sn.length;n++){ //for each name
for(var i=0;i<rng1.length;i++){ //loop sheet2 data
for(var j=0;j<rng1[0].length;j++){
if(rng1[i][j].indexOf(sv[0][m])>-1 && rng1[i][j].indexOf(sn[n][0])>-1){ //if sheet2 data cell contains vehicle and name
var c=ss.getSheetByName(whiltelist[sheetNum]).getRange(n+6, col).setBackground(rng2[i][j]) //set color of vehicle and name on sheet1
}
}
}
}
}
}
}
i have a google spreadsheet document with dates on the left rows:
1.1.2016
2.1.2016
.....
i want to highlight days that have already passed, and i have got this code:
function myFunction()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetSelect = ss.getSheetByName("test");
var todayis = getCell(1,12);
{
for(var i = 0; i<sheetSelect.getMaxRows();i++)
{
if (getCell(i,1) == todayis)
{
var changeRange = sheetSelect.getRange(i,1);
changeRange.setBackgroundRGB(111, 111, 222);
}
}
}
}
This is not working, any suggestions ?
There's a less-complicated way to do it:
Right click on the cells you want to conditionally highlight.
Click "Conditional Formatting" from the right-click menu.
Under "Format cells if...", select "Date is before"
In the next drop-down select "today"
Select the background color you want.
Click "Done"
You can do this without a google apps script also if your dates are all formatted into text as it shows in your example you can use the conditional formatting with a custom formula - highlight the column you want, choose conditional formatting and enter in this formula:
=arrayformula(if(DATEVALUE(substitute(A1:A,".","/"))<today(),true,false))