Get the row/col of a clicked drawing? - google-apps-script

I assign to a cell in each row a png icon and a script with
sheet.insertImage(blob, 11, i).assignScript("ClickMe");
At the end of the process I have a few hundred rows with buttons at the end. What I'm struggling to do at the moment is work out which row gets clicked on. I've tried a few likely candidates like
var ss = SpreadsheetApp.getActiveSheet();
var sheetName = ss.getSheetName();
var activeRange = ss.getActiveRange();
var selection = ss.getSelection();
var ar = activeRange.getA1Notation();
var cc = ss.getCurrentCell().getA1Notation();
var scr = selection.getCurrentCell().getA1Notation();
var ac = ss.getActiveCell().getA1Notation();
Logger.log("scr=%s, ar=%s, cc=%s, ac=%s", scr, ar, cc, ac);
but having clicked on the 9th line, the Logger.log says
[19-06-20 22:39:01:123 HKT] scr=A1, ar=A1, cc=A1, ac=A1
So the question is this: if someone clicks on the button on the 21st line, can the script figure out what line the click occurred on?

The other answer is that you need to make 21 helper functions,
what I imagine you have now (> binding script)
Button1 > script
Button2 > script
Button3 > script
Button4 > script
function script(){
//Do something
}
Instead you need
Button1 > scriptOne
Button2 > scriptTwo
Button3 > scriptThree
Button4 > scriptFour
with carrier functions:
function scriptOne (){script(1);}
function scriptTwo (){script(2);}
function scriptThree (){script(3);}
function scriptFour(){script(4);}
and then you have that info in the script function
function script(input){
var row = input;
//Do something
}
This does get tedious, and isn't exactly easy to generate in bulk, but if you have a fixed sheet, that might work.
You can also consider making the button a checkbox, and then making your script be "onEdit" and checking for which row the change occurred in. But the way you are currently thinking won't work because there is no way to bind the scripts passing a variable from the button itself. The script will see all of your current buttons as the exact same thing.

It’s not possible to achieve exactly what you want in your code. This is because when you use the functions “getActiveRange” and “getSelection” [1], it returns the cell or cells that are being selected (have the focus on it). When you click in the inserted image, you’re not clicking the cell but only the image, so the focus is still in the first cell.
To obtain the cell where the image is anchored you can use the following snippet:
var image = sheet.insertImage(blob, 11, i).assignScript("ClickMe");
image.getAnchorCell().getA1Notation());
The problem is that you can’t pass any parameters in the assignScript function (like the cell position in this case), only the name of the function to run [2].
A possible workaround is to adjust the size of the cell to contain the complete image. Once this is done, request to your users that they must first click in the cell and then in the image:
var image = sheet.insertImage(blob, 11, i).setHeight(20).setWidth(20).assignScript("ClickMe");
[1] https://developers.google.com/apps-script/reference/spreadsheet/range
[2] https://developers.google.com/apps-script/reference/spreadsheet/over-grid-image

Related

Add a new row based on the custom image button's current position

I recently started learning scripts. I've come to a dead end where I can't think of the logic on how I would implement my script where once i click the button it would automatically add a row above the button. Here is sample picture of my spreadsheet I use it to fix my finances.
Here is my current code but its nowhere near what I wanted to do. Kindly help please to lead me to the proper script. Thank you!
function addRow() {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.insertRowAfter(22);
}
If I understand correctly, you want to add a row above the button you have in your sheet. If that is the case, you can try with the following:
function addRow(){
var sheet = SpreadsheetApp.getActiveSheet();
//Use this if the button is an image
var image = sheet.getImages();
var imageRow = image[0].getAnchorCell().getRow();
sheet.insertRowBefore(imageRow);
//Use this if the button is a drawing
/*
var drawings = sheet.getDrawings();
var row = drawings[0].getContainerInfo().getAnchorRow();
sheet.insertRowBefore(row);
*/
}
Note: In the sample provided, I only have one image in the sheet and that's why I have specified image[0].
Remember to assign the script to the button by clicking in the three dots of the drawing, select "Assign script" and add the name of the function.

Selected cell does not update when using button linked to script

I have a sheet for users to enter a cash-count and their initials then hit a button which runs a script and stores these inputs in a table.
The problem is, if the user types the information (e.g. their initials) and hits the button without first pressing Return or selecting another cell, the information is not saved.
I've seen a similar post:
How to force flush a user's input?
and tried the solutions, some don't work and none are really what I'm looking for. I know that I could use a Checkbox instead of a button but I'd like to find a way do it without resorting to that. The table is a cash-count for a till so not all cells require a value (there may be no $100 notes for example) and I won't know which was the last cell edited.
[The Data][1]
[1]: https://i.stack.imgur.com/mdKXk.png
The Code:
function Accept() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var db = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DATA");
var vals = db.getRange("B:B").getValues();
var last = vals.filter(String).length;
var ui = SpreadsheetApp.getUi();
var user = ui.prompt("Notes:");
var values = [[
sheet.getRange("I7").getValue(), //Date/Time
sheet.getRange("N16").getValue(), //Amount
sheet.getRange("E17").getValue(), //Initials
user.getResponseText(), //Notes
]];
db.getRange(last+1,2,1,4).setValues(values);
}
One solution is to utilize a checkbox as the Accept button, and use an onEdit(e) simple trigger to run your Accept() function.
See the checkboxButtons_ script for sample code.

Use Over-grid Button to reference button's anchor cell in script

I'm looking to use a button's anchor cell in the function that that button calls. My buttons are inserted on different rows. Clicking on one should have it interact only with the data on the row of its anchor cell.
This is for use in a Sheets/Apps Script that I use to search our G Suite domain's Google Groups for external mailing addresses. It is easy to do a memberOf() check for internal users, but G Suite has no way of searching for external users' group memberships.
The script creates a "Remove" button next to each result. Clicking the remove button should remove the user from the group listed
The removal script is working as long as the correct cell is provided. I just can't see how to make sure the button's anchor cell becomes the selected/active cell when the user interacts with it.
Here's the script for adding the remove buttons, to give you an idea of how they're inserted:
function createRemoveButtons(numberOfResults) {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var searchResultStartingColumn = 3;
var searchResultStartingRow = 4;
for (let i = 0; i < numberOfResults.length; i++) {
const resultRow = numberOfResults[i];
sheet.getRange(searchResultStartingRow + i, searchResultStartingColumn)
.activate();
// Get Remove button image.
var ImageBlob = DriveApp.getFileById("1wxSNDJhRz5ehxNZBFCoshMW8j2nFTfGx")
.getBlob().getAs('image/png');
var image = sheet.insertImage(ImageBlob, searchResultStartingColumn, searchResultStartingRow, 1, 1);
image.setHeight(29).setWidth(73).assignScript("removeUserFromGroup");
}
}
You have to create a function for each button and use that function to set the row to act on when that button is clicked.
function buttonRow10(){
var row = 10;
// Do the things to do on row 10
}
The above because the function called by clicking on a button isn't aware of which button was clicked. In other words, contrary as occurs on simple/installable triggers that have an event object as argument, the functions assigned to a button hasn't it.

issue with console log for google apps scripts

I'm trying to troubleshoot the below code (the main problem is that I can't get the range to display) and I've included a log command to show what is being printed out. When I run doGet() and go to View > Logs it says: "[16-10-07 17:31:19:145 EDT] number for you: Range"
sheet.getRange("H1:DE1") refers to part of a row that has dates. Shouldn't it list the contents of the row?
function doGet() {
// The code below opens a spreadsheet using its id and logs the name for it.
// Note that the spreadsheet is NOT physically opened on the client side.
// It is opened on the server only (for modification by the script).
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1aCXXXXXXXXXXXXw61H6VuigRf6YykW8O6A0WWmG46VO7ijI/edit#gid=1673918325');
Logger.log(ss.getName());
var sheet = ss.getSheets()[1];
var range = sheet.getRange("H1:DE1");
Logger.log('number for you: %s', range);
// Calling this method with "true" sets the first line to be the title of the axes
var datatable = range.getDataTable(true);
// Note that this doesn't build an EmbeddedChart, so we can't just use
// Sheet#insertChart(). If we want to do that, we should use
// sheet.newChart().addRange() instead.
var chart = Charts.newBarChart()
.setDataTable(datatable)
.setOption("title", "Your Title Here")
.build();
var app = UiApp.createApplication();
app.add(chart);
ss.show(app);
}
Can anyone advise?
You can't to print the range object in this way. Instead you have to write range.getRow() or range.getColumn(). Please note you have various get functions of the range object.
The range object does not contain sheet values. In order to get them use either of the following:
values = range.getValues(); // gets internal values
values = range.getDisplayValues(); // gets displayed values
The best approach to fix errors in an apps script is to use the built in debugger (not the Logger object, not the execution transcript) and breakpoints, like this:
At the script editors top bar, find the dropdown menu and select a function you want to debug
Set a breakpoint: click the index of a line you would like to pause at (assume to be buggy)
Click the bug icon in the top bar to run the script in debug mode up to the specified breakpoint
Inspect your variables in the opened tab
Step through the lines of your script line by line by clicking the step in, step out, and step over buttons (at the right of the script editors top bar)

Highlight entire row when cell is active

How to design a sheet script that would result in an active row being highlighted?
I would like to have an entire row change color of font or background when one cell in that row is active.
I don't want the trigger to be any specific value in the cell, just clicking on a cell should trigger the highlight for the whole row that cell belongs to.
Sorry, this can't be done with conditional formatting or script by just selecting a cell. You can, however, highlight an entire row of the active cell with the key combination Shift+Spacebar.
I realize this question was asked a while ago, but I stumbled upon it when I was also looking for this same function. My solution is a little cumbersome and isn't a full solution to what you're looking for, but it combines both a tiny script and a little conditional formatting.
I first wrote a small script using the onEdit() function:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var targetCell = sheet.getRange('AD1');
var activeCell = e.range.getA1Notation();
targetCell.setValue(activeCell);
}
I chose 'AD1' as the target cell, as it was far out of the way and, if need be, I could also choose to hide that column.
Then I went over to the conditional highlighting and typed this in as a custom formula:
=ROW()=ROW(INDIRECT($AD$1))
Voila! Every time I edit a cell, it automatically highlights that entire row.
It's not exactly what you're looking for, as it won't automatically highlight the entire row as soon as you click on a cell ... only when you edit the cell. Also, if you have other formulas running and other conditional formatting going on, your spreadsheet can start to get slow. But this is the closest I've seen out there to a possible solution.
Much less as cool, but still somewhat functional regarding legibility is a basic highlighting of every other row. For example:
in conditional formatting: =ROW()=EVEN(ROW())
You can use the onSelectionChange event, like this.
In my case, row 1 had some title cells in it with their own background colors. I only highlight the current row if you're in row 2 or later.
function onSelectionChange(e) {
const range = e.range;
const sheet = range.getSheet();
const maxRows = sheet.getMaxRows();
const maxColumns = sheet.getMaxColumns();
// Clear the background color from all cells.
// (Except row 1 - that has my titles in it)
sheet.getRange(2, 1, maxRows - 1, maxColumns).setBackground(null);
// Don't set the background color if you're on the first row
if (range.getRow() > 1) {
// Highlight the current row
sheet.getRange(range.getRow(), 1, 1, maxColumns).setBackground("#c9daf8");
}
}
It takes a second or so to update - I guess that event's a little slow firing.
This works on desktop or touch device.
The problem you describe can be solved indirectly through the checkbox.
Insert column A in the table.
In column A, select cells in the rows you want to highlight with color.
From the Insert menu, choose Checkbox.
Select entire rows in which the check box has been inserted.
From the Format menu, choose Conditional Formatting.
In the Formatting rules panel, add the Custom formula to this rule.
Enter the formula =$A1=TRUE (instead of 1, use the first line number you selected in step 4).
Specify the Formatting style.
From now on, after selecting the check box, the entire row will be highlighted.
Sadly this cannot be done via onFocus as we all would prefer, but this works well enough for me using the onEdit event. It's still oddly slow, so perhaps someone could make it faster (certainly from reading / writing to properties, but that's the only way I found to track which row is highlighted).
function onEdit(e){
manageRowHighlight(e);
}
function manageRowHighlight(e) {
var props = PropertiesService.getScriptProperties();
var prevRow = parseInt(props.getProperty('highlightedRow'));
var range = e.range;
var thisRow = range.getRow();
//if it's same row, just ignore
if (prevRow == thisRow) {
return;
} else if (prevRow != null){
//else unhighlight it
range = range.getSheet().getRange(prevRow + ':' + prevRow);
range.setBackground(null);
}
//highlight the current row
var range = range.getSheet().getRange(thisRow + ':' + thisRow);
range.setBackground('#fff2cc')
//save the row so highlight can be removed later
props.setProperty('highlightedRow', thisRow);
};
Reading spreadsheets is difficult when there are many columns. When selecting one cell/row, it will highlight the entire row, otherwise, it won't bother:
function onSelectionChange(e) {
const sht = SpreadsheetApp.getActive().getActiveSheet();
const rowCount = sht.getActiveRange().getNumRows();
const maxRows = sht.getMaxRows();
const maxColumns = sht.getMaxColumns();
sht.getRange(2, 1, maxRows - 1, maxColumns).setBackground(null); //skip the first row (headers)
if (rowCount == 1) {
const myrow = sht.getActiveRange().getRow();
if (myrow > 1) { //don't paint the header row
sht.getRange(myrow, 1, 1, maxColumns).setBackgroundRGB(230,230,130);
}
}
}
A workaround that works wonderfully for me was install this app https://www.autocontrol.app/ to redefine the normal behavior of keys into shift+space combination which is actually the shortcut for selecting the current row.
In summary: I changed the behavior of down/up arrows to synthesize a new keyboard input as down-arrow + shift + space. This is the procedure:
After install the extension create a new rule and use the down arrow as trigger
Set the behavior to only occurs in Google Sheets by checking if the URL starts-with https://docs.google.com/spreadsheets/
In the action section select synthesize input (go to advance options>others)
The specific synthesize is: down arrow, then make the combination shift+space. Do it exactly in this way. The app will show you four buttons.
In the synthesize box click the space key and set it to action 2 times (this is to select the full row and not only partially in some cases, yet I suggest you to try using "1 times")
Repeat the process with the UP arrow.
That's all. It took me a time at first but after knowing the trick it is easy. I added an image of how this setting looks like.
Note: Why does the GUI show four buttons in the synthesize box: arrow, shift, space and shift again? This is the expected behavior due to press/release events.
Note2: If the extension in general seems not to be working check the "emergency repair" with right click over the extension icon, or just write the developers.
I'm not a javascript expert, but this can be done fairly easily by modifying directly the document on reaction to specific events. I looked a bit at the current Sheets HTML and running the following code in the JavaScript console makes it highlight on mouse click:
function f() { let mylist = document.getElementsByClassName("selection"); for (let i=0; i<mylist.length; i++) { if (parseInt(mylist[i].style.height)>0 && parseInt(mylist[i].style.width)>0) { mylist[i].style.left='0px'; mylist[i].style.width='100000px'; mylist[i].style.display=null; break; }; }; }; onclick = f;
You can open the console with F12 on Chrome, then Click on the Console tab. You can close it afterward (F12 or X button), the change will remain in effect until the page is closed or reloaded.
Of course Google can change the HTML & CSS at any time and break this, so ideally if this is really needed someone should maintain an extension and keep it updated with Sheet changes (there may be already generic extensions where this could be added easily, please comment if you know any).
The expanded and commented code, if you're interested in modifying or fixing it later:
function f() {
// Get all elements with class "selection"; there should be just a few
// of them, and almost all should be have 0-width or 0-height
let mylist = document.getElementsByClassName("selection")
for (let i=0; i<mylist.length; i++) {
// Find the first element with an area (usually just one)
if (parseInt(mylist[i].style.height)>0 && parseInt(mylist[i].style.width)>0) {
// Set left to 0px (it's >0 when you click on other columns than A)
mylist[i].style.left = '0px';
// Set width to something large so it take the whole sheet
mylist[i].style.width = '100000px'; // NB: very wide sheets could need more!
// Undef the display attribute (set to 'none' by default to hide selection color)
mylist[i].style.display = null;
// On multi-row selections we may match more than one element,
// changing anything but the first breaks the sheet
break;
};
};
};
// Run the function on every mouse click
onclick = f;